forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			209 lines
		
	
	
	
		
			7.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			209 lines
		
	
	
	
		
			7.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
/* 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 = ["SessionStorage"];
 | 
						|
 | 
						|
const Cu = Components.utils;
 | 
						|
const Ci = Components.interfaces;
 | 
						|
 | 
						|
Cu.import("resource://gre/modules/Services.jsm");
 | 
						|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 | 
						|
 | 
						|
XPCOMUtils.defineLazyModuleGetter(this, "console",
 | 
						|
  "resource://gre/modules/Console.jsm");
 | 
						|
 | 
						|
// A bound to the size of data to store for DOM Storage.
 | 
						|
const DOM_STORAGE_LIMIT_PREF = "browser.sessionstore.dom_storage_limit";
 | 
						|
 | 
						|
// Returns the principal for a given |frame| contained in a given |docShell|.
 | 
						|
function getPrincipalForFrame(docShell, frame) {
 | 
						|
  let ssm = Services.scriptSecurityManager;
 | 
						|
  let uri = frame.document.documentURIObject;
 | 
						|
  return ssm.getDocShellCodebasePrincipal(uri, docShell);
 | 
						|
}
 | 
						|
 | 
						|
this.SessionStorage = Object.freeze({
 | 
						|
  /**
 | 
						|
   * Updates all sessionStorage "super cookies"
 | 
						|
   * @param docShell
 | 
						|
   *        That tab's docshell (containing the sessionStorage)
 | 
						|
   * @param frameTree
 | 
						|
   *        The docShell's FrameTree instance.
 | 
						|
   * @return Returns a nested object that will have hosts as keys and per-origin
 | 
						|
   *         session storage data as strings. For example:
 | 
						|
   *         {"https://example.com^userContextId=1": {"key": "value", "my_number": "123"}}
 | 
						|
   */
 | 
						|
  collect(docShell, frameTree) {
 | 
						|
    return SessionStorageInternal.collect(docShell, frameTree);
 | 
						|
  },
 | 
						|
 | 
						|
  /**
 | 
						|
   * Restores all sessionStorage "super cookies".
 | 
						|
   * @param aDocShell
 | 
						|
   *        A tab's docshell (containing the sessionStorage)
 | 
						|
   * @param aStorageData
 | 
						|
   *        A nested object with storage data to be restored that has hosts as
 | 
						|
   *        keys and per-origin session storage data as strings. For example:
 | 
						|
   *        {"https://example.com^userContextId=1": {"key": "value", "my_number": "123"}}
 | 
						|
   */
 | 
						|
  restore(aDocShell, aStorageData) {
 | 
						|
    SessionStorageInternal.restore(aDocShell, aStorageData);
 | 
						|
  },
 | 
						|
});
 | 
						|
 | 
						|
var SessionStorageInternal = {
 | 
						|
  /**
 | 
						|
   * Reads all session storage data from the given docShell.
 | 
						|
   * @param docShell
 | 
						|
   *        A tab's docshell (containing the sessionStorage)
 | 
						|
   * @param frameTree
 | 
						|
   *        The docShell's FrameTree instance.
 | 
						|
   * @return Returns a nested object that will have hosts as keys and per-origin
 | 
						|
   *         session storage data as strings. For example:
 | 
						|
   *         {"https://example.com^userContextId=1": {"key": "value", "my_number": "123"}}
 | 
						|
   */
 | 
						|
  collect(docShell, frameTree) {
 | 
						|
    let data = {};
 | 
						|
    let visitedOrigins = new Set();
 | 
						|
 | 
						|
    frameTree.forEach(frame => {
 | 
						|
      let principal = getPrincipalForFrame(docShell, frame);
 | 
						|
      if (!principal) {
 | 
						|
        return;
 | 
						|
      }
 | 
						|
 | 
						|
      // Get the origin of the current history entry
 | 
						|
      // and use that as a key for the per-principal storage data.
 | 
						|
      let origin;
 | 
						|
      try {
 | 
						|
        // The origin getter may throw for about:blank iframes as of bug 1340710,
 | 
						|
        // but we should ignore them anyway.
 | 
						|
        origin = principal.origin;
 | 
						|
      } catch (e) {
 | 
						|
        return;
 | 
						|
      }
 | 
						|
      if (visitedOrigins.has(origin)) {
 | 
						|
        // Don't read a host twice.
 | 
						|
        return;
 | 
						|
      }
 | 
						|
 | 
						|
      // Mark the current origin as visited.
 | 
						|
      visitedOrigins.add(origin);
 | 
						|
 | 
						|
      let originData = this._readEntry(principal, docShell);
 | 
						|
      if (Object.keys(originData).length) {
 | 
						|
        data[origin] = originData;
 | 
						|
      }
 | 
						|
    });
 | 
						|
 | 
						|
    return Object.keys(data).length ? data : null;
 | 
						|
  },
 | 
						|
 | 
						|
  /**
 | 
						|
   * Writes session storage data to the given tab.
 | 
						|
   * @param aDocShell
 | 
						|
   *        A tab's docshell (containing the sessionStorage)
 | 
						|
   * @param aStorageData
 | 
						|
   *        A nested object with storage data to be restored that has hosts as
 | 
						|
   *        keys and per-origin session storage data as strings. For example:
 | 
						|
   *        {"https://example.com^userContextId=1": {"key": "value", "my_number": "123"}}
 | 
						|
   */
 | 
						|
  restore(aDocShell, aStorageData) {
 | 
						|
    for (let origin of Object.keys(aStorageData)) {
 | 
						|
      let data = aStorageData[origin];
 | 
						|
 | 
						|
      let principal;
 | 
						|
 | 
						|
      try {
 | 
						|
        // NOTE: In capture() we record the full origin for the URI which the
 | 
						|
        // sessionStorage is being captured for. As of bug 1235657 this code
 | 
						|
        // stopped parsing any origins which have originattributes correctly, as
 | 
						|
        // it decided to use the origin attributes from the docshell, and try to
 | 
						|
        // interpret the origin as a URI. Since bug 1353844 this code now correctly
 | 
						|
        // parses the full origin, and then discards the origin attributes, to
 | 
						|
        // make the behavior line up with the original intentions in bug 1235657
 | 
						|
        // while preserving the ability to read all session storage from
 | 
						|
        // previous versions. In the future, if this behavior is desired, we may
 | 
						|
        // want to use the spec instead of the origin as the key, and avoid
 | 
						|
        // transmitting origin attribute information which we then discard when
 | 
						|
        // restoring.
 | 
						|
        //
 | 
						|
        // If changing this logic, make sure to also change the principal
 | 
						|
        // computation logic in SessionStore::_sendRestoreHistory.
 | 
						|
        let attrs = aDocShell.getOriginAttributes();
 | 
						|
        let dataPrincipal = Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(origin);
 | 
						|
        principal = Services.scriptSecurityManager.createCodebasePrincipal(dataPrincipal.URI, attrs);
 | 
						|
      } catch (e) {
 | 
						|
        console.error(e);
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
 | 
						|
      let storageManager = aDocShell.QueryInterface(Ci.nsIDOMStorageManager);
 | 
						|
      let window = aDocShell.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow);
 | 
						|
 | 
						|
      // There is no need to pass documentURI, it's only used to fill documentURI property of
 | 
						|
      // domstorage event, which in this case has no consumer. Prevention of events in case
 | 
						|
      // of missing documentURI will be solved in a followup bug to bug 600307.
 | 
						|
      let storage = storageManager.createStorage(window, principal, "", aDocShell.usePrivateBrowsing);
 | 
						|
 | 
						|
      for (let key of Object.keys(data)) {
 | 
						|
        try {
 | 
						|
          storage.setItem(key, data[key]);
 | 
						|
        } catch (e) {
 | 
						|
          // throws e.g. for URIs that can't have sessionStorage
 | 
						|
          console.error(e);
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  /**
 | 
						|
   * Reads an entry in the session storage data contained in a tab's history.
 | 
						|
   * @param aURI
 | 
						|
   *        That history entry uri
 | 
						|
   * @param aDocShell
 | 
						|
   *        A tab's docshell (containing the sessionStorage)
 | 
						|
   */
 | 
						|
  _readEntry(aPrincipal, aDocShell) {
 | 
						|
    let hostData = {};
 | 
						|
    let storage;
 | 
						|
 | 
						|
    let window = aDocShell.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow);
 | 
						|
 | 
						|
    try {
 | 
						|
      let storageManager = aDocShell.QueryInterface(Ci.nsIDOMStorageManager);
 | 
						|
      storage = storageManager.getStorage(window, aPrincipal);
 | 
						|
      storage.length; // XXX: Bug 1232955 - storage.length can throw, catch that failure
 | 
						|
    } catch (e) {
 | 
						|
      // sessionStorage might throw if it's turned off, see bug 458954
 | 
						|
      storage = null;
 | 
						|
    }
 | 
						|
 | 
						|
    if (!storage || !storage.length) {
 | 
						|
      return hostData;
 | 
						|
    }
 | 
						|
 | 
						|
    // If the DOMSessionStorage contains too much data, ignore it.
 | 
						|
    let usage = window.QueryInterface(Ci.nsIInterfaceRequestor)
 | 
						|
                      .getInterface(Ci.nsIDOMWindowUtils)
 | 
						|
                      .getStorageUsage(storage);
 | 
						|
    Services.telemetry.getHistogramById("FX_SESSION_RESTORE_DOM_STORAGE_SIZE_ESTIMATE_CHARS").add(usage);
 | 
						|
    if (usage > Services.prefs.getIntPref(DOM_STORAGE_LIMIT_PREF)) {
 | 
						|
      return hostData;
 | 
						|
    }
 | 
						|
 | 
						|
    for (let i = 0; i < storage.length; i++) {
 | 
						|
      try {
 | 
						|
        let key = storage.key(i);
 | 
						|
        hostData[key] = storage.getItem(key);
 | 
						|
      } catch (e) {
 | 
						|
        // This currently throws for secured items (cf. bug 442048).
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    return hostData;
 | 
						|
  }
 | 
						|
};
 |