|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
|
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 "use strict"; |
|
6 |
|
7 this.EXPORTED_SYMBOLS = ["SessionStorage"]; |
|
8 |
|
9 const Cu = Components.utils; |
|
10 const Ci = Components.interfaces; |
|
11 |
|
12 Cu.import("resource://gre/modules/Services.jsm"); |
|
13 Cu.import("resource://gre/modules/XPCOMUtils.jsm"); |
|
14 |
|
15 XPCOMUtils.defineLazyModuleGetter(this, "console", |
|
16 "resource://gre/modules/devtools/Console.jsm"); |
|
17 |
|
18 // Returns the principal for a given |frame| contained in a given |docShell|. |
|
19 function getPrincipalForFrame(docShell, frame) { |
|
20 let ssm = Services.scriptSecurityManager; |
|
21 let uri = frame.document.documentURIObject; |
|
22 return ssm.getDocShellCodebasePrincipal(uri, docShell); |
|
23 } |
|
24 |
|
25 this.SessionStorage = Object.freeze({ |
|
26 /** |
|
27 * Updates all sessionStorage "super cookies" |
|
28 * @param docShell |
|
29 * That tab's docshell (containing the sessionStorage) |
|
30 * @param frameTree |
|
31 * The docShell's FrameTree instance. |
|
32 * @return Returns a nested object that will have hosts as keys and per-host |
|
33 * session storage data as values. For example: |
|
34 * {"example.com": {"key": "value", "my_number": 123}} |
|
35 */ |
|
36 collect: function (docShell, frameTree) { |
|
37 return SessionStorageInternal.collect(docShell, frameTree); |
|
38 }, |
|
39 |
|
40 /** |
|
41 * Restores all sessionStorage "super cookies". |
|
42 * @param aDocShell |
|
43 * A tab's docshell (containing the sessionStorage) |
|
44 * @param aStorageData |
|
45 * A nested object with storage data to be restored that has hosts as |
|
46 * keys and per-host session storage data as values. For example: |
|
47 * {"example.com": {"key": "value", "my_number": 123}} |
|
48 */ |
|
49 restore: function (aDocShell, aStorageData) { |
|
50 SessionStorageInternal.restore(aDocShell, aStorageData); |
|
51 } |
|
52 }); |
|
53 |
|
54 let SessionStorageInternal = { |
|
55 /** |
|
56 * Reads all session storage data from the given docShell. |
|
57 * @param docShell |
|
58 * A tab's docshell (containing the sessionStorage) |
|
59 * @param frameTree |
|
60 * The docShell's FrameTree instance. |
|
61 * @return Returns a nested object that will have hosts as keys and per-host |
|
62 * session storage data as values. For example: |
|
63 * {"example.com": {"key": "value", "my_number": 123}} |
|
64 */ |
|
65 collect: function (docShell, frameTree) { |
|
66 let data = {}; |
|
67 let visitedOrigins = new Set(); |
|
68 |
|
69 frameTree.forEach(frame => { |
|
70 let principal = getPrincipalForFrame(docShell, frame); |
|
71 if (!principal) { |
|
72 return; |
|
73 } |
|
74 |
|
75 // Get the root domain of the current history entry |
|
76 // and use that as a key for the per-host storage data. |
|
77 let origin = principal.jarPrefix + principal.origin; |
|
78 if (visitedOrigins.has(origin)) { |
|
79 // Don't read a host twice. |
|
80 return; |
|
81 } |
|
82 |
|
83 // Mark the current origin as visited. |
|
84 visitedOrigins.add(origin); |
|
85 |
|
86 let originData = this._readEntry(principal, docShell); |
|
87 if (Object.keys(originData).length) { |
|
88 data[origin] = originData; |
|
89 } |
|
90 }); |
|
91 |
|
92 return Object.keys(data).length ? data : null; |
|
93 }, |
|
94 |
|
95 /** |
|
96 * Writes session storage data to the given tab. |
|
97 * @param aDocShell |
|
98 * A tab's docshell (containing the sessionStorage) |
|
99 * @param aStorageData |
|
100 * A nested object with storage data to be restored that has hosts as |
|
101 * keys and per-host session storage data as values. For example: |
|
102 * {"example.com": {"key": "value", "my_number": 123}} |
|
103 */ |
|
104 restore: function (aDocShell, aStorageData) { |
|
105 for (let host of Object.keys(aStorageData)) { |
|
106 let data = aStorageData[host]; |
|
107 let uri = Services.io.newURI(host, null, null); |
|
108 let principal = Services.scriptSecurityManager.getDocShellCodebasePrincipal(uri, aDocShell); |
|
109 let storageManager = aDocShell.QueryInterface(Ci.nsIDOMStorageManager); |
|
110 |
|
111 // There is no need to pass documentURI, it's only used to fill documentURI property of |
|
112 // domstorage event, which in this case has no consumer. Prevention of events in case |
|
113 // of missing documentURI will be solved in a followup bug to bug 600307. |
|
114 // TODO: We should call createStorageForFirstParty() here. See comment |
|
115 // inside _readEntry() for details. |
|
116 let storage = storageManager.createStorage(principal, "", aDocShell.usePrivateBrowsing); |
|
117 |
|
118 for (let key of Object.keys(data)) { |
|
119 try { |
|
120 storage.setItem(key, data[key]); |
|
121 } catch (e) { |
|
122 // throws e.g. for URIs that can't have sessionStorage |
|
123 console.error(e); |
|
124 } |
|
125 } |
|
126 } |
|
127 }, |
|
128 |
|
129 /** |
|
130 * Reads an entry in the session storage data contained in a tab's history. |
|
131 * @param aURI |
|
132 * That history entry uri |
|
133 * @param aDocShell |
|
134 * A tab's docshell (containing the sessionStorage) |
|
135 */ |
|
136 _readEntry: function (aPrincipal, aDocShell) { |
|
137 let hostData = {}; |
|
138 let storage; |
|
139 |
|
140 try { |
|
141 let storageManager = aDocShell.QueryInterface(Ci.nsIDOMStorageManager); |
|
142 // TODO: We should call getStorageForFirstParty() here, but we need an |
|
143 // nsIDocument to call thirdPartyUtil.getFirstPartyURI(), and |
|
144 // unfortunately nsIDocument is not exposed to JavaScript. |
|
145 // However, this code is not called in TBB because the |
|
146 // browser.sessionstore.privacy_level pref. is set to 2. |
|
147 storage = storageManager.getStorage(aPrincipal); |
|
148 } catch (e) { |
|
149 // sessionStorage might throw if it's turned off, see bug 458954 |
|
150 } |
|
151 |
|
152 if (storage && storage.length) { |
|
153 for (let i = 0; i < storage.length; i++) { |
|
154 try { |
|
155 let key = storage.key(i); |
|
156 hostData[key] = storage.getItem(key); |
|
157 } catch (e) { |
|
158 // This currently throws for secured items (cf. bug 442048). |
|
159 } |
|
160 } |
|
161 } |
|
162 |
|
163 return hostData; |
|
164 } |
|
165 }; |