Bug 930967 - Add tests for broadcasting sessionstore data r=yoric,billm,smacleod

From 31c74a1bd1244404b5a98348b1b4ca4e3698afce Mon Sep 17 00:00:00 2001
This commit is contained in:
Tim Taubert 2013-10-25 12:02:25 +02:00
parent c9ef718ff6
commit 0fdfb159bb
14 changed files with 630 additions and 189 deletions

View File

@ -10,13 +10,15 @@
[DEFAULT]
support-files =
head.js
content.js
browser_form_restore_events_sample.html
browser_formdata_format_sample.html
browser_input_sample.html
browser_pageStyle_sample.html
browser_pageStyle_sample_nested.html
browser_248970_b_sample.html
browser_339445_sample.html
browser_346337_sample.html
browser_408470_sample.html
browser_423132_sample.html
browser_447951_sample.html
browser_454908_sample.html
@ -47,6 +49,7 @@ support-files =
#disabled-for-intermittent-failures--bug-765389, browser_461743_sample.html
[browser_attributes.js]
[browser_broadcast.js]
[browser_capabilities.js]
[browser_dying_cache.js]
[browser_form_restore_events.js]
@ -54,6 +57,7 @@ support-files =
[browser_global_store.js]
[browser_input.js]
[browser_pageshow.js]
[browser_pageStyle.js]
[browser_sessionStorage.js]
[browser_swapDocShells.js]
[browser_tabStateCache.js]
@ -74,7 +78,6 @@ skip-if = true
[browser_394759_behavior.js]
[browser_394759_perwindowpb.js]
[browser_394759_purge.js]
[browser_408470.js]
[browser_423132.js]
[browser_447951.js]
[browser_448741.js]

View File

@ -34,7 +34,7 @@ function test() {
EventUtils.sendMouseEvent({type: "click"}, chk);
let browser = newWin.gBrowser.selectedBrowser;
waitForContentMessage(browser, "SessionStore:input", 1000, result => {
promiseContentMessage(browser, "SessionStore:input").then(result => {
ok(result, "received message for input changes");
newWin.close();

View File

@ -1,56 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
function test() {
/** Test for Bug 408470 **/
waitForExplicitFinish();
let pendingCount = 1;
let rootDir = getRootDirectory(gTestPath);
let testUrl = rootDir + "browser_408470_sample.html";
let tab = gBrowser.addTab(testUrl);
tab.linkedBrowser.addEventListener("load", function(aEvent) {
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
// enable all stylesheets and verify that they're correctly persisted
Array.forEach(tab.linkedBrowser.contentDocument.styleSheets, function(aSS, aIx) {
pendingCount++;
let ssTitle = aSS.title;
gPageStyleMenu.switchStyleSheet(ssTitle, tab.linkedBrowser.contentWindow);
let newTab = gBrowser.duplicateTab(tab);
newTab.linkedBrowser.addEventListener("load", function(aEvent) {
newTab.linkedBrowser.removeEventListener("load", arguments.callee, true);
let states = Array.map(newTab.linkedBrowser.contentDocument.styleSheets,
function(aSS) !aSS.disabled);
let correct = states.indexOf(true) == aIx && states.indexOf(true, aIx + 1) == -1;
if (/^fail_/.test(ssTitle))
ok(!correct, "didn't restore stylesheet " + ssTitle);
else
ok(correct, "restored stylesheet " + ssTitle);
gBrowser.removeTab(newTab);
if (--pendingCount == 0)
finish();
}, true);
});
// disable all styles and verify that this is correctly persisted
tab.linkedBrowser.markupDocumentViewer.authorStyleDisabled = true;
let newTab = gBrowser.duplicateTab(tab);
newTab.linkedBrowser.addEventListener("load", function(aEvent) {
newTab.linkedBrowser.removeEventListener("load", arguments.callee, true);
is(newTab.linkedBrowser.markupDocumentViewer.authorStyleDisabled, true,
"disabled all stylesheets");
gBrowser.removeTab(newTab);
if (--pendingCount == 0)
finish();
}, true);
gBrowser.removeTab(tab);
}, true);
}

View File

@ -1,19 +0,0 @@
<html>
<head>
<title>Test for bug 408470</title>
<link href="404.css" title="default" rel="stylesheet">
<link href="404.css" title="alternate" rel="alternate stylesheet">
<link href="404.css" title="altERnate" rel=" styLEsheet altERnate ">
<link href="404.css" title="media_empty" rel="alternate stylesheet" media="">
<link href="404.css" title="media_all" rel="alternate stylesheet" media="all">
<link href="404.css" title="media_ALL" rel="alternate stylesheet" media=" ALL ">
<link href="404.css" title="media_screen" rel="alternate stylesheet" media="screen">
<link href="404.css" title="media_print_screen" rel="alternate stylesheet" media="print,screen">
<link href="404.css" title="fail_media_print" rel="alternate stylesheet" media="print">
<link href="404.css" title="fail_media_projection" rel="stylesheet" media="projection">
<link href="404.css" title="fail_media_invalid" rel="alternate stylesheet" media="hallo">
</head>
<body></body>
</html>

View File

@ -21,12 +21,7 @@ let {Task, Promise} = Scope;
const URI_TO_LOAD = "about:mozilla";
function waitForLoadStarted(aTab) {
let deferred = Promise.defer();
waitForContentMessage(aTab.linkedBrowser,
"SessionStore:loadStart",
1000,
deferred.resolve);
return deferred.promise;
return promiseContentMessage(aTab.linkedBrowser, "SessionStore:loadStart");
}
function waitForTabLoaded(aTab) {

View File

@ -1,6 +1,10 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
let tmp;
Cu.import("resource:///modules/sessionstore/TabStateCache.jsm", tmp);
let {TabStateCache} = tmp;
const URL = "http://mochi.test:8888/browser/" +
"browser/components/sessionstore/test/browser_916390_sample.html";
@ -24,11 +28,9 @@ function runTests() {
let {formdata} = state.windows[0].tabs[1].entries[0];
is(formdata.id.txt, "m", "txt's value is correct");
// Change the number of session history entries and modify
// DOMSessionStorage data to invalidate the TabStateCache.
// Change the number of session history entries to invalidate the cache.
browser.loadURI(URL + "#");
browser.contentWindow.sessionStorage.foo = "bar";
yield waitForStorageChange();
TabStateCache.delete(browser);
// Check that we'll save the form data state correctly.
let state = JSON.parse(ss.getBrowserState());

View File

@ -0,0 +1,196 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const INITIAL_VALUE = "initial-value-" + Date.now();
/**
* This test ensures we won't lose tab data queued in the content script when
* closing a tab.
*/
add_task(function flush_on_tabclose() {
let tab = yield createTabWithStorageData(["http://example.com"]);
let browser = tab.linkedBrowser;
yield modifySessionStorage(browser, {test: "on-tab-close"});
gBrowser.removeTab(tab);
let [{state: {storage}}] = JSON.parse(ss.getClosedTabData(window));
is(storage["http://example.com"].test, "on-tab-close",
"sessionStorage data has been flushed on TabClose");
});
/**
* This test ensures we won't lose tab data queued in the content script when
* the application tries to quit.
*/
add_task(function flush_on_quit_requested() {
let tab = yield createTabWithStorageData(["http://example.com"]);
let browser = tab.linkedBrowser;
yield modifySessionStorage(browser, {test: "on-quit-requested"});
// Note that sending quit-application-requested should not interfere with
// other tests and code. We're just notifying about a shutdown request but
// we will not send quit-application-granted. Observers will thus assume
// that some other observer has canceled the request.
sendQuitApplicationRequested();
let {storage} = JSON.parse(ss.getTabState(tab));
is(storage["http://example.com"].test, "on-quit-requested",
"sessionStorage data has been flushed when a quit is requested");
gBrowser.removeTab(tab);
});
/**
* This test ensures we won't lose tab data queued in the content script when
* duplicating a tab.
*/
add_task(function flush_on_duplicate() {
let tab = yield createTabWithStorageData(["http://example.com"]);
let browser = tab.linkedBrowser;
yield modifySessionStorage(browser, {test: "on-duplicate"});
let tab2 = ss.duplicateTab(window, tab);
let {storage} = JSON.parse(ss.getTabState(tab2));
is(storage["http://example.com"].test, "on-duplicate",
"sessionStorage data has been flushed when duplicating tabs");
yield promiseTabRestored(tab2);
let {storage} = JSON.parse(ss.getTabState(tab2));
is(storage["http://example.com"].test, "on-duplicate",
"sessionStorage data has been flushed when duplicating tabs");
gBrowser.removeTab(tab);
gBrowser.removeTab(tab2);
});
/**
* This test ensures we won't lose tab data queued in the content script when
* a window is closed.
*/
add_task(function flush_on_windowclose() {
let win = yield promiseNewWindow();
let tab = yield createTabWithStorageData(["http://example.com"], win);
let browser = tab.linkedBrowser;
yield modifySessionStorage(browser, {test: "on-window-close"});
yield closeWindow(win);
let [{tabs: [_, {storage}]}] = JSON.parse(ss.getClosedWindowData());
is(storage["http://example.com"].test, "on-window-close",
"sessionStorage data has been flushed when closing a window");
});
/**
* This test ensures that stale tab data is ignored when reusing a tab
* (via e.g. setTabState) and does not overwrite the new data.
*/
add_task(function flush_on_settabstate() {
let tab = yield createTabWithStorageData(["http://example.com"]);
let browser = tab.linkedBrowser;
// Flush to make sure our tab state is up-to-date.
SyncHandlers.get(browser).flush();
let state = ss.getTabState(tab);
yield modifySessionStorage(browser, {test: "on-set-tab-state"});
// Flush all data contained in the content script but send it using
// asynchronous messages.
SyncHandlers.get(browser).flushAsync();
ss.setTabState(tab, state);
yield promiseTabRestored(tab);
let {storage} = JSON.parse(ss.getTabState(tab));
is(storage["http://example.com"].test, INITIAL_VALUE,
"sessionStorage data has not been overwritten");
gBrowser.removeTab(tab);
});
/**
* This test ensures that we won't lose tab data that has been sent
* asynchronously just before closing a tab. Flushing must re-send all data
* that hasn't been received by chrome, yet.
*/
add_task(function flush_on_tabclose_racy() {
let tab = yield createTabWithStorageData(["http://example.com"]);
let browser = tab.linkedBrowser;
// Flush to make sure we start with an empty queue.
SyncHandlers.get(browser).flush();
yield modifySessionStorage(browser, {test: "on-tab-close-racy"});
// Flush all data contained in the content script but send it using
// asynchronous messages.
SyncHandlers.get(browser).flushAsync();
gBrowser.removeTab(tab);
let [{state: {storage}}] = JSON.parse(ss.getClosedTabData(window));
is(storage["http://example.com"].test, "on-tab-close-racy",
"sessionStorage data has been merged correctly to prevent data loss");
});
function promiseNewWindow() {
let deferred = Promise.defer();
whenNewWindowLoaded({private: false}, function (win) {
win.messageManager.loadFrameScript(FRAME_SCRIPT, true);
deferred.resolve(win);
});
return deferred.promise;
}
function closeWindow(win) {
let deferred = Promise.defer();
let outerID = win.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils)
.outerWindowID;
Services.obs.addObserver(function obs(subject, topic) {
let id = subject.QueryInterface(Ci.nsISupportsPRUint64).data;
if (id == outerID) {
Services.obs.removeObserver(obs, topic);
deferred.resolve();
}
}, "outer-window-destroyed", false);
win.close();
return deferred.promise;
}
function createTabWithStorageData(urls, win = window) {
return Task.spawn(function task() {
let tab = win.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 sendQuitApplicationRequested() {
let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"]
.createInstance(Ci.nsISupportsPRBool);
Services.obs.notifyObservers(cancelQuit, "quit-application-requested", null);
}
function modifySessionStorage(browser, data) {
browser.messageManager.sendAsyncMessage("ss-test:modifySessionStorage", data);
return waitForStorageEvent(browser);
}

View File

@ -1,22 +1,17 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
function test() {
TestRunner.run();
}
"use strict";
/**
* This test ensures that disabling features by flipping nsIDocShell.allow*
* These tests ensures that disabling features by flipping nsIDocShell.allow*
* properties are (re)stored as disabled. Disallowed features must be
* re-enabled when the tab is re-used by another tab restoration.
*/
function runTests() {
// Create a tab that we're going to use for our tests.
let tab = gBrowser.selectedTab = gBrowser.addTab("about:mozilla");
add_task(function docshell_capabilities() {
let tab = yield createTab();
let browser = tab.linkedBrowser;
let docShell = browser.docShell;
yield waitForLoad(browser);
// Get the list of capabilities for docShells.
let flags = Object.keys(docShell).filter(k => k.startsWith("allow"));
@ -31,9 +26,12 @@ function runTests() {
docShell.allowMetaRedirects = false;
// Now reload the document to ensure that these capabilities
// are taken into account
// are taken into account.
browser.reload();
yield whenBrowserLoaded(browser);
yield promiseBrowserLoaded(browser);
// Flush to make sure chrome received all data.
SyncHandlers.get(browser).flush();
// Check that we correctly save disallowed features.
let disallowedState = JSON.parse(ss.getTabState(tab));
@ -44,7 +42,10 @@ function runTests() {
// Reuse the tab to restore a new, clean state into it.
ss.setTabState(tab, JSON.stringify({ entries: [{url: "about:robots"}] }));
yield waitForLoad(browser);
yield promiseTabRestored(tab);
// Flush to make sure chrome received all data.
SyncHandlers.get(browser).flush();
// After restoring disallowed features must be available again.
state = JSON.parse(ss.getTabState(tab));
@ -53,7 +54,7 @@ function runTests() {
// Restore the state with disallowed features.
ss.setTabState(tab, JSON.stringify(disallowedState));
yield waitForLoad(browser);
yield promiseTabRestored(tab);
// Check that docShell flags are set.
ok(!docShell.allowImages, "images not allowed");
@ -68,11 +69,10 @@ function runTests() {
// Clean up after ourselves.
gBrowser.removeTab(tab);
}
});
function waitForLoad(aElement) {
aElement.addEventListener("load", function onLoad() {
aElement.removeEventListener("load", onLoad, true);
executeSoon(next);
}, true);
function createTab() {
let tab = gBrowser.addTab("about:mozilla");
let browser = tab.linkedBrowser;
return promiseBrowserLoaded(browser).then(() => tab);
}

View File

@ -0,0 +1,88 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const URL = getRootDirectory(gTestPath) + "browser_pageStyle_sample.html";
const URL_NESTED = getRootDirectory(gTestPath) + "browser_pageStyle_sample_nested.html";
/**
* This test ensures that page style information is correctly persisted.
*/
add_task(function page_style() {
let tab = gBrowser.addTab(URL);
let browser = tab.linkedBrowser;
yield promiseBrowserLoaded(browser);
let sheets = yield getStyleSheets(browser);
// Enable all style sheets one by one.
for (let [title, disabled] of sheets) {
yield enableStyleSheetsForSet(browser, title);
let tab2 = gBrowser.duplicateTab(tab);
yield promiseTabRestored(tab2);
let sheets = yield getStyleSheets(tab2.linkedBrowser);
let enabled = sheets.filter(([title, disabled]) => !disabled);
if (title.startsWith("fail_")) {
ok(!enabled.length, "didn't restore " + title);
} else {
ok(enabled.length == 1 && enabled[0][0] == title, "restored " + title);
}
gBrowser.removeTab(tab2);
}
// Disable all styles and verify that this is correctly persisted.
yield setAuthorStyleDisabled(browser, true);
let tab2 = gBrowser.duplicateTab(tab);
yield promiseTabRestored(tab2);
let authorStyleDisabled = yield getAuthorStyleDisabled(tab2.linkedBrowser);
ok(authorStyleDisabled, "disabled all stylesheets");
// Clean up.
gBrowser.removeTab(tab);
gBrowser.removeTab(tab2);
});
/**
* This test ensures that page style notification from nested documents are
* received and the page style is persisted correctly.
*/
add_task(function nested_page_style() {
let tab = gBrowser.addTab(URL_NESTED);
let browser = tab.linkedBrowser;
yield promiseBrowserLoaded(browser);
yield enableSubDocumentStyleSheetsForSet(browser, "alternate");
gBrowser.removeTab(tab);
let [{state: {pageStyle}}] = JSON.parse(ss.getClosedTabData(window));
is(pageStyle, "alternate", "correct pageStyle persisted");
});
function getStyleSheets(browser) {
return sendMessage(browser, "ss-test:getStyleSheets").then(({data}) => data);
}
function enableStyleSheetsForSet(browser, name) {
return sendMessage(browser, "ss-test:enableStyleSheetsForSet", name);
}
function enableSubDocumentStyleSheetsForSet(browser, name) {
return sendMessage(browser, "ss-test:enableSubDocumentStyleSheetsForSet", {
id: "iframe", set: name
});
}
function getAuthorStyleDisabled(browser) {
return sendMessage(browser, "ss-test:getAuthorStyleDisabled")
.then(({data}) => data);
}
function setAuthorStyleDisabled(browser, val) {
return sendMessage(browser, "ss-test:setAuthorStyleDisabled", val)
}

View File

@ -0,0 +1,19 @@
<html>
<head>
<meta charset="utf-8">
<title>pageStyle sample</title>
<link href="404.css" title="default" rel="stylesheet">
<link href="404.css" title="alternate" rel="alternate stylesheet">
<link href="404.css" title="altERnate" rel=" styLEsheet altERnate ">
<link href="404.css" title="media_empty" rel="alternate stylesheet" media="">
<link href="404.css" title="media_all" rel="alternate stylesheet" media="all">
<link href="404.css" title="media_ALL" rel="alternate stylesheet" media=" ALL ">
<link href="404.css" title="media_screen" rel="alternate stylesheet" media="screen">
<link href="404.css" title="media_print_screen" rel="alternate stylesheet" media="print,screen">
<link href="404.css" title="fail_media_print" rel="alternate stylesheet" media="print">
<link href="404.css" title="fail_media_projection" rel="stylesheet" media="projection">
<link href="404.css" title="fail_media_invalid" rel="alternate stylesheet" media="hallo">
</head>
<body></body>
</html>

View File

@ -0,0 +1,9 @@
<html>
<head>
<meta charset="utf-8">
<title>pageStyle sample (nested)</title>
</head>
<body>
<iframe id="iframe" src="browser_pageStyle_sample.html"/>
</body>
</html>

View File

@ -1,78 +1,194 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
let Scope = {};
Cu.import("resource://gre/modules/Task.jsm", Scope);
Cu.import("resource://gre/modules/Promise.jsm", Scope);
let {Task, Promise} = Scope;
"use strict";
let tmp = {};
Cu.import("resource://gre/modules/Promise.jsm", tmp);
Cu.import("resource://gre/modules/ForgetAboutSite.jsm", tmp);
let {Promise, ForgetAboutSite} = tmp;
function waitForStorageChange(aTab) {
let deferred = Promise.defer();
waitForContentMessage(aTab.linkedBrowser,
"SessionStore:MozStorageChanged",
1000,
deferred.resolve);
return deferred.promise;
}
const INITIAL_VALUE = "initial-value-" + Date.now();
function test() {
/**
* 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 browser = tab.linkedBrowser;
waitForExplicitFinish();
// Flush to make sure chrome received all data.
SyncHandlers.get(browser).flush();
let tab;
Task.spawn(function() {
try {
let SESSION_STORAGE_KEY = "SESSION_STORAGE_KEY " + Math.random();
let SESSION_STORAGE_VALUE = "SESSION_STORAGE_VALUE " + Math.random();
let LOCAL_STORAGE_KEY = "LOCAL_STORAGE_KEY " + Math.random();
let LOCAL_STORAGE_VALUE = "LOCAL_STORAGE_VALUE " + Math.random();
let {storage} = JSON.parse(ss.getTabState(tab));
is(storage["http://example.com"].test, INITIAL_VALUE,
"sessionStorage data for example.com has been serialized correctly");
is(storage["http://mochi.test:8888"].test, INITIAL_VALUE,
"sessionStorage data for mochi.test has been serialized correctly");
tab = gBrowser.addTab("http://example.com");
// about:home supports sessionStorage and localStorage
// Ensure that modifying sessionStore values works.
yield modifySessionStorage(browser, {test: "modified"});
SyncHandlers.get(browser).flush();
let win = tab.linkedBrowser.contentWindow;
let {storage} = JSON.parse(ss.getTabState(tab));
is(storage["http://example.com"].test, INITIAL_VALUE,
"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");
// Flush loading and next save, call getBrowserState()
// a few times to ensure that everything is cached.
yield promiseBrowserLoaded(tab.linkedBrowser);
yield forceSaveState();
info("Calling getBrowserState() to populate cache");
ss.getBrowserState();
// Test that duplicating a tab works.
let tab2 = gBrowser.duplicateTab(tab);
let browser2 = tab2.linkedBrowser;
yield promiseTabRestored(tab2);
info("Change sessionStorage, ensure that state is saved");
let storageChangedPromise = waitForStorageChange(tab);
win.sessionStorage[SESSION_STORAGE_KEY] = SESSION_STORAGE_VALUE;
let storageChanged = yield storageChangedPromise;
ok(storageChanged, "Changing sessionStorage triggered the right message");
yield forceSaveState();
// Flush to make sure chrome received all data.
SyncHandlers.get(browser2).flush();
let state = ss.getBrowserState();
ok(state.indexOf(SESSION_STORAGE_KEY) != -1, "Key appears in state");
ok(state.indexOf(SESSION_STORAGE_VALUE) != -1, "Value appears in state");
let {storage} = JSON.parse(ss.getTabState(tab2));
is(storage["http://example.com"].test, INITIAL_VALUE,
"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"});
SyncHandlers.get(browser2).flush();
info("Change localStorage, ensure that state is not saved");
storageChangedPromise = waitForStorageChange(tab);
win.localStorage[LOCAL_STORAGE_KEY] = LOCAL_STORAGE_VALUE;
storageChanged = yield storageChangedPromise;
ok(!storageChanged, "Changing localStorage did not trigger a message");
yield forceSaveState();
let {storage} = JSON.parse(ss.getTabState(tab2));
is(storage["http://example.com"].test, INITIAL_VALUE,
"sessionStorage data for example.com has been duplicated correctly");
is(storage["http://mochi.test:8888"].test, "modified2",
"sessionStorage data for mochi.test has been duplicated correctly");
state = ss.getBrowserState();
ok(state.indexOf(LOCAL_STORAGE_KEY) == -1, "Key does not appear in state");
ok(state.indexOf(LOCAL_STORAGE_VALUE) == -1, "Value does not appear in state");
} catch (ex) {
ok(false, ex);
info(ex.stack);
} finally {
// clean up
if (tab) {
gBrowser.removeTab(tab);
}
// Clean up.
gBrowser.removeTab(tab);
gBrowser.removeTab(tab2);
});
executeSoon(finish);
/**
* This test ensures that purging domain data also purges data from the
* sessionStorage data collected for tabs.
*/
add_task(function purge_domain() {
let tab = yield createTabWithStorageData(["http://example.com", "http://mochi.test:8888"]);
let browser = tab.linkedBrowser;
ForgetAboutSite.removeDataFromDomain("mochi.test");
yield waitForUpdateMessage(browser);
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,
"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");
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"]);
gBrowser.removeTab(tab);
let [{state: {storage}}] = JSON.parse(ss.getClosedTabData(window));
is(storage["http://example.com"].test, INITIAL_VALUE,
"http sessionStorage data has been saved");
is(storage["https://example.com"].test, INITIAL_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"]);
gBrowser.removeTab(tab);
let [{state: {storage}}] = JSON.parse(ss.getClosedTabData(window));
is(storage["http://example.com"].test, INITIAL_VALUE,
"http sessionStorage data has been saved");
ok(!storage["https://example.com"],
"https sessionStorage data has *not* been saved");
// Disable saving data for any site.
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 tab2 = gBrowser.duplicateTab(tab);
yield promiseBrowserLoaded(tab2.linkedBrowser);
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");
// Restore the default privacy level and close the duplicated tab.
Services.prefs.clearUserPref("browser.sessionstore.privacy_level");
gBrowser.removeTab(tab2);
// 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,
"http sessionStorage data has been saved");
is(storage["https://example.com"].test, INITIAL_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) {
browser.messageManager.sendAsyncMessage("ss-test:notifyObservers", topic);
return waitForUpdateMessage(browser);
}

View File

@ -0,0 +1,52 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* This frame script is only loaded for sessionstore mochitests. It enables us
* to modify and query docShell data when running with multiple processes.
*/
addEventListener("MozStorageChanged", function () {
sendSyncMessage("ss-test:MozStorageChanged");
});
addMessageListener("ss-test:modifySessionStorage", function (msg) {
for (let key of Object.keys(msg.data)) {
content.sessionStorage[key] = msg.data[key];
}
});
addMessageListener("ss-test:notifyObservers", function (msg) {
Services.obs.notifyObservers(null, msg.data, "");
});
addMessageListener("ss-test:getStyleSheets", function (msg) {
let sheets = content.document.styleSheets;
let titles = Array.map(sheets, ss => [ss.title, ss.disabled]);
sendSyncMessage("ss-test:getStyleSheets", titles);
});
addMessageListener("ss-test:enableStyleSheetsForSet", function (msg) {
content.document.enableStyleSheetsForSet(msg.data);
sendSyncMessage("ss-test:enableStyleSheetsForSet");
});
addMessageListener("ss-test:enableSubDocumentStyleSheetsForSet", function (msg) {
let iframe = content.document.getElementById(msg.data.id);
iframe.contentDocument.enableStyleSheetsForSet(msg.data.set);
sendSyncMessage("ss-test:enableSubDocumentStyleSheetsForSet");
});
addMessageListener("ss-test:getAuthorStyleDisabled", function (msg) {
let {authorStyleDisabled} =
docShell.contentViewer.QueryInterface(Ci.nsIMarkupDocumentViewer);
sendSyncMessage("ss-test:getAuthorStyleDisabled", authorStyleDisabled);
});
addMessageListener("ss-test:setAuthorStyleDisabled", function (msg) {
let markupDocumentViewer =
docShell.contentViewer.QueryInterface(Ci.nsIMarkupDocumentViewer);
markupDocumentViewer.authorStyleDisabled = msg.data;
sendSyncMessage("ss-test:setAuthorStyleDisabled");
});

View File

@ -5,9 +5,33 @@
const TAB_STATE_NEEDS_RESTORE = 1;
const TAB_STATE_RESTORING = 2;
const FRAME_SCRIPT = "chrome://mochitests/content/browser/browser/components/" +
"sessionstore/test/content.js";
let mm = Cc["@mozilla.org/globalmessagemanager;1"]
.getService(Ci.nsIMessageListenerManager);
mm.loadFrameScript(FRAME_SCRIPT, true);
mm.addMessageListener("SessionStore:setupSyncHandler", onSetupSyncHandler);
/**
* This keeps track of all SyncHandlers passed to chrome from frame scripts.
* We need this to let tests communicate with frame scripts and cause (a)sync
* flushes.
*/
let SyncHandlers = new WeakMap();
function onSetupSyncHandler(msg) {
SyncHandlers.set(msg.target, msg.objects.handler);
}
registerCleanupFunction(() => {
mm.removeDelayedFrameScript(FRAME_SCRIPT);
mm.removeMessageListener("SessionStore:setupSyncHandler", onSetupSyncHandler);
});
let tmp = {};
Cu.import("resource://gre/modules/Promise.jsm", tmp);
Cu.import("resource:///modules/sessionstore/SessionStore.jsm", tmp);
let SessionStore = tmp.SessionStore;
let {Promise, SessionStore} = tmp;
let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
@ -159,36 +183,32 @@ function waitForTabState(aTab, aState, aCallback) {
/**
* Wait for a content -> chrome message.
*/
function waitForContentMessage(aBrowser, aTopic, aTimeout, aCallback) {
let mm = aBrowser.messageManager;
let observing = false;
function removeObserver() {
if (!observing)
return;
mm.removeMessageListener(aTopic, observer);
observing = false;
function promiseContentMessage(browser, name, timeout = 1000) {
let deferred = Promise.defer();
let mm = browser.messageManager;
function removeListener() {
mm.removeMessageListener(name, listener);
}
let timeout = setTimeout(function () {
removeObserver();
aCallback(false);
}, aTimeout);
removeListener();
deferred.resolve(false);
}, timeout);
function observer(aSubject, aTopic, aData) {
removeObserver();
timeout = clearTimeout(timeout);
executeSoon(() => aCallback(true));
function listener(msg) {
removeListener();
clearTimeout(timeout);
deferred.resolve(msg);
}
registerCleanupFunction(function() {
removeObserver();
if (timeout) {
clearTimeout(timeout);
}
removeListener();
clearTimeout(timeout);
});
observing = true;
mm.addMessageListener(aTopic, observer);
mm.addMessageListener(name, listener);
return deferred.promise;
}
function waitForTopic(aTopic, aTimeout, aCallback) {
@ -456,3 +476,19 @@ let TestRunner = {
function next() {
TestRunner.next();
}
function promiseTabRestored(tab) {
let deferred = Promise.defer();
tab.addEventListener("SSTabRestored", function onRestored() {
tab.removeEventListener("SSTabRestored", onRestored);
deferred.resolve();
});
return deferred.promise;
}
function sendMessage(browser, name, data = {}) {
browser.messageManager.sendAsyncMessage(name, data);
return promiseContentMessage(browser, name);
}