diff options
Diffstat (limited to 'browser/components/sessionstore/SessionCookies.jsm')
-rw-r--r-- | browser/components/sessionstore/SessionCookies.jsm | 476 |
1 files changed, 0 insertions, 476 deletions
diff --git a/browser/components/sessionstore/SessionCookies.jsm b/browser/components/sessionstore/SessionCookies.jsm deleted file mode 100644 index b99ab927b..000000000 --- a/browser/components/sessionstore/SessionCookies.jsm +++ /dev/null @@ -1,476 +0,0 @@ -/* 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); -Cu.import("resource://gre/modules/XPCOMUtils.jsm", this); - -XPCOMUtils.defineLazyModuleGetter(this, "Utils", - "resource://gre/modules/sessionstore/Utils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "PrivacyLevel", - "resource:///modules/sessionstore/PrivacyLevel.jsm"); - -// 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({ - update: function (windows) { - SessionCookiesInternal.update(windows); - }, - - getHostsForWindow: function (window, checkPrivacy = false) { - return SessionCookiesInternal.getHostsForWindow(window, checkPrivacy); - }, - - restore(cookies) { - SessionCookiesInternal.restore(cookies); - } -}); - -/** - * The internal API. - */ -var SessionCookiesInternal = { - /** - * Stores whether we're initialized, yet. - */ - _initialized: false, - - /** - * Retrieve the list of all hosts contained in the given windows' session - * history entries (per window) and collect the associated cookies for those - * hosts, if any. The given state object is being modified. - * - * @param windows - * Array of window state objects. - * [{ tabs: [...], cookies: [...] }, ...] - */ - update: function (windows) { - this._ensureInitialized(); - - for (let window of windows) { - let cookies = []; - - // Collect all hosts for the current window. - let hosts = this.getHostsForWindow(window, true); - - for (let host of Object.keys(hosts)) { - let isPinned = hosts[host]; - - for (let cookie of CookieStore.getCookiesForHost(host)) { - // _getCookiesForHost() will only return hosts with the right privacy - // rules, so there is no need to do anything special with this call - // to PrivacyLevel.canSave(). - if (PrivacyLevel.canSave({isHttps: cookie.secure, isPinned: isPinned})) { - cookies.push(cookie); - } - } - } - - // Don't include/keep empty cookie sections. - if (cookies.length) { - window.cookies = cookies; - } else if ("cookies" in window) { - delete window.cookies; - } - } - }, - - /** - * Returns a map of all hosts for a given window that we might want to - * collect cookies for. - * - * @param window - * A window state object containing tabs with history entries. - * @param checkPrivacy (bool) - * Whether to check the privacy level for each host. - * @return {object} A map of hosts for a given window state object. The keys - * will be hosts, the values are boolean and determine - * whether we will use the deferred privacy level when - * checking how much data to save on quitting. - */ - getHostsForWindow: function (window, checkPrivacy = false) { - let hosts = {}; - - for (let tab of window.tabs) { - for (let entry of tab.entries) { - this._extractHostsFromEntry(entry, hosts, checkPrivacy, tab.pinned); - } - } - - return hosts; - }, - - /** - * Restores a given list of session cookies. - */ - restore(cookies) { - - for (let cookie of cookies) { - let expiry = "expiry" in cookie ? cookie.expiry : MAX_EXPIRY; - let cookieObj = { - host: cookie.host, - path: cookie.path || "", - name: cookie.name || "" - }; - if (!Services.cookies.cookieExists(cookieObj, cookie.originAttributes || {})) { - Services.cookies.add(cookie.host, cookie.path || "", cookie.name || "", - cookie.value, !!cookie.secure, !!cookie.httponly, - /* isSession = */ true, expiry, cookie.originAttributes || {}); - } - } - }, - - /** - * 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); - } - }, - - /** - * Fill a given map with hosts found in the given entry's session history and - * any child entries. - * - * @param entry - * the history entry, serialized - * @param hosts - * the hash that will be used to store hosts eg, { hostname: true } - * @param checkPrivacy - * should we check the privacy level for https - * @param isPinned - * is the entry we're evaluating for a pinned tab; used only if - * checkPrivacy - */ - _extractHostsFromEntry: function (entry, hosts, checkPrivacy, isPinned) { - let host = entry._host; - let scheme = entry._scheme; - - // If host & scheme aren't defined, then we are likely here in the startup - // process via _splitCookiesFromWindow. In that case, we'll turn entry.url - // into an nsIURI and get host/scheme from that. This will throw for about: - // urls in which case we don't need to do anything. - if (!host && !scheme) { - try { - let uri = Utils.makeURI(entry.url); - host = uri.host; - scheme = uri.scheme; - this._extractHostsFromHostScheme(host, scheme, hosts, checkPrivacy, isPinned); - } - catch (ex) { } - } - - if (entry.children) { - for (let child of entry.children) { - this._extractHostsFromEntry(child, hosts, checkPrivacy, isPinned); - } - } - }, - - /** - * Add a given host to a given map of hosts if the privacy level allows - * saving cookie data for it. - * - * @param host - * the host of a uri (usually via nsIURI.host) - * @param scheme - * the scheme of a uri (usually via nsIURI.scheme) - * @param hosts - * the hash that will be used to store hosts eg, { hostname: true } - * @param checkPrivacy - * should we check the privacy level for https - * @param isPinned - * is the entry we're evaluating for a pinned tab; used only if - * checkPrivacy - */ - _extractHostsFromHostScheme: - function (host, scheme, hosts, checkPrivacy, isPinned) { - // host and scheme may not be set (for about: urls for example), in which - // case testing scheme will be sufficient. - if (/https?/.test(scheme) && !hosts[host] && - (!checkPrivacy || - PrivacyLevel.canSave({isHttps: scheme == "https", isPinned: isPinned}))) { - // By setting this to true or false, we can determine when looking at - // the host in update() if we should check for privacy. - hosts[host] = isPinned; - } else if (scheme == "file") { - hosts[host] = true; - } - }, - - /** - * Updates or adds a given cookie to the store. - */ - _updateCookie: function (cookie) { - cookie.QueryInterface(Ci.nsICookie2); - - if (cookie.isSession) { - CookieStore.set(cookie); - } else { - CookieStore.delete(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()); - } - } -}; - -/** - * Generates all possible subdomains for a given host and prepends a leading - * dot to all variants. - * - * See http://tools.ietf.org/html/rfc6265#section-5.1.3 - * http://en.wikipedia.org/wiki/HTTP_cookie#Domain_and_Path - * - * All cookies belonging to a web page will be internally represented by a - * nsICookie object. nsICookie.host will be the request host if no domain - * parameter was given when setting the cookie. If a specific domain was given - * then nsICookie.host will contain that specific domain and prepend a leading - * dot to it. - * - * We thus generate all possible subdomains for a given domain and prepend a - * leading dot to them as that is the value that was used as the map key when - * the cookie was set. - */ -function* getPossibleSubdomainVariants(host) { - // Try given domain with a leading dot (.www.example.com). - yield "." + host; - - // Stop if there are only two parts left (e.g. example.com was given). - let parts = host.split("."); - if (parts.length < 3) { - return; - } - - // Remove the first subdomain (www.example.com -> example.com). - let rest = parts.slice(1).join("."); - - // Try possible parent subdomains. - yield* getPossibleSubdomainVariants(rest); -} - -/** - * The internal cookie storage that keeps track of every active session cookie. - * These are stored using maps per host, path, and cookie name. - */ -var 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...} - * } - * }, - * ".example.com": { - * "/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) { - let cookies = []; - - let appendCookiesForHost = host => { - if (!this._hosts.has(host)) { - return; - } - - for (let pathToNamesMap of this._hosts.get(host).values()) { - for (let nameToCookiesMap of pathToNamesMap.values()) { - cookies.push(...nameToCookiesMap.values()); - } - } - } - - // Try to find cookies for the given host, e.g. <www.example.com>. - // The full hostname will be in the map if the Set-Cookie header did not - // have a domain= attribute, i.e. the cookie will only be stored for the - // request domain. Also, try to find cookies for subdomains, e.g. - // <.example.com>. We will find those variants with a leading dot in the - // map if the Set-Cookie header had a domain= attribute, i.e. the cookie - // will be stored for a parent domain and we send it for any subdomain. - for (let variant of [host, ...getPossibleSubdomainVariants(host)]) { - appendCookiesForHost(variant); - } - - 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; - } - - if (cookie.originAttributes) { - jscookie.originAttributes = cookie.originAttributes; - } - - 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 originAttributesMap = this._hosts.get(cookie.host); - // If cookie.originAttributes is null, originAttributes will be an empty string. - let originAttributes = ChromeUtils.originAttributesToSuffix(cookie.originAttributes); - if (!originAttributesMap.has(originAttributes)) { - originAttributesMap.set(originAttributes, new Map()); - } - - let pathToNamesMap = originAttributesMap.get(originAttributes); - - if (!pathToNamesMap.has(cookie.path)) { - pathToNamesMap.set(cookie.path, new Map()); - } - - return pathToNamesMap.get(cookie.path); - } -}; |