|
1 const Cu = Components.utils; |
|
2 Cu.import("resource://gre/modules/Services.jsm"); |
|
3 let tempScope = {}; |
|
4 Cu.import("resource://gre/modules/devtools/dbg-client.jsm", tempScope); |
|
5 Cu.import("resource://gre/modules/devtools/dbg-server.jsm", tempScope); |
|
6 Cu.import("resource://gre/modules/Promise.jsm", tempScope); |
|
7 let {DebuggerServer, DebuggerClient, Promise} = tempScope; |
|
8 tempScope = null; |
|
9 |
|
10 const {StorageFront} = require("devtools/server/actors/storage"); |
|
11 let gFront, gWindow; |
|
12 |
|
13 const beforeReload = { |
|
14 cookies: { |
|
15 "test1.example.org": ["c1", "cs2", "c3", "uc1"], |
|
16 "sectest1.example.org": ["uc1", "cs2"] |
|
17 }, |
|
18 localStorage: { |
|
19 "http://test1.example.org": ["ls1", "ls2"], |
|
20 "http://sectest1.example.org": ["iframe-u-ls1"] |
|
21 }, |
|
22 sessionStorage: { |
|
23 "http://test1.example.org": ["ss1"], |
|
24 "http://sectest1.example.org": ["iframe-u-ss1", "iframe-u-ss2"] |
|
25 }, |
|
26 indexedDB: { |
|
27 "http://test1.example.org": [ |
|
28 JSON.stringify(["idb1", "obj1"]), |
|
29 JSON.stringify(["idb1", "obj2"]), |
|
30 JSON.stringify(["idb2", "obj3"]), |
|
31 ], |
|
32 "http://sectest1.example.org": [] |
|
33 } |
|
34 }; |
|
35 |
|
36 function finishTests(client) { |
|
37 // Cleanup so that indexed db created from this test do not interfere next ones |
|
38 |
|
39 /** |
|
40 * This method iterates over iframes in a window and clears the indexed db |
|
41 * created by this test. |
|
42 */ |
|
43 let clearIDB = (w, i, c) => { |
|
44 if (w[i] && w[i].clear) { |
|
45 w[i].clearIterator = w[i].clear(() => clearIDB(w, i + 1, c)); |
|
46 w[i].clearIterator.next(); |
|
47 } |
|
48 else if (w[i] && w[i + 1]) { |
|
49 clearIDB(w, i + 1, c); |
|
50 } |
|
51 else { |
|
52 c(); |
|
53 } |
|
54 }; |
|
55 |
|
56 let closeConnection = () => { |
|
57 // Forcing GC/CC to get rid of docshells and windows created by this test. |
|
58 forceCollections(); |
|
59 client.close(() => { |
|
60 forceCollections(); |
|
61 DebuggerServer.destroy(); |
|
62 forceCollections(); |
|
63 gFront = gWindow = DebuggerClient = DebuggerServer = null; |
|
64 finish(); |
|
65 }); |
|
66 } |
|
67 gWindow.clearIterator = gWindow.clear(() => { |
|
68 clearIDB(gWindow, 0, closeConnection); |
|
69 }); |
|
70 gWindow.clearIterator.next(); |
|
71 } |
|
72 |
|
73 function testStores(data, client) { |
|
74 testWindowsBeforeReload(data); |
|
75 testReload().then(() => |
|
76 testAddIframe()).then(() => |
|
77 testRemoveIframe()).then(() => |
|
78 finishTests(client)); |
|
79 } |
|
80 |
|
81 function testWindowsBeforeReload(data) { |
|
82 for (let storageType in beforeReload) { |
|
83 ok(data[storageType], storageType + " storage actor is present"); |
|
84 is(Object.keys(data[storageType].hosts).length, |
|
85 Object.keys(beforeReload[storageType]).length, |
|
86 "Number of hosts for " + storageType + "match"); |
|
87 for (let host in beforeReload[storageType]) { |
|
88 ok(data[storageType].hosts[host], "Host " + host + " is present"); |
|
89 } |
|
90 } |
|
91 } |
|
92 |
|
93 function markOutMatched(toBeEmptied, data, deleted) { |
|
94 if (!Object.keys(toBeEmptied).length) { |
|
95 info("Object empty") |
|
96 return; |
|
97 } |
|
98 ok(Object.keys(data).length, |
|
99 "Atleast some storage types should be present in deleted"); |
|
100 for (let storageType in toBeEmptied) { |
|
101 if (!data[storageType]) { |
|
102 continue; |
|
103 } |
|
104 info("Testing for " + storageType); |
|
105 for (let host in data[storageType]) { |
|
106 ok(toBeEmptied[storageType][host], "Host " + host + " found"); |
|
107 if (!deleted) { |
|
108 for (let item of data[storageType][host]) { |
|
109 let index = toBeEmptied[storageType][host].indexOf(item); |
|
110 ok(index > -1, "Item found - " + item); |
|
111 if (index > -1) { |
|
112 toBeEmptied[storageType][host].splice(index, 1); |
|
113 } |
|
114 } |
|
115 if (!toBeEmptied[storageType][host].length) { |
|
116 delete toBeEmptied[storageType][host]; |
|
117 } |
|
118 } |
|
119 else { |
|
120 delete toBeEmptied[storageType][host]; |
|
121 } |
|
122 } |
|
123 if (!Object.keys(toBeEmptied[storageType]).length) { |
|
124 delete toBeEmptied[storageType]; |
|
125 } |
|
126 } |
|
127 } |
|
128 |
|
129 function testReload() { |
|
130 info("Testing if reload works properly"); |
|
131 |
|
132 let shouldBeEmptyFirst = Cu.cloneInto(beforeReload, {}); |
|
133 let shouldBeEmptyLast = Cu.cloneInto(beforeReload, {}); |
|
134 let reloaded = Promise.defer(); |
|
135 |
|
136 let onStoresUpdate = data => { |
|
137 info("in stores update of testReload"); |
|
138 // This might be second time stores update is happening, in which case, |
|
139 // data.deleted will be null. |
|
140 // OR.. This might be the first time on a super slow machine where both |
|
141 // data.deleted and data.added is missing in the first update. |
|
142 if (data.deleted) { |
|
143 markOutMatched(shouldBeEmptyFirst, data.deleted, true); |
|
144 } |
|
145 |
|
146 if (!Object.keys(shouldBeEmptyFirst).length) { |
|
147 info("shouldBeEmptyFirst is empty now"); |
|
148 } |
|
149 |
|
150 // stores-update call might not have data.added for the first time on slow |
|
151 // machines, in which case, data.added will be null |
|
152 if (data.added) { |
|
153 markOutMatched(shouldBeEmptyLast, data.added); |
|
154 } |
|
155 |
|
156 if (!Object.keys(shouldBeEmptyLast).length) { |
|
157 info("Everything to be received is received."); |
|
158 endTestReloaded(); |
|
159 } |
|
160 }; |
|
161 |
|
162 let endTestReloaded = () => { |
|
163 gFront.off("stores-update", onStoresUpdate); |
|
164 reloaded.resolve(); |
|
165 }; |
|
166 |
|
167 gFront.on("stores-update", onStoresUpdate); |
|
168 |
|
169 content.location.reload(); |
|
170 return reloaded.promise; |
|
171 } |
|
172 |
|
173 function testAddIframe() { |
|
174 info("Testing if new iframe addition works properly"); |
|
175 let reloaded = Promise.defer(); |
|
176 |
|
177 let shouldBeEmpty = { |
|
178 localStorage: { |
|
179 "https://sectest1.example.org": ["iframe-s-ls1"] |
|
180 }, |
|
181 sessionStorage: { |
|
182 "https://sectest1.example.org": ["iframe-s-ss1"] |
|
183 }, |
|
184 cookies: { |
|
185 "sectest1.example.org": ["sc1"] |
|
186 }, |
|
187 indexedDB: { |
|
188 // empty because indexed db creation happens after the page load, so at |
|
189 // the time of window-ready, there was no indexed db present. |
|
190 "https://sectest1.example.org": [] |
|
191 } |
|
192 }; |
|
193 |
|
194 let onStoresUpdate = data => { |
|
195 info("checking if the hosts list is correct for this iframe addition"); |
|
196 |
|
197 markOutMatched(shouldBeEmpty, data.added); |
|
198 |
|
199 ok(!data.changed || !data.changed.cookies || |
|
200 !data.changed.cookies["https://sectest1.example.org"], |
|
201 "Nothing got changed for cookies"); |
|
202 ok(!data.changed || !data.changed.localStorage || |
|
203 !data.changed.localStorage["https://sectest1.example.org"], |
|
204 "Nothing got changed for local storage"); |
|
205 ok(!data.changed || !data.changed.sessionStorage || |
|
206 !data.changed.sessionStorage["https://sectest1.example.org"], |
|
207 "Nothing got changed for session storage"); |
|
208 ok(!data.changed || !data.changed.indexedDB || |
|
209 !data.changed.indexedDB["https://sectest1.example.org"], |
|
210 "Nothing got changed for indexed db"); |
|
211 |
|
212 ok(!data.deleted || !data.deleted.cookies || |
|
213 !data.deleted.cookies["https://sectest1.example.org"], |
|
214 "Nothing got deleted for cookies"); |
|
215 ok(!data.deleted || !data.deleted.localStorage || |
|
216 !data.deleted.localStorage["https://sectest1.example.org"], |
|
217 "Nothing got deleted for local storage"); |
|
218 ok(!data.deleted || !data.deleted.sessionStorage || |
|
219 !data.deleted.sessionStorage["https://sectest1.example.org"], |
|
220 "Nothing got deleted for session storage"); |
|
221 ok(!data.deleted || !data.deleted.indexedDB || |
|
222 !data.deleted.indexedDB["https://sectest1.example.org"], |
|
223 "Nothing got deleted for indexed db"); |
|
224 |
|
225 if (!Object.keys(shouldBeEmpty).length) { |
|
226 info("Everything to be received is received."); |
|
227 endTestReloaded(); |
|
228 } |
|
229 }; |
|
230 |
|
231 let endTestReloaded = () => { |
|
232 gFront.off("stores-update", onStoresUpdate); |
|
233 reloaded.resolve(); |
|
234 }; |
|
235 |
|
236 gFront.on("stores-update", onStoresUpdate); |
|
237 |
|
238 let iframe = content.document.createElement("iframe"); |
|
239 iframe.src = ALT_DOMAIN_SECURED + "storage-secured-iframe.html"; |
|
240 content.document.querySelector("body").appendChild(iframe); |
|
241 return reloaded.promise; |
|
242 } |
|
243 |
|
244 function testRemoveIframe() { |
|
245 info("Testing if iframe removal works properly"); |
|
246 let reloaded = Promise.defer(); |
|
247 |
|
248 let shouldBeEmpty = { |
|
249 localStorage: { |
|
250 "http://sectest1.example.org": [] |
|
251 }, |
|
252 sessionStorage: { |
|
253 "http://sectest1.example.org": [] |
|
254 } |
|
255 }; |
|
256 |
|
257 let onStoresUpdate = data => { |
|
258 info("checking if the hosts list is correct for this iframe deletion"); |
|
259 |
|
260 markOutMatched(shouldBeEmpty, data.deleted, true); |
|
261 |
|
262 ok(!data.deleted.cookies || !data.deleted.cookies["sectest1.example.org"], |
|
263 "Nothing got deleted for Cookies as the same hostname is still present"); |
|
264 |
|
265 ok(!data.changed || !data.changed.cookies || |
|
266 !data.changed.cookies["http://sectest1.example.org"], |
|
267 "Nothing got changed for cookies"); |
|
268 ok(!data.changed || !data.changed.localStorage || |
|
269 !data.changed.localStorage["http://sectest1.example.org"], |
|
270 "Nothing got changed for local storage"); |
|
271 ok(!data.changed || !data.changed.sessionStorage || |
|
272 !data.changed.sessionStorage["http://sectest1.example.org"], |
|
273 "Nothing got changed for session storage"); |
|
274 |
|
275 ok(!data.added || !data.added.cookies || |
|
276 !data.added.cookies["http://sectest1.example.org"], |
|
277 "Nothing got added for cookies"); |
|
278 ok(!data.added || !data.added.localStorage || |
|
279 !data.added.localStorage["http://sectest1.example.org"], |
|
280 "Nothing got added for local storage"); |
|
281 ok(!data.added || !data.added.sessionStorage || |
|
282 !data.added.sessionStorage["http://sectest1.example.org"], |
|
283 "Nothing got added for session storage"); |
|
284 |
|
285 if (!Object.keys(shouldBeEmpty).length) { |
|
286 info("Everything to be received is received."); |
|
287 endTestReloaded(); |
|
288 } |
|
289 }; |
|
290 |
|
291 let endTestReloaded = () => { |
|
292 gFront.off("stores-update", onStoresUpdate); |
|
293 reloaded.resolve(); |
|
294 }; |
|
295 |
|
296 gFront.on("stores-update", onStoresUpdate); |
|
297 |
|
298 for (let iframe of content.document.querySelectorAll("iframe")) { |
|
299 if (iframe.src.startsWith("http:")) { |
|
300 iframe.remove(); |
|
301 break; |
|
302 } |
|
303 } |
|
304 return reloaded.promise; |
|
305 } |
|
306 |
|
307 function test() { |
|
308 waitForExplicitFinish(); |
|
309 addTab(MAIN_DOMAIN + "storage-dynamic-windows.html", function(doc) { |
|
310 try { |
|
311 // Sometimes debugger server does not get destroyed correctly by previous |
|
312 // tests. |
|
313 DebuggerServer.destroy(); |
|
314 } catch (ex) { } |
|
315 DebuggerServer.init(function () { return true; }); |
|
316 DebuggerServer.addBrowserActors(); |
|
317 |
|
318 let createConnection = () => { |
|
319 let client = new DebuggerClient(DebuggerServer.connectPipe()); |
|
320 client.connect(function onConnect() { |
|
321 client.listTabs(function onListTabs(aResponse) { |
|
322 let form = aResponse.tabs[aResponse.selected]; |
|
323 gFront = StorageFront(client, form); |
|
324 |
|
325 gFront.listStores().then(data => testStores(data, client)); |
|
326 }); |
|
327 }); |
|
328 }; |
|
329 |
|
330 /** |
|
331 * This method iterates over iframes in a window and setups the indexed db |
|
332 * required for this test. |
|
333 */ |
|
334 let setupIDBInFrames = (w, i, c) => { |
|
335 if (w[i] && w[i].idbGenerator) { |
|
336 w[i].setupIDB = w[i].idbGenerator(() => setupIDBInFrames(w, i + 1, c)); |
|
337 w[i].setupIDB.next(); |
|
338 } |
|
339 else if (w[i] && w[i + 1]) { |
|
340 setupIDBInFrames(w, i + 1, c); |
|
341 } |
|
342 else { |
|
343 c(); |
|
344 } |
|
345 }; |
|
346 // Setup the indexed db in main window. |
|
347 gWindow = doc.defaultView.wrappedJSObject; |
|
348 gWindow.setupIDB = gWindow.idbGenerator(() => { |
|
349 setupIDBInFrames(gWindow, 0, createConnection); |
|
350 }); |
|
351 gWindow.setupIDB.next(); |
|
352 }); |
|
353 } |