mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 903398 - [Session Restore] Use a 'cookie-changed' observer instead of asking the cookie service for every host; r=yoric
This commit is contained in:
parent
c9ed7b0daa
commit
d03361e6c6
249
browser/components/sessionstore/src/SessionCookies.jsm
Normal file
249
browser/components/sessionstore/src/SessionCookies.jsm
Normal file
@ -0,0 +1,249 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["SessionCookies"];
|
||||
|
||||
const Cu = Components.utils;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm", this);
|
||||
|
||||
// MAX_EXPIRY should be 2^63-1, but JavaScript can't handle that precision.
|
||||
const MAX_EXPIRY = Math.pow(2, 62);
|
||||
|
||||
|
||||
/**
|
||||
* The external API implemented by the SessionCookies module.
|
||||
*/
|
||||
this.SessionCookies = Object.freeze({
|
||||
getCookiesForHost: function (host) {
|
||||
return SessionCookiesInternal.getCookiesForHost(host);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* The internal API.
|
||||
*/
|
||||
let SessionCookiesInternal = {
|
||||
/**
|
||||
* Stores whether we're initialized, yet.
|
||||
*/
|
||||
_initialized: false,
|
||||
|
||||
/**
|
||||
* Returns the list of active session cookies for a given host.
|
||||
*/
|
||||
getCookiesForHost: function (host) {
|
||||
this._ensureInitialized();
|
||||
return CookieStore.getCookiesForHost(host);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles observers notifications that are sent whenever cookies are added,
|
||||
* changed, or removed. Ensures that the storage is updated accordingly.
|
||||
*/
|
||||
observe: function (subject, topic, data) {
|
||||
switch (data) {
|
||||
case "added":
|
||||
case "changed":
|
||||
this._updateCookie(subject);
|
||||
break;
|
||||
case "deleted":
|
||||
this._removeCookie(subject);
|
||||
break;
|
||||
case "cleared":
|
||||
CookieStore.clear();
|
||||
break;
|
||||
case "batch-deleted":
|
||||
this._removeCookies(subject);
|
||||
break;
|
||||
case "reload":
|
||||
CookieStore.clear();
|
||||
this._reloadCookies();
|
||||
break;
|
||||
default:
|
||||
throw new Error("Unhandled cookie-changed notification.");
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* If called for the first time in a session, iterates all cookies in the
|
||||
* cookies service and puts them into the store if they're session cookies.
|
||||
*/
|
||||
_ensureInitialized: function () {
|
||||
if (!this._initialized) {
|
||||
this._reloadCookies();
|
||||
this._initialized = true;
|
||||
Services.obs.addObserver(this, "cookie-changed", false);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates or adds a given cookie to the store.
|
||||
*/
|
||||
_updateCookie: function (cookie) {
|
||||
cookie.QueryInterface(Ci.nsICookie2);
|
||||
|
||||
if (cookie.isSession) {
|
||||
CookieStore.set(cookie);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes a given cookie from the store.
|
||||
*/
|
||||
_removeCookie: function (cookie) {
|
||||
cookie.QueryInterface(Ci.nsICookie2);
|
||||
|
||||
if (cookie.isSession) {
|
||||
CookieStore.delete(cookie);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes a given list of cookies from the store.
|
||||
*/
|
||||
_removeCookies: function (cookies) {
|
||||
for (let i = 0; i < cookies.length; i++) {
|
||||
this._removeCookie(cookies.queryElementAt(i, Ci.nsICookie2));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Iterates all cookies in the cookies service and puts them into the store
|
||||
* if they're session cookies.
|
||||
*/
|
||||
_reloadCookies: function () {
|
||||
let iter = Services.cookies.enumerator;
|
||||
while (iter.hasMoreElements()) {
|
||||
this._updateCookie(iter.getNext());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The internal cookie storage that keeps track of every active session cookie.
|
||||
* These are stored using maps per host, path, and cookie name.
|
||||
*/
|
||||
let CookieStore = {
|
||||
/**
|
||||
* The internal structure holding all known cookies.
|
||||
*
|
||||
* Host =>
|
||||
* Path =>
|
||||
* Name => {path: "/", name: "sessionid", secure: true}
|
||||
*
|
||||
* Maps are used for storage but the data structure is equivalent to this:
|
||||
*
|
||||
* this._hosts = {
|
||||
* "www.mozilla.org": {
|
||||
* "/": {
|
||||
* "username": {name: "username", value: "my_name_is", etc...},
|
||||
* "sessionid": {name: "sessionid", value: "1fdb3a", etc...}
|
||||
* }
|
||||
* },
|
||||
* "tbpl.mozilla.org": {
|
||||
* "/path": {
|
||||
* "cookiename": {name: "cookiename", value: "value", etc...}
|
||||
* }
|
||||
* }
|
||||
* };
|
||||
*/
|
||||
_hosts: new Map(),
|
||||
|
||||
/**
|
||||
* Returns the list of stored session cookies for a given host.
|
||||
*
|
||||
* @param host
|
||||
* A string containing the host name we want to get cookies for.
|
||||
*/
|
||||
getCookiesForHost: function (host) {
|
||||
if (!this._hosts.has(host)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
let cookies = [];
|
||||
|
||||
for (let pathToNamesMap of this._hosts.get(host).values()) {
|
||||
cookies = cookies.concat([cookie for (cookie of pathToNamesMap.values())]);
|
||||
}
|
||||
|
||||
return cookies;
|
||||
},
|
||||
|
||||
/**
|
||||
* Stores a given cookie.
|
||||
*
|
||||
* @param cookie
|
||||
* The nsICookie2 object to add to the storage.
|
||||
*/
|
||||
set: function (cookie) {
|
||||
let jscookie = {host: cookie.host, value: cookie.value};
|
||||
|
||||
// Only add properties with non-default values to save a few bytes.
|
||||
if (cookie.path) {
|
||||
jscookie.path = cookie.path;
|
||||
}
|
||||
|
||||
if (cookie.name) {
|
||||
jscookie.name = cookie.name;
|
||||
}
|
||||
|
||||
if (cookie.isSecure) {
|
||||
jscookie.secure = true;
|
||||
}
|
||||
|
||||
if (cookie.isHttpOnly) {
|
||||
jscookie.httponly = true;
|
||||
}
|
||||
|
||||
if (cookie.expiry < MAX_EXPIRY) {
|
||||
jscookie.expiry = cookie.expiry;
|
||||
}
|
||||
|
||||
this._ensureMap(cookie).set(cookie.name, jscookie);
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes a given cookie.
|
||||
*
|
||||
* @param cookie
|
||||
* The nsICookie2 object to be removed from storage.
|
||||
*/
|
||||
delete: function (cookie) {
|
||||
this._ensureMap(cookie).delete(cookie.name);
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes all cookies.
|
||||
*/
|
||||
clear: function () {
|
||||
this._hosts.clear();
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates all maps necessary to store a given cookie.
|
||||
*
|
||||
* @param cookie
|
||||
* The nsICookie2 object to create maps for.
|
||||
*
|
||||
* @return The newly created Map instance mapping cookie names to
|
||||
* internal jscookies, in the given path of the given host.
|
||||
*/
|
||||
_ensureMap: function (cookie) {
|
||||
if (!this._hosts.has(cookie.host)) {
|
||||
this._hosts.set(cookie.host, new Map());
|
||||
}
|
||||
|
||||
let pathToNamesMap = this._hosts.get(cookie.host);
|
||||
|
||||
if (!pathToNamesMap.has(cookie.path)) {
|
||||
pathToNamesMap.set(cookie.path, new Map());
|
||||
}
|
||||
|
||||
return pathToNamesMap.get(cookie.path);
|
||||
}
|
||||
};
|
@ -120,6 +120,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "DocumentUtils",
|
||||
"resource:///modules/sessionstore/DocumentUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "SessionStorage",
|
||||
"resource:///modules/sessionstore/SessionStorage.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "SessionCookies",
|
||||
"resource:///modules/sessionstore/SessionCookies.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "_SessionFile",
|
||||
"resource:///modules/sessionstore/_SessionFile.jsm");
|
||||
|
||||
@ -2402,21 +2404,6 @@ let SessionStoreInternal = {
|
||||
* { tabs: [ ... ], etc. }
|
||||
*/
|
||||
_updateCookies: function ssi_updateCookies(aWindows) {
|
||||
function addCookieToHash(aHash, aHost, aPath, aName, aCookie) {
|
||||
// lazily build up a 3-dimensional hash, with
|
||||
// aHost, aPath, and aName as keys
|
||||
if (!aHash[aHost])
|
||||
aHash[aHost] = {};
|
||||
if (!aHash[aHost][aPath])
|
||||
aHash[aHost][aPath] = {};
|
||||
aHash[aHost][aPath][aName] = aCookie;
|
||||
}
|
||||
|
||||
var jscookies = {};
|
||||
var _this = this;
|
||||
// MAX_EXPIRY should be 2^63-1, but JavaScript can't handle that precision
|
||||
var MAX_EXPIRY = Math.pow(2, 62);
|
||||
|
||||
for (let window of aWindows) {
|
||||
window.cookies = [];
|
||||
|
||||
@ -2429,35 +2416,12 @@ let SessionStoreInternal = {
|
||||
}, this);
|
||||
|
||||
for (var [host, isPinned] in Iterator(hosts)) {
|
||||
let list;
|
||||
try {
|
||||
list = Services.cookies.getCookiesFromHost(host);
|
||||
}
|
||||
catch (ex) {
|
||||
debug("getCookiesFromHost failed. Host: " + host);
|
||||
}
|
||||
while (list && list.hasMoreElements()) {
|
||||
var cookie = list.getNext().QueryInterface(Ci.nsICookie2);
|
||||
for (let cookie of SessionCookies.getCookiesForHost(host)) {
|
||||
// window._hosts will only have hosts with the right privacy rules,
|
||||
// so there is no need to do anything special with this call to
|
||||
// checkPrivacyLevel.
|
||||
if (cookie.isSession && _this.checkPrivacyLevel(cookie.isSecure, isPinned)) {
|
||||
// use the cookie's host, path, and name as keys into a hash,
|
||||
// to make sure we serialize each cookie only once
|
||||
if (!(cookie.host in jscookies &&
|
||||
cookie.path in jscookies[cookie.host] &&
|
||||
cookie.name in jscookies[cookie.host][cookie.path])) {
|
||||
var jscookie = { "host": cookie.host, "value": cookie.value };
|
||||
// only add attributes with non-default values (saving a few bits)
|
||||
if (cookie.path) jscookie.path = cookie.path;
|
||||
if (cookie.name) jscookie.name = cookie.name;
|
||||
if (cookie.isSecure) jscookie.secure = true;
|
||||
if (cookie.isHttpOnly) jscookie.httponly = true;
|
||||
if (cookie.expiry < MAX_EXPIRY) jscookie.expiry = cookie.expiry;
|
||||
|
||||
addCookieToHash(jscookies, cookie.host, cookie.path, cookie.name, jscookie);
|
||||
}
|
||||
window.cookies.push(jscookies[cookie.host][cookie.path][cookie.name]);
|
||||
if (this.checkPrivacyLevel(cookie.secure, isPinned)) {
|
||||
window.cookies.push(cookie);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ JS_MODULES_PATH = 'modules/sessionstore'
|
||||
|
||||
EXTRA_JS_MODULES = [
|
||||
'DocumentUtils.jsm',
|
||||
'SessionCookies.jsm',
|
||||
'SessionMigration.jsm',
|
||||
'SessionStorage.jsm',
|
||||
'SessionWorker.js',
|
||||
|
Loading…
Reference in New Issue
Block a user