1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/browser/components/sessionstore/src/SessionStorage.jsm Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,165 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 +* License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.6 +* You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +"use strict"; 1.9 + 1.10 +this.EXPORTED_SYMBOLS = ["SessionStorage"]; 1.11 + 1.12 +const Cu = Components.utils; 1.13 +const Ci = Components.interfaces; 1.14 + 1.15 +Cu.import("resource://gre/modules/Services.jsm"); 1.16 +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); 1.17 + 1.18 +XPCOMUtils.defineLazyModuleGetter(this, "console", 1.19 + "resource://gre/modules/devtools/Console.jsm"); 1.20 + 1.21 +// Returns the principal for a given |frame| contained in a given |docShell|. 1.22 +function getPrincipalForFrame(docShell, frame) { 1.23 + let ssm = Services.scriptSecurityManager; 1.24 + let uri = frame.document.documentURIObject; 1.25 + return ssm.getDocShellCodebasePrincipal(uri, docShell); 1.26 +} 1.27 + 1.28 +this.SessionStorage = Object.freeze({ 1.29 + /** 1.30 + * Updates all sessionStorage "super cookies" 1.31 + * @param docShell 1.32 + * That tab's docshell (containing the sessionStorage) 1.33 + * @param frameTree 1.34 + * The docShell's FrameTree instance. 1.35 + * @return Returns a nested object that will have hosts as keys and per-host 1.36 + * session storage data as values. For example: 1.37 + * {"example.com": {"key": "value", "my_number": 123}} 1.38 + */ 1.39 + collect: function (docShell, frameTree) { 1.40 + return SessionStorageInternal.collect(docShell, frameTree); 1.41 + }, 1.42 + 1.43 + /** 1.44 + * Restores all sessionStorage "super cookies". 1.45 + * @param aDocShell 1.46 + * A tab's docshell (containing the sessionStorage) 1.47 + * @param aStorageData 1.48 + * A nested object with storage data to be restored that has hosts as 1.49 + * keys and per-host session storage data as values. For example: 1.50 + * {"example.com": {"key": "value", "my_number": 123}} 1.51 + */ 1.52 + restore: function (aDocShell, aStorageData) { 1.53 + SessionStorageInternal.restore(aDocShell, aStorageData); 1.54 + } 1.55 +}); 1.56 + 1.57 +let SessionStorageInternal = { 1.58 + /** 1.59 + * Reads all session storage data from the given docShell. 1.60 + * @param docShell 1.61 + * A tab's docshell (containing the sessionStorage) 1.62 + * @param frameTree 1.63 + * The docShell's FrameTree instance. 1.64 + * @return Returns a nested object that will have hosts as keys and per-host 1.65 + * session storage data as values. For example: 1.66 + * {"example.com": {"key": "value", "my_number": 123}} 1.67 + */ 1.68 + collect: function (docShell, frameTree) { 1.69 + let data = {}; 1.70 + let visitedOrigins = new Set(); 1.71 + 1.72 + frameTree.forEach(frame => { 1.73 + let principal = getPrincipalForFrame(docShell, frame); 1.74 + if (!principal) { 1.75 + return; 1.76 + } 1.77 + 1.78 + // Get the root domain of the current history entry 1.79 + // and use that as a key for the per-host storage data. 1.80 + let origin = principal.jarPrefix + principal.origin; 1.81 + if (visitedOrigins.has(origin)) { 1.82 + // Don't read a host twice. 1.83 + return; 1.84 + } 1.85 + 1.86 + // Mark the current origin as visited. 1.87 + visitedOrigins.add(origin); 1.88 + 1.89 + let originData = this._readEntry(principal, docShell); 1.90 + if (Object.keys(originData).length) { 1.91 + data[origin] = originData; 1.92 + } 1.93 + }); 1.94 + 1.95 + return Object.keys(data).length ? data : null; 1.96 + }, 1.97 + 1.98 + /** 1.99 + * Writes session storage data to the given tab. 1.100 + * @param aDocShell 1.101 + * A tab's docshell (containing the sessionStorage) 1.102 + * @param aStorageData 1.103 + * A nested object with storage data to be restored that has hosts as 1.104 + * keys and per-host session storage data as values. For example: 1.105 + * {"example.com": {"key": "value", "my_number": 123}} 1.106 + */ 1.107 + restore: function (aDocShell, aStorageData) { 1.108 + for (let host of Object.keys(aStorageData)) { 1.109 + let data = aStorageData[host]; 1.110 + let uri = Services.io.newURI(host, null, null); 1.111 + let principal = Services.scriptSecurityManager.getDocShellCodebasePrincipal(uri, aDocShell); 1.112 + let storageManager = aDocShell.QueryInterface(Ci.nsIDOMStorageManager); 1.113 + 1.114 + // There is no need to pass documentURI, it's only used to fill documentURI property of 1.115 + // domstorage event, which in this case has no consumer. Prevention of events in case 1.116 + // of missing documentURI will be solved in a followup bug to bug 600307. 1.117 + // TODO: We should call createStorageForFirstParty() here. See comment 1.118 + // inside _readEntry() for details. 1.119 + let storage = storageManager.createStorage(principal, "", aDocShell.usePrivateBrowsing); 1.120 + 1.121 + for (let key of Object.keys(data)) { 1.122 + try { 1.123 + storage.setItem(key, data[key]); 1.124 + } catch (e) { 1.125 + // throws e.g. for URIs that can't have sessionStorage 1.126 + console.error(e); 1.127 + } 1.128 + } 1.129 + } 1.130 + }, 1.131 + 1.132 + /** 1.133 + * Reads an entry in the session storage data contained in a tab's history. 1.134 + * @param aURI 1.135 + * That history entry uri 1.136 + * @param aDocShell 1.137 + * A tab's docshell (containing the sessionStorage) 1.138 + */ 1.139 + _readEntry: function (aPrincipal, aDocShell) { 1.140 + let hostData = {}; 1.141 + let storage; 1.142 + 1.143 + try { 1.144 + let storageManager = aDocShell.QueryInterface(Ci.nsIDOMStorageManager); 1.145 + // TODO: We should call getStorageForFirstParty() here, but we need an 1.146 + // nsIDocument to call thirdPartyUtil.getFirstPartyURI(), and 1.147 + // unfortunately nsIDocument is not exposed to JavaScript. 1.148 + // However, this code is not called in TBB because the 1.149 + // browser.sessionstore.privacy_level pref. is set to 2. 1.150 + storage = storageManager.getStorage(aPrincipal); 1.151 + } catch (e) { 1.152 + // sessionStorage might throw if it's turned off, see bug 458954 1.153 + } 1.154 + 1.155 + if (storage && storage.length) { 1.156 + for (let i = 0; i < storage.length; i++) { 1.157 + try { 1.158 + let key = storage.key(i); 1.159 + hostData[key] = storage.getItem(key); 1.160 + } catch (e) { 1.161 + // This currently throws for secured items (cf. bug 442048). 1.162 + } 1.163 + } 1.164 + } 1.165 + 1.166 + return hostData; 1.167 + } 1.168 +};