michael@0: const Cu = Components.utils; michael@0: Cu.import("resource://gre/modules/Services.jsm"); michael@0: let tempScope = {}; michael@0: Cu.import("resource://gre/modules/devtools/dbg-client.jsm", tempScope); michael@0: Cu.import("resource://gre/modules/devtools/dbg-server.jsm", tempScope); michael@0: Cu.import("resource://gre/modules/Promise.jsm", tempScope); michael@0: let {DebuggerServer, DebuggerClient, Promise} = tempScope; michael@0: tempScope = null; michael@0: michael@0: const {StorageFront} = require("devtools/server/actors/storage"); michael@0: let gFront, gWindow; michael@0: michael@0: const beforeReload = { michael@0: cookies: { michael@0: "test1.example.org": ["c1", "cs2", "c3", "uc1"], michael@0: "sectest1.example.org": ["uc1", "cs2"] michael@0: }, michael@0: localStorage: { michael@0: "http://test1.example.org": ["ls1", "ls2"], michael@0: "http://sectest1.example.org": ["iframe-u-ls1"] michael@0: }, michael@0: sessionStorage: { michael@0: "http://test1.example.org": ["ss1"], michael@0: "http://sectest1.example.org": ["iframe-u-ss1", "iframe-u-ss2"] michael@0: }, michael@0: indexedDB: { michael@0: "http://test1.example.org": [ michael@0: JSON.stringify(["idb1", "obj1"]), michael@0: JSON.stringify(["idb1", "obj2"]), michael@0: JSON.stringify(["idb2", "obj3"]), michael@0: ], michael@0: "http://sectest1.example.org": [] michael@0: } michael@0: }; michael@0: michael@0: function finishTests(client) { michael@0: // Cleanup so that indexed db created from this test do not interfere next ones michael@0: michael@0: /** michael@0: * This method iterates over iframes in a window and clears the indexed db michael@0: * created by this test. michael@0: */ michael@0: let clearIDB = (w, i, c) => { michael@0: if (w[i] && w[i].clear) { michael@0: w[i].clearIterator = w[i].clear(() => clearIDB(w, i + 1, c)); michael@0: w[i].clearIterator.next(); michael@0: } michael@0: else if (w[i] && w[i + 1]) { michael@0: clearIDB(w, i + 1, c); michael@0: } michael@0: else { michael@0: c(); michael@0: } michael@0: }; michael@0: michael@0: let closeConnection = () => { michael@0: // Forcing GC/CC to get rid of docshells and windows created by this test. michael@0: forceCollections(); michael@0: client.close(() => { michael@0: forceCollections(); michael@0: DebuggerServer.destroy(); michael@0: forceCollections(); michael@0: gFront = gWindow = DebuggerClient = DebuggerServer = null; michael@0: finish(); michael@0: }); michael@0: } michael@0: gWindow.clearIterator = gWindow.clear(() => { michael@0: clearIDB(gWindow, 0, closeConnection); michael@0: }); michael@0: gWindow.clearIterator.next(); michael@0: } michael@0: michael@0: function testStores(data, client) { michael@0: testWindowsBeforeReload(data); michael@0: testReload().then(() => michael@0: testAddIframe()).then(() => michael@0: testRemoveIframe()).then(() => michael@0: finishTests(client)); michael@0: } michael@0: michael@0: function testWindowsBeforeReload(data) { michael@0: for (let storageType in beforeReload) { michael@0: ok(data[storageType], storageType + " storage actor is present"); michael@0: is(Object.keys(data[storageType].hosts).length, michael@0: Object.keys(beforeReload[storageType]).length, michael@0: "Number of hosts for " + storageType + "match"); michael@0: for (let host in beforeReload[storageType]) { michael@0: ok(data[storageType].hosts[host], "Host " + host + " is present"); michael@0: } michael@0: } michael@0: } michael@0: michael@0: function markOutMatched(toBeEmptied, data, deleted) { michael@0: if (!Object.keys(toBeEmptied).length) { michael@0: info("Object empty") michael@0: return; michael@0: } michael@0: ok(Object.keys(data).length, michael@0: "Atleast some storage types should be present in deleted"); michael@0: for (let storageType in toBeEmptied) { michael@0: if (!data[storageType]) { michael@0: continue; michael@0: } michael@0: info("Testing for " + storageType); michael@0: for (let host in data[storageType]) { michael@0: ok(toBeEmptied[storageType][host], "Host " + host + " found"); michael@0: if (!deleted) { michael@0: for (let item of data[storageType][host]) { michael@0: let index = toBeEmptied[storageType][host].indexOf(item); michael@0: ok(index > -1, "Item found - " + item); michael@0: if (index > -1) { michael@0: toBeEmptied[storageType][host].splice(index, 1); michael@0: } michael@0: } michael@0: if (!toBeEmptied[storageType][host].length) { michael@0: delete toBeEmptied[storageType][host]; michael@0: } michael@0: } michael@0: else { michael@0: delete toBeEmptied[storageType][host]; michael@0: } michael@0: } michael@0: if (!Object.keys(toBeEmptied[storageType]).length) { michael@0: delete toBeEmptied[storageType]; michael@0: } michael@0: } michael@0: } michael@0: michael@0: function testReload() { michael@0: info("Testing if reload works properly"); michael@0: michael@0: let shouldBeEmptyFirst = Cu.cloneInto(beforeReload, {}); michael@0: let shouldBeEmptyLast = Cu.cloneInto(beforeReload, {}); michael@0: let reloaded = Promise.defer(); michael@0: michael@0: let onStoresUpdate = data => { michael@0: info("in stores update of testReload"); michael@0: // This might be second time stores update is happening, in which case, michael@0: // data.deleted will be null. michael@0: // OR.. This might be the first time on a super slow machine where both michael@0: // data.deleted and data.added is missing in the first update. michael@0: if (data.deleted) { michael@0: markOutMatched(shouldBeEmptyFirst, data.deleted, true); michael@0: } michael@0: michael@0: if (!Object.keys(shouldBeEmptyFirst).length) { michael@0: info("shouldBeEmptyFirst is empty now"); michael@0: } michael@0: michael@0: // stores-update call might not have data.added for the first time on slow michael@0: // machines, in which case, data.added will be null michael@0: if (data.added) { michael@0: markOutMatched(shouldBeEmptyLast, data.added); michael@0: } michael@0: michael@0: if (!Object.keys(shouldBeEmptyLast).length) { michael@0: info("Everything to be received is received."); michael@0: endTestReloaded(); michael@0: } michael@0: }; michael@0: michael@0: let endTestReloaded = () => { michael@0: gFront.off("stores-update", onStoresUpdate); michael@0: reloaded.resolve(); michael@0: }; michael@0: michael@0: gFront.on("stores-update", onStoresUpdate); michael@0: michael@0: content.location.reload(); michael@0: return reloaded.promise; michael@0: } michael@0: michael@0: function testAddIframe() { michael@0: info("Testing if new iframe addition works properly"); michael@0: let reloaded = Promise.defer(); michael@0: michael@0: let shouldBeEmpty = { michael@0: localStorage: { michael@0: "https://sectest1.example.org": ["iframe-s-ls1"] michael@0: }, michael@0: sessionStorage: { michael@0: "https://sectest1.example.org": ["iframe-s-ss1"] michael@0: }, michael@0: cookies: { michael@0: "sectest1.example.org": ["sc1"] michael@0: }, michael@0: indexedDB: { michael@0: // empty because indexed db creation happens after the page load, so at michael@0: // the time of window-ready, there was no indexed db present. michael@0: "https://sectest1.example.org": [] michael@0: } michael@0: }; michael@0: michael@0: let onStoresUpdate = data => { michael@0: info("checking if the hosts list is correct for this iframe addition"); michael@0: michael@0: markOutMatched(shouldBeEmpty, data.added); michael@0: michael@0: ok(!data.changed || !data.changed.cookies || michael@0: !data.changed.cookies["https://sectest1.example.org"], michael@0: "Nothing got changed for cookies"); michael@0: ok(!data.changed || !data.changed.localStorage || michael@0: !data.changed.localStorage["https://sectest1.example.org"], michael@0: "Nothing got changed for local storage"); michael@0: ok(!data.changed || !data.changed.sessionStorage || michael@0: !data.changed.sessionStorage["https://sectest1.example.org"], michael@0: "Nothing got changed for session storage"); michael@0: ok(!data.changed || !data.changed.indexedDB || michael@0: !data.changed.indexedDB["https://sectest1.example.org"], michael@0: "Nothing got changed for indexed db"); michael@0: michael@0: ok(!data.deleted || !data.deleted.cookies || michael@0: !data.deleted.cookies["https://sectest1.example.org"], michael@0: "Nothing got deleted for cookies"); michael@0: ok(!data.deleted || !data.deleted.localStorage || michael@0: !data.deleted.localStorage["https://sectest1.example.org"], michael@0: "Nothing got deleted for local storage"); michael@0: ok(!data.deleted || !data.deleted.sessionStorage || michael@0: !data.deleted.sessionStorage["https://sectest1.example.org"], michael@0: "Nothing got deleted for session storage"); michael@0: ok(!data.deleted || !data.deleted.indexedDB || michael@0: !data.deleted.indexedDB["https://sectest1.example.org"], michael@0: "Nothing got deleted for indexed db"); michael@0: michael@0: if (!Object.keys(shouldBeEmpty).length) { michael@0: info("Everything to be received is received."); michael@0: endTestReloaded(); michael@0: } michael@0: }; michael@0: michael@0: let endTestReloaded = () => { michael@0: gFront.off("stores-update", onStoresUpdate); michael@0: reloaded.resolve(); michael@0: }; michael@0: michael@0: gFront.on("stores-update", onStoresUpdate); michael@0: michael@0: let iframe = content.document.createElement("iframe"); michael@0: iframe.src = ALT_DOMAIN_SECURED + "storage-secured-iframe.html"; michael@0: content.document.querySelector("body").appendChild(iframe); michael@0: return reloaded.promise; michael@0: } michael@0: michael@0: function testRemoveIframe() { michael@0: info("Testing if iframe removal works properly"); michael@0: let reloaded = Promise.defer(); michael@0: michael@0: let shouldBeEmpty = { michael@0: localStorage: { michael@0: "http://sectest1.example.org": [] michael@0: }, michael@0: sessionStorage: { michael@0: "http://sectest1.example.org": [] michael@0: } michael@0: }; michael@0: michael@0: let onStoresUpdate = data => { michael@0: info("checking if the hosts list is correct for this iframe deletion"); michael@0: michael@0: markOutMatched(shouldBeEmpty, data.deleted, true); michael@0: michael@0: ok(!data.deleted.cookies || !data.deleted.cookies["sectest1.example.org"], michael@0: "Nothing got deleted for Cookies as the same hostname is still present"); michael@0: michael@0: ok(!data.changed || !data.changed.cookies || michael@0: !data.changed.cookies["http://sectest1.example.org"], michael@0: "Nothing got changed for cookies"); michael@0: ok(!data.changed || !data.changed.localStorage || michael@0: !data.changed.localStorage["http://sectest1.example.org"], michael@0: "Nothing got changed for local storage"); michael@0: ok(!data.changed || !data.changed.sessionStorage || michael@0: !data.changed.sessionStorage["http://sectest1.example.org"], michael@0: "Nothing got changed for session storage"); michael@0: michael@0: ok(!data.added || !data.added.cookies || michael@0: !data.added.cookies["http://sectest1.example.org"], michael@0: "Nothing got added for cookies"); michael@0: ok(!data.added || !data.added.localStorage || michael@0: !data.added.localStorage["http://sectest1.example.org"], michael@0: "Nothing got added for local storage"); michael@0: ok(!data.added || !data.added.sessionStorage || michael@0: !data.added.sessionStorage["http://sectest1.example.org"], michael@0: "Nothing got added for session storage"); michael@0: michael@0: if (!Object.keys(shouldBeEmpty).length) { michael@0: info("Everything to be received is received."); michael@0: endTestReloaded(); michael@0: } michael@0: }; michael@0: michael@0: let endTestReloaded = () => { michael@0: gFront.off("stores-update", onStoresUpdate); michael@0: reloaded.resolve(); michael@0: }; michael@0: michael@0: gFront.on("stores-update", onStoresUpdate); michael@0: michael@0: for (let iframe of content.document.querySelectorAll("iframe")) { michael@0: if (iframe.src.startsWith("http:")) { michael@0: iframe.remove(); michael@0: break; michael@0: } michael@0: } michael@0: return reloaded.promise; michael@0: } michael@0: michael@0: function test() { michael@0: waitForExplicitFinish(); michael@0: addTab(MAIN_DOMAIN + "storage-dynamic-windows.html", function(doc) { michael@0: try { michael@0: // Sometimes debugger server does not get destroyed correctly by previous michael@0: // tests. michael@0: DebuggerServer.destroy(); michael@0: } catch (ex) { } michael@0: DebuggerServer.init(function () { return true; }); michael@0: DebuggerServer.addBrowserActors(); michael@0: michael@0: let createConnection = () => { michael@0: let client = new DebuggerClient(DebuggerServer.connectPipe()); michael@0: client.connect(function onConnect() { michael@0: client.listTabs(function onListTabs(aResponse) { michael@0: let form = aResponse.tabs[aResponse.selected]; michael@0: gFront = StorageFront(client, form); michael@0: michael@0: gFront.listStores().then(data => testStores(data, client)); michael@0: }); michael@0: }); michael@0: }; michael@0: michael@0: /** michael@0: * This method iterates over iframes in a window and setups the indexed db michael@0: * required for this test. michael@0: */ michael@0: let setupIDBInFrames = (w, i, c) => { michael@0: if (w[i] && w[i].idbGenerator) { michael@0: w[i].setupIDB = w[i].idbGenerator(() => setupIDBInFrames(w, i + 1, c)); michael@0: w[i].setupIDB.next(); michael@0: } michael@0: else if (w[i] && w[i + 1]) { michael@0: setupIDBInFrames(w, i + 1, c); michael@0: } michael@0: else { michael@0: c(); michael@0: } michael@0: }; michael@0: // Setup the indexed db in main window. michael@0: gWindow = doc.defaultView.wrappedJSObject; michael@0: gWindow.setupIDB = gWindow.idbGenerator(() => { michael@0: setupIDBInFrames(gWindow, 0, createConnection); michael@0: }); michael@0: gWindow.setupIDB.next(); michael@0: }); michael@0: }