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 gTests; michael@0: let gExpected; michael@0: let index = 0; michael@0: michael@0: const beforeReload = { michael@0: cookies: ["test1.example.org", "sectest1.example.org"], michael@0: localStorage: ["http://test1.example.org", "http://sectest1.example.org"], michael@0: sessionStorage: ["http://test1.example.org", "http://sectest1.example.org"], michael@0: }; michael@0: michael@0: function finishTests(client) { 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: DebuggerClient = DebuggerServer = gTests = null; michael@0: finish(); 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: 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: if (!Object.keys(toBeEmptied[storageType]).length) { michael@0: delete toBeEmptied[storageType]; michael@0: } michael@0: } michael@0: } michael@0: michael@0: function onStoresCleared(data) { michael@0: if (data.sessionStorage || data.localStorage) { michael@0: let hosts = data.sessionStorage || data.localStorage; michael@0: info("Stores cleared required for session storage"); michael@0: is(hosts.length, 1, "number of hosts is 1"); michael@0: is(hosts[0], "http://test1.example.org", michael@0: "host matches for " + Object.keys(data)[0]); michael@0: gTests.next(); michael@0: } michael@0: else { michael@0: ok(false, "Stores cleared should only be for local and sesion storage"); michael@0: } michael@0: michael@0: } michael@0: michael@0: function onStoresUpdate({added, changed, deleted}) { michael@0: info("inside stores update for index " + index); michael@0: michael@0: // Here, added, changed and deleted might be null even if they are required as michael@0: // per gExpected. This is fine as they might come in the next stores-update michael@0: // call or have already come in the previous one. michael@0: if (added) { michael@0: info("matching added object for index " + index); michael@0: markOutMatched(gExpected.added, added); michael@0: } michael@0: if (changed) { michael@0: info("matching changed object for index " + index); michael@0: markOutMatched(gExpected.changed, changed); michael@0: } michael@0: if (deleted) { michael@0: info("matching deleted object for index " + index); michael@0: markOutMatched(gExpected.deleted, deleted); michael@0: } michael@0: michael@0: if ((!gExpected.added || !Object.keys(gExpected.added).length) && michael@0: (!gExpected.changed || !Object.keys(gExpected.changed).length) && michael@0: (!gExpected.deleted || !Object.keys(gExpected.deleted).length)) { michael@0: info("Everything expected has been received for index " + index); michael@0: index++; michael@0: gTests.next(); michael@0: } michael@0: else { michael@0: info("Still some updates pending for index " + index); michael@0: } michael@0: } michael@0: michael@0: function* UpdateTests(front, win, client) { michael@0: front.on("stores-update", onStoresUpdate); michael@0: michael@0: // index 0 michael@0: gExpected = { michael@0: added: { michael@0: cookies: { michael@0: "test1.example.org": ["c1", "c2"] michael@0: }, michael@0: localStorage: { michael@0: "http://test1.example.org": ["l1"] michael@0: } michael@0: } michael@0: }; michael@0: win.addCookie("c1", "foobar1"); michael@0: win.addCookie("c2", "foobar2"); michael@0: win.localStorage.setItem("l1", "foobar1"); michael@0: yield undefined; michael@0: michael@0: // index 1 michael@0: gExpected = { michael@0: changed: { michael@0: cookies: { michael@0: "test1.example.org": ["c1"] michael@0: } michael@0: }, michael@0: added: { michael@0: localStorage: { michael@0: "http://test1.example.org": ["l2"] michael@0: } michael@0: } michael@0: }; michael@0: win.addCookie("c1", "new_foobar1"); michael@0: win.localStorage.setItem("l2", "foobar2"); michael@0: yield undefined; michael@0: michael@0: // index 2 michael@0: gExpected = { michael@0: deleted: { michael@0: cookies: { michael@0: "test1.example.org": ["c2"] michael@0: }, michael@0: localStorage: { michael@0: "http://test1.example.org": ["l1"] michael@0: } michael@0: }, michael@0: added: { michael@0: localStorage: { michael@0: "http://test1.example.org": ["l3"] michael@0: } michael@0: } michael@0: }; michael@0: win.removeCookie("c2"); michael@0: win.localStorage.removeItem("l1"); michael@0: win.localStorage.setItem("l3", "foobar3"); michael@0: yield undefined; michael@0: michael@0: // index 3 michael@0: gExpected = { michael@0: added: { michael@0: cookies: { michael@0: "test1.example.org": ["c3"] michael@0: }, michael@0: sessionStorage: { michael@0: "http://test1.example.org": ["s1", "s2"] michael@0: } michael@0: }, michael@0: changed: { michael@0: localStorage: { michael@0: "http://test1.example.org": ["l3"] michael@0: } michael@0: }, michael@0: deleted: { michael@0: cookies: { michael@0: "test1.example.org": ["c1"] michael@0: }, michael@0: localStorage: { michael@0: "http://test1.example.org": ["l2"] michael@0: } michael@0: } michael@0: }; michael@0: win.removeCookie("c1"); michael@0: win.addCookie("c3", "foobar3"); michael@0: win.localStorage.removeItem("l2"); michael@0: win.sessionStorage.setItem("s1", "foobar1"); michael@0: win.sessionStorage.setItem("s2", "foobar2"); michael@0: win.localStorage.setItem("l3", "new_foobar3"); michael@0: yield undefined; michael@0: michael@0: // index 4 michael@0: gExpected = { michael@0: deleted: { michael@0: sessionStorage: { michael@0: "http://test1.example.org": ["s1"] michael@0: } michael@0: } michael@0: }; michael@0: win.sessionStorage.removeItem("s1"); michael@0: yield undefined; michael@0: michael@0: // index 5 michael@0: gExpected = { michael@0: deleted: { michael@0: cookies: { michael@0: "test1.example.org": ["c3"] michael@0: } michael@0: } michael@0: }; michael@0: front.on("stores-cleared", onStoresCleared); michael@0: win.clear(); michael@0: yield undefined; michael@0: // Another 2 more yield undefined s so as to wait for the "stores-cleared" to michael@0: // fire. One for Local Storage and other for Session Storage michael@0: yield undefined; michael@0: yield undefined; michael@0: michael@0: front.off("stores-cleared", onStoresCleared); michael@0: front.off("stores-update", onStoresUpdate); michael@0: finishTests(client); michael@0: } michael@0: michael@0: michael@0: function test() { michael@0: waitForExplicitFinish(); michael@0: addTab(MAIN_DOMAIN + "storage-updates.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 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: let front = StorageFront(client, form); michael@0: gTests = UpdateTests(front, doc.defaultView.wrappedJSObject, michael@0: client); michael@0: // Make an initial call to initialize the actor michael@0: front.listStores().then(() => gTests.next()); michael@0: }); michael@0: }); michael@0: }) michael@0: }