mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 952998 - Use FrameTree to collect DOMSessionStorage data r=yoric
From 594cc2bdabe535ef356276bce49c4b36c73ab3a2 Mon Sep 17 00:00:00 2001
This commit is contained in:
parent
ad5b3f57fa
commit
17ef08ca03
@ -333,12 +333,12 @@ let SessionStorageListener = {
|
||||
init: function () {
|
||||
addEventListener("MozStorageChanged", this);
|
||||
Services.obs.addObserver(this, "browser:purge-domain-data", true);
|
||||
Services.obs.addObserver(this, "browser:purge-session-history", true);
|
||||
gFrameTree.addObserver(this);
|
||||
},
|
||||
|
||||
handleEvent: function (event) {
|
||||
// Ignore events triggered by localStorage or globalStorage changes.
|
||||
if (isSessionStorageEvent(event)) {
|
||||
if (gFrameTree.contains(event.target) && isSessionStorageEvent(event)) {
|
||||
this.collect();
|
||||
}
|
||||
},
|
||||
@ -350,7 +350,17 @@ let SessionStorageListener = {
|
||||
},
|
||||
|
||||
collect: function () {
|
||||
MessageQueue.push("storage", () => SessionStorage.collect(docShell));
|
||||
if (docShell) {
|
||||
MessageQueue.push("storage", () => SessionStorage.collect(docShell, gFrameTree));
|
||||
}
|
||||
},
|
||||
|
||||
onFrameTreeCollected: function () {
|
||||
this.collect();
|
||||
},
|
||||
|
||||
onFrameTreeReset: function () {
|
||||
this.collect();
|
||||
},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
|
||||
|
@ -11,7 +11,7 @@ const Ci = Components.interfaces;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
|
||||
|
||||
const EXPORTED_METHODS = ["addObserver", "contains", "map"];
|
||||
const EXPORTED_METHODS = ["addObserver", "contains", "map", "forEach"];
|
||||
|
||||
/**
|
||||
* A FrameTree represents all frames that were reachable when the document
|
||||
@ -161,6 +161,34 @@ FrameTreeInternal.prototype = {
|
||||
return walk(this.content);
|
||||
},
|
||||
|
||||
/**
|
||||
* Applies the given function |cb| to all frames stored in the tree. Use this
|
||||
* method if |map()| doesn't suit your needs and you want more control over
|
||||
* how data is collected.
|
||||
*
|
||||
* @param cb (function)
|
||||
* This callback receives the current frame as the only argument.
|
||||
*/
|
||||
forEach: function (cb) {
|
||||
let frames = this._frames;
|
||||
|
||||
function walk(frame) {
|
||||
cb(frame);
|
||||
|
||||
if (!frames.has(frame)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Array.forEach(frame.frames, subframe => {
|
||||
if (frames.has(subframe)) {
|
||||
cb(subframe);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
walk(this.content);
|
||||
},
|
||||
|
||||
/**
|
||||
* Stores a given |frame| and its children in the frame tree.
|
||||
*
|
||||
|
@ -14,20 +14,28 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "console",
|
||||
"resource://gre/modules/devtools/Console.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PrivacyLevel",
|
||||
"resource:///modules/sessionstore/PrivacyLevel.jsm");
|
||||
|
||||
// Returns the principal for a given |frame| contained in a given |docShell|.
|
||||
function getPrincipalForFrame(docShell, frame) {
|
||||
let ssm = Services.scriptSecurityManager;
|
||||
let doc = frame && frame.document;
|
||||
let uri = Services.io.newURI(doc.documentURI, null, null);
|
||||
return ssm.getDocShellCodebasePrincipal(uri, docShell);
|
||||
}
|
||||
|
||||
this.SessionStorage = Object.freeze({
|
||||
/**
|
||||
* Updates all sessionStorage "super cookies"
|
||||
* @param aDocShell
|
||||
* @param docShell
|
||||
* That tab's docshell (containing the sessionStorage)
|
||||
* @param frameTree
|
||||
* The docShell's FrameTree instance.
|
||||
* @return Returns a nested object that will have hosts as keys and per-host
|
||||
* session storage data as values. For example:
|
||||
* {"example.com": {"key": "value", "my_number": 123}}
|
||||
*/
|
||||
collect: function (aDocShell) {
|
||||
return SessionStorageInternal.collect(aDocShell);
|
||||
collect: function (docShell, frameTree) {
|
||||
return SessionStorageInternal.collect(docShell, frameTree);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -47,36 +55,40 @@ this.SessionStorage = Object.freeze({
|
||||
let SessionStorageInternal = {
|
||||
/**
|
||||
* Reads all session storage data from the given docShell.
|
||||
* @param aDocShell
|
||||
* @param docShell
|
||||
* A tab's docshell (containing the sessionStorage)
|
||||
* @param frameTree
|
||||
* The docShell's FrameTree instance.
|
||||
* @return Returns a nested object that will have hosts as keys and per-host
|
||||
* session storage data as values. For example:
|
||||
* {"example.com": {"key": "value", "my_number": 123}}
|
||||
*/
|
||||
collect: function (aDocShell) {
|
||||
collect: function (docShell, frameTree) {
|
||||
let data = {};
|
||||
let webNavigation = aDocShell.QueryInterface(Ci.nsIWebNavigation);
|
||||
let shistory = webNavigation.sessionHistory;
|
||||
let visitedOrigins = new Set();
|
||||
|
||||
for (let i = 0; shistory && i < shistory.count; i++) {
|
||||
let principal = History.getPrincipalForEntry(shistory, i, aDocShell);
|
||||
frameTree.forEach(frame => {
|
||||
let principal = getPrincipalForFrame(docShell, frame);
|
||||
if (!principal) {
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the root domain of the current history entry
|
||||
// and use that as a key for the per-host storage data.
|
||||
let origin = principal.jarPrefix + principal.origin;
|
||||
if (data.hasOwnProperty(origin)) {
|
||||
if (visitedOrigins.has(origin)) {
|
||||
// Don't read a host twice.
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
|
||||
let originData = this._readEntry(principal, aDocShell);
|
||||
// Mark the current origin as visited.
|
||||
visitedOrigins.add(origin);
|
||||
|
||||
let originData = this._readEntry(principal, docShell);
|
||||
if (Object.keys(originData).length) {
|
||||
data[origin] = originData;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return Object.keys(data).length ? data : null;
|
||||
},
|
||||
@ -91,10 +103,11 @@ let SessionStorageInternal = {
|
||||
* {"example.com": {"key": "value", "my_number": 123}}
|
||||
*/
|
||||
restore: function (aDocShell, aStorageData) {
|
||||
for (let [host, data] in Iterator(aStorageData)) {
|
||||
for (let host of Object.keys(aStorageData)) {
|
||||
let data = aStorageData[host];
|
||||
let uri = Services.io.newURI(host, null, null);
|
||||
let principal = Services.scriptSecurityManager.getDocShellCodebasePrincipal(uri, aDocShell);
|
||||
let storageManager = aDocShell.QueryInterface(Components.interfaces.nsIDOMStorageManager);
|
||||
let storageManager = aDocShell.QueryInterface(Ci.nsIDOMStorageManager);
|
||||
|
||||
// There is no need to pass documentURI, it's only used to fill documentURI property of
|
||||
// domstorage event, which in this case has no consumer. Prevention of events in case
|
||||
@ -124,7 +137,7 @@ let SessionStorageInternal = {
|
||||
let storage;
|
||||
|
||||
try {
|
||||
let storageManager = aDocShell.QueryInterface(Components.interfaces.nsIDOMStorageManager);
|
||||
let storageManager = aDocShell.QueryInterface(Ci.nsIDOMStorageManager);
|
||||
storage = storageManager.getStorage(aPrincipal);
|
||||
} catch (e) {
|
||||
// sessionStorage might throw if it's turned off, see bug 458954
|
||||
@ -144,25 +157,3 @@ let SessionStorageInternal = {
|
||||
return hostData;
|
||||
}
|
||||
};
|
||||
|
||||
let History = {
|
||||
/**
|
||||
* Returns a given history entry's URI.
|
||||
* @param aHistory
|
||||
* That tab's session history
|
||||
* @param aIndex
|
||||
* The history entry's index
|
||||
* @param aDocShell
|
||||
* That tab's docshell
|
||||
*/
|
||||
getPrincipalForEntry: function History_getPrincipalForEntry(aHistory,
|
||||
aIndex,
|
||||
aDocShell) {
|
||||
try {
|
||||
return Services.scriptSecurityManager.getDocShellCodebasePrincipal(
|
||||
aHistory.getEntryAtIndex(aIndex, false).URI, aDocShell);
|
||||
} catch (e) {
|
||||
// This might throw for some reason.
|
||||
}
|
||||
},
|
||||
};
|
||||
|
@ -366,7 +366,7 @@ let TabStateInternal = {
|
||||
if (key != "storage" || includePrivateData) {
|
||||
tabData[key] = data[key];
|
||||
} else {
|
||||
tabData.storage = {};
|
||||
let storage = {};
|
||||
let isPinned = tab.pinned;
|
||||
|
||||
// If we're not allowed to include private data, let's filter out hosts
|
||||
@ -374,9 +374,13 @@ let TabStateInternal = {
|
||||
for (let host of Object.keys(data.storage)) {
|
||||
let isHttps = host.startsWith("https:");
|
||||
if (PrivacyLevel.canSave({isHttps: isHttps, isPinned: isPinned})) {
|
||||
tabData.storage[host] = data.storage[host];
|
||||
storage[host] = data.storage[host];
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.keys(storage).length) {
|
||||
tabData.storage = storage;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -20,6 +20,7 @@ support-files =
|
||||
browser_pageStyle_sample_nested.html
|
||||
browser_scrollPositions_sample.html
|
||||
browser_scrollPositions_sample_frameset.html
|
||||
browser_sessionStorage.html
|
||||
browser_248970_b_sample.html
|
||||
browser_339445_sample.html
|
||||
browser_346337_sample.html
|
||||
|
@ -0,0 +1,26 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>browser_sessionStorage.html</title>
|
||||
</head>
|
||||
<body>
|
||||
<script type="text/javascript;version=1.8">
|
||||
let isOuter = window == window.top;
|
||||
let args = window.location.search.slice(1).split("&");
|
||||
let rand = args[0];
|
||||
|
||||
if (isOuter) {
|
||||
let iframe = document.createElement("iframe");
|
||||
let isSecure = args.indexOf("secure") > -1;
|
||||
let scheme = isSecure ? "https" : "http";
|
||||
iframe.setAttribute("src", scheme + "://example.com" + location.pathname + "?" + rand);
|
||||
document.body.appendChild(iframe);
|
||||
}
|
||||
|
||||
if (sessionStorage.length === 0) {
|
||||
sessionStorage.test = (isOuter ? "outer" : "inner") + "-value-" + rand;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -3,35 +3,39 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
let tmp = {};
|
||||
Cu.import("resource://gre/modules/Promise.jsm", tmp);
|
||||
let {Promise} = tmp;
|
||||
const RAND = Math.random();
|
||||
const URL = "http://mochi.test:8888/browser/" +
|
||||
"browser/components/sessionstore/test/browser_sessionStorage.html" +
|
||||
"?" + RAND;
|
||||
|
||||
const INITIAL_VALUE = "initial-value-" + Date.now();
|
||||
const OUTER_VALUE = "outer-value-" + RAND;
|
||||
const INNER_VALUE = "inner-value-" + RAND;
|
||||
|
||||
/**
|
||||
* This test ensures that setting, modifying and restoring sessionStorage data
|
||||
* works as expected.
|
||||
*/
|
||||
add_task(function session_storage() {
|
||||
let tab = yield createTabWithStorageData(["http://example.com", "http://mochi.test:8888"]);
|
||||
let tab = gBrowser.addTab(URL);
|
||||
let browser = tab.linkedBrowser;
|
||||
yield promiseBrowserLoaded(browser);
|
||||
|
||||
// Flush to make sure chrome received all data.
|
||||
SyncHandlers.get(browser).flush();
|
||||
|
||||
let {storage} = JSON.parse(ss.getTabState(tab));
|
||||
is(storage["http://example.com"].test, INITIAL_VALUE,
|
||||
is(storage["http://example.com"].test, INNER_VALUE,
|
||||
"sessionStorage data for example.com has been serialized correctly");
|
||||
is(storage["http://mochi.test:8888"].test, INITIAL_VALUE,
|
||||
is(storage["http://mochi.test:8888"].test, OUTER_VALUE,
|
||||
"sessionStorage data for mochi.test has been serialized correctly");
|
||||
|
||||
// Ensure that modifying sessionStore values works.
|
||||
yield modifySessionStorage(browser, {test: "modified"});
|
||||
yield modifySessionStorage2(browser, {test: "modified2"});
|
||||
SyncHandlers.get(browser).flush();
|
||||
|
||||
let {storage} = JSON.parse(ss.getTabState(tab));
|
||||
is(storage["http://example.com"].test, INITIAL_VALUE,
|
||||
is(storage["http://example.com"].test, "modified2",
|
||||
"sessionStorage data for example.com has been serialized correctly");
|
||||
is(storage["http://mochi.test:8888"].test, "modified",
|
||||
"sessionStorage data for mochi.test has been serialized correctly");
|
||||
@ -45,22 +49,40 @@ add_task(function session_storage() {
|
||||
SyncHandlers.get(browser2).flush();
|
||||
|
||||
let {storage} = JSON.parse(ss.getTabState(tab2));
|
||||
is(storage["http://example.com"].test, INITIAL_VALUE,
|
||||
is(storage["http://example.com"].test, "modified2",
|
||||
"sessionStorage data for example.com has been duplicated correctly");
|
||||
is(storage["http://mochi.test:8888"].test, "modified",
|
||||
"sessionStorage data for mochi.test has been duplicated correctly");
|
||||
|
||||
// Ensure that the content script retains restored data
|
||||
// (by e.g. duplicateTab) and send it along with new data.
|
||||
yield modifySessionStorage(browser2, {test: "modified2"});
|
||||
// (by e.g. duplicateTab) and sends it along with new data.
|
||||
yield modifySessionStorage(browser2, {test: "modified3"});
|
||||
SyncHandlers.get(browser2).flush();
|
||||
|
||||
let {storage} = JSON.parse(ss.getTabState(tab2));
|
||||
is(storage["http://example.com"].test, INITIAL_VALUE,
|
||||
is(storage["http://example.com"].test, "modified2",
|
||||
"sessionStorage data for example.com has been duplicated correctly");
|
||||
is(storage["http://mochi.test:8888"].test, "modified2",
|
||||
is(storage["http://mochi.test:8888"].test, "modified3",
|
||||
"sessionStorage data for mochi.test has been duplicated correctly");
|
||||
|
||||
// Check that loading a new URL discards data.
|
||||
browser2.loadURI("http://mochi.test:8888/");
|
||||
yield promiseBrowserLoaded(browser2);
|
||||
SyncHandlers.get(browser2).flush();
|
||||
|
||||
let {storage} = JSON.parse(ss.getTabState(tab2));
|
||||
is(storage["http://mochi.test:8888"].test, "modified3",
|
||||
"navigating retains correct storage data");
|
||||
ok(!storage["http://example.com"], "storage data was discarded");
|
||||
|
||||
// Check that loading a new URL discards data.
|
||||
browser2.loadURI("about:mozilla");
|
||||
yield promiseBrowserLoaded(browser2);
|
||||
SyncHandlers.get(browser2).flush();
|
||||
|
||||
let state = JSON.parse(ss.getTabState(tab2));
|
||||
ok(!state.hasOwnProperty("storage"), "storage data was discarded");
|
||||
|
||||
// Clean up.
|
||||
gBrowser.removeTab(tab);
|
||||
gBrowser.removeTab(tab2);
|
||||
@ -71,10 +93,12 @@ add_task(function session_storage() {
|
||||
* sessionStorage data collected for tabs.
|
||||
*/
|
||||
add_task(function purge_domain() {
|
||||
let tab = yield createTabWithStorageData(["http://example.com", "http://mochi.test:8888"]);
|
||||
let tab = gBrowser.addTab(URL);
|
||||
let browser = tab.linkedBrowser;
|
||||
yield promiseBrowserLoaded(browser);
|
||||
|
||||
yield notifyObservers(browser, "browser:purge-domain-data", "mochi.test");
|
||||
// Purge data for "mochi.test".
|
||||
yield purgeDomainData(browser, "mochi.test");
|
||||
|
||||
// Flush to make sure chrome received all data.
|
||||
SyncHandlers.get(browser).flush();
|
||||
@ -82,56 +106,36 @@ add_task(function purge_domain() {
|
||||
let {storage} = JSON.parse(ss.getTabState(tab));
|
||||
ok(!storage["http://mochi.test:8888"],
|
||||
"sessionStorage data for mochi.test has been purged");
|
||||
is(storage["http://example.com"].test, INITIAL_VALUE,
|
||||
is(storage["http://example.com"].test, INNER_VALUE,
|
||||
"sessionStorage data for example.com has been preserved");
|
||||
|
||||
gBrowser.removeTab(tab);
|
||||
});
|
||||
|
||||
/**
|
||||
* This test ensures that purging session history data also purges data from
|
||||
* sessionStorage data collected for tabs
|
||||
*/
|
||||
add_task(function purge_shistory() {
|
||||
let tab = yield createTabWithStorageData(["http://example.com", "http://mochi.test:8888"]);
|
||||
let browser = tab.linkedBrowser;
|
||||
|
||||
yield notifyObservers(browser, "browser:purge-session-history");
|
||||
|
||||
// Flush to make sure chrome received all data.
|
||||
SyncHandlers.get(browser).flush();
|
||||
|
||||
let {storage} = JSON.parse(ss.getTabState(tab));
|
||||
ok(!storage["http://example.com"],
|
||||
"sessionStorage data for example.com has been purged");
|
||||
is(storage["http://mochi.test:8888"].test, INITIAL_VALUE,
|
||||
"sessionStorage data for mochi.test has been preserved");
|
||||
|
||||
gBrowser.removeTab(tab);
|
||||
});
|
||||
|
||||
/**
|
||||
* This test ensures that collecting sessionStorage data respects the privacy
|
||||
* levels as set by the user.
|
||||
*/
|
||||
add_task(function respect_privacy_level() {
|
||||
let tab = yield createTabWithStorageData(["http://example.com", "https://example.com"]);
|
||||
let tab = gBrowser.addTab(URL + "&secure");
|
||||
yield promiseBrowserLoaded(tab.linkedBrowser);
|
||||
gBrowser.removeTab(tab);
|
||||
|
||||
let [{state: {storage}}] = JSON.parse(ss.getClosedTabData(window));
|
||||
is(storage["http://example.com"].test, INITIAL_VALUE,
|
||||
is(storage["http://mochi.test:8888"].test, OUTER_VALUE,
|
||||
"http sessionStorage data has been saved");
|
||||
is(storage["https://example.com"].test, INITIAL_VALUE,
|
||||
is(storage["https://example.com"].test, INNER_VALUE,
|
||||
"https sessionStorage data has been saved");
|
||||
|
||||
// Disable saving data for encrypted sites.
|
||||
Services.prefs.setIntPref("browser.sessionstore.privacy_level", 1);
|
||||
|
||||
let tab = yield createTabWithStorageData(["http://example.com", "https://example.com"]);
|
||||
let tab = gBrowser.addTab(URL + "&secure");
|
||||
yield promiseBrowserLoaded(tab.linkedBrowser);
|
||||
gBrowser.removeTab(tab);
|
||||
|
||||
let [{state: {storage}}] = JSON.parse(ss.getClosedTabData(window));
|
||||
is(storage["http://example.com"].test, INITIAL_VALUE,
|
||||
is(storage["http://mochi.test:8888"].test, OUTER_VALUE,
|
||||
"http sessionStorage data has been saved");
|
||||
ok(!storage["https://example.com"],
|
||||
"https sessionStorage data has *not* been saved");
|
||||
@ -140,17 +144,15 @@ add_task(function respect_privacy_level() {
|
||||
Services.prefs.setIntPref("browser.sessionstore.privacy_level", 2);
|
||||
|
||||
// Check that duplicating a tab copies all private data.
|
||||
let tab = yield createTabWithStorageData(["http://example.com", "https://example.com"]);
|
||||
let tab = gBrowser.addTab(URL + "&secure");
|
||||
yield promiseBrowserLoaded(tab.linkedBrowser);
|
||||
let tab2 = gBrowser.duplicateTab(tab);
|
||||
yield promiseBrowserLoaded(tab2.linkedBrowser);
|
||||
yield promiseTabRestored(tab2);
|
||||
gBrowser.removeTab(tab);
|
||||
|
||||
// With privacy_level=2 the |tab| shouldn't have any sessionStorage data.
|
||||
let [{state: {storage}}] = JSON.parse(ss.getClosedTabData(window));
|
||||
ok(!storage["http://example.com"],
|
||||
"http sessionStorage data has *not* been saved");
|
||||
ok(!storage["https://example.com"],
|
||||
"https sessionStorage data has *not* been saved");
|
||||
ok(!storage, "sessionStorage data has *not* been saved");
|
||||
|
||||
// Restore the default privacy level and close the duplicated tab.
|
||||
Services.prefs.clearUserPref("browser.sessionstore.privacy_level");
|
||||
@ -158,42 +160,26 @@ add_task(function respect_privacy_level() {
|
||||
|
||||
// With privacy_level=0 the duplicated |tab2| should persist all data.
|
||||
let [{state: {storage}}] = JSON.parse(ss.getClosedTabData(window));
|
||||
is(storage["http://example.com"].test, INITIAL_VALUE,
|
||||
is(storage["http://mochi.test:8888"].test, OUTER_VALUE,
|
||||
"http sessionStorage data has been saved");
|
||||
is(storage["https://example.com"].test, INITIAL_VALUE,
|
||||
is(storage["https://example.com"].test, INNER_VALUE,
|
||||
"https sessionStorage data has been saved");
|
||||
});
|
||||
|
||||
function createTabWithStorageData(urls) {
|
||||
return Task.spawn(function task() {
|
||||
let tab = gBrowser.addTab();
|
||||
let browser = tab.linkedBrowser;
|
||||
|
||||
for (let url of urls) {
|
||||
browser.loadURI(url);
|
||||
yield promiseBrowserLoaded(browser);
|
||||
yield modifySessionStorage(browser, {test: INITIAL_VALUE});
|
||||
}
|
||||
|
||||
throw new Task.Result(tab);
|
||||
});
|
||||
}
|
||||
|
||||
function waitForStorageEvent(browser) {
|
||||
return promiseContentMessage(browser, "ss-test:MozStorageChanged");
|
||||
}
|
||||
|
||||
function waitForUpdateMessage(browser) {
|
||||
return promiseContentMessage(browser, "SessionStore:update");
|
||||
}
|
||||
|
||||
function modifySessionStorage(browser, data) {
|
||||
browser.messageManager.sendAsyncMessage("ss-test:modifySessionStorage", data);
|
||||
return waitForStorageEvent(browser);
|
||||
}
|
||||
|
||||
function notifyObservers(browser, topic, data) {
|
||||
let msg = {topic: topic, data: data};
|
||||
browser.messageManager.sendAsyncMessage("ss-test:notifyObservers", msg);
|
||||
return waitForUpdateMessage(browser);
|
||||
function modifySessionStorage2(browser, data) {
|
||||
browser.messageManager.sendAsyncMessage("ss-test:modifySessionStorage2", data);
|
||||
return waitForStorageEvent(browser);
|
||||
}
|
||||
|
||||
function purgeDomainData(browser, domain) {
|
||||
return sendMessage(browser, "ss-test:purgeDomainData", domain);
|
||||
}
|
||||
|
@ -33,8 +33,15 @@ addMessageListener("ss-test:modifySessionStorage", function (msg) {
|
||||
}
|
||||
});
|
||||
|
||||
addMessageListener("ss-test:notifyObservers", function ({data: {topic, data}}) {
|
||||
Services.obs.notifyObservers(null, topic, data || "");
|
||||
addMessageListener("ss-test:modifySessionStorage2", function (msg) {
|
||||
for (let key of Object.keys(msg.data)) {
|
||||
content.frames[0].sessionStorage[key] = msg.data[key];
|
||||
}
|
||||
});
|
||||
|
||||
addMessageListener("ss-test:purgeDomainData", function ({data: domain}) {
|
||||
Services.obs.notifyObservers(null, "browser:purge-domain-data", domain);
|
||||
content.setTimeout(() => sendAsyncMessage("ss-test:purgeDomainData"));
|
||||
});
|
||||
|
||||
addMessageListener("ss-test:getStyleSheets", function (msg) {
|
||||
|
Loading…
Reference in New Issue
Block a user