Bug 867143 - Adapt testsuite to cached session restore. r=ttaubert

This commit is contained in:
David Rajchenbach-Teller 2013-07-26 12:15:25 -04:00
parent 2b83439124
commit 327b5116d8
5 changed files with 216 additions and 71 deletions

View File

@ -27,6 +27,7 @@ MOCHITEST_BROWSER_FILES = \
browser_input.js \
browser_input_sample.html \
browser_pageshow.js \
browser_sessionStorage.js \
browser_upgrade_backup.js \
browser_windowRestore_perwindowpb.js \
browser_248970_b_perwindowpb.js \

View File

@ -2,13 +2,16 @@
* 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/. */
let Scope = {};
Cu.import("resource://gre/modules/Task.jsm", Scope);
Cu.import("resource://gre/modules/Promise.jsm", Scope);
let {Task, Promise} = Scope;
// This tests that a tab which is closed while loading is not lost.
// Specifically, that session store does not rely on an invalid cache when
// constructing data for a tab which is loading.
// The newly created tab which we load a URL into and try closing/undoing.
let tab;
// This test steps through the following parts:
// 1. Tab has been created is loading URI_TO_LOAD.
// 2. Before URI_TO_LOAD finishes loading, browser.currentURI has changed and
@ -17,69 +20,67 @@ let tab;
// should fully load.
const URI_TO_LOAD = "about:mozilla";
function waitForLoadStarted(aTab) {
let deferred = Promise.defer();
waitForContentMessage(aTab.linkedBrowser,
"SessionStore:loadStart",
1000,
deferred.resolve);
return deferred.promise;
}
function waitForTabLoaded(aTab) {
let deferred = Promise.defer();
whenBrowserLoaded(aTab.linkedBrowser, deferred.resolve);
return deferred.promise;
}
function waitForTabClosed() {
let deferred = Promise.defer();
let observer = function() {
gBrowser.tabContainer.removeEventListener("TabClose", observer, true);
deferred.resolve();
};
gBrowser.tabContainer.addEventListener("TabClose", observer, true);
return deferred.promise;
}
function test() {
waitForExplicitFinish();
gBrowser.addTabsProgressListener(tabsListener);
Task.spawn(function() {
try {
// Open a new tab
let tab = gBrowser.addTab("about:blank");
yield waitForTabLoaded(tab);
tab = gBrowser.addTab();
// Trigger a save state, to initialize any caches
ss.getBrowserState();
tab.linkedBrowser.addEventListener("load", firstOnLoad, true);
is(gBrowser.tabs[1], tab, "newly created tab should exist by now");
ok(tab.linkedBrowser.__SS_data, "newly created tab should be in save state");
gBrowser.tabContainer.addEventListener("TabClose", onTabClose, true);
}
// Start a load and interrupt it by closing the tab
tab.linkedBrowser.loadURI(URI_TO_LOAD);
let loaded = yield waitForLoadStarted(tab);
ok(loaded, "Load started");
function firstOnLoad(aEvent) {
tab.linkedBrowser.removeEventListener("load", firstOnLoad, true);
let uri = aEvent.target.location;
is(uri, "about:blank", "first load should be for about:blank");
// Trigger a save state.
ss.getBrowserState();
is(gBrowser.tabs[1], tab, "newly created tab should exist by now");
ok(tab.linkedBrowser.__SS_data, "newly created tab should be in save state");
tab.linkedBrowser.loadURI(URI_TO_LOAD);
}
let tabsListener = {
onLocationChange: function onLocationChange(aBrowser) {
gBrowser.removeTabsProgressListener(tabsListener);
is(aBrowser.currentURI.spec, URI_TO_LOAD,
"should occur after about:blank load and be loading next page");
// Since we are running in the context of tabs listeners, we do not
// want to disrupt other tabs listeners.
executeSoon(function() {
let tabClosing = waitForTabClosed();
gBrowser.removeTab(tab);
});
}
};
info("Now waiting for TabClose to close");
yield tabClosing;
function onTabClose(aEvent) {
gBrowser.tabContainer.removeEventListener("TabClose", onTabClose, true);
// Undo the tab, ensure that it proceeds with loading
tab = ss.undoCloseTab(window, 0);
yield waitForTabLoaded(tab);
is(tab.linkedBrowser.currentURI.spec, URI_TO_LOAD, "loading proceeded as expected");
is(tab.linkedBrowser.currentURI.spec, URI_TO_LOAD,
"should only remove when loading page");
gBrowser.removeTab(tab);
executeSoon(function() {
tab = ss.undoCloseTab(window, 0);
tab.linkedBrowser.addEventListener("load", secondOnLoad, true);
executeSoon(finish);
} catch (ex) {
ok(false, ex);
info(ex.stack);
}
});
}
function secondOnLoad(aEvent) {
let uri = aEvent.target.location;
is(uri, URI_TO_LOAD, "should load page from undoCloseTab");
done();
}
function done() {
tab.linkedBrowser.removeEventListener("load", secondOnLoad, true);
gBrowser.removeTab(tab);
executeSoon(finish);
}

View File

@ -30,6 +30,11 @@ function runTests() {
docShell.allowImages = false;
docShell.allowMetaRedirects = false;
// Now reload the document to ensure that these capabilities
// are taken into account
browser.reload();
yield whenBrowserLoaded(browser);
// Check that we correctly save disallowed features.
let disallowedState = JSON.parse(ss.getTabState(tab));
let disallow = new Set(disallowedState.disallow.split(","));
@ -52,7 +57,7 @@ function runTests() {
// Check that docShell flags are set.
ok(!docShell.allowImages, "images not allowed");
ok(!docShell.allowMetaRedirects, "meta redirects not allowed")
ok(!docShell.allowMetaRedirects, "meta redirects not allowed");
// Check that we correctly restored features as disabled.
state = JSON.parse(ss.getTabState(tab));

View File

@ -0,0 +1,91 @@
/* 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/. */
let Scope = {};
Cu.import("resource://gre/modules/Task.jsm", Scope);
Cu.import("resource://gre/modules/Promise.jsm", Scope);
let {Task, Promise} = Scope;
function promiseBrowserLoaded(aBrowser) {
let deferred = Promise.defer();
whenBrowserLoaded(aBrowser, () => deferred.resolve());
return deferred.promise;
}
function forceWriteState() {
let deferred = Promise.defer();
const PREF = "browser.sessionstore.interval";
const TOPIC = "sessionstore-state-write";
Services.obs.addObserver(function observe() {
Services.obs.removeObserver(observe, TOPIC);
Services.prefs.clearUserPref(PREF);
deferred.resolve();
}, TOPIC, false);
Services.prefs.setIntPref(PREF, 0);
return deferred.promise;
}
function waitForStorageChange(aTab) {
let deferred = Promise.defer();
waitForContentMessage(aTab.linkedBrowser,
"SessionStore:MozStorageChanged",
1000,
deferred.resolve);
return deferred.promise;
}
function test() {
waitForExplicitFinish();
let tab;
Task.spawn(function() {
try {
tab = gBrowser.addTab("http://example.com");
// about:home supports sessionStorage and localStorage
let win = tab.linkedBrowser.contentWindow;
// Flush loading and next save, call getBrowserState()
// a few times to ensure that everything is cached.
yield promiseBrowserLoaded(tab.linkedBrowser);
yield forceWriteState();
info("Calling getBrowserState() to populate cache");
ss.getBrowserState();
info("Change sessionStorage, ensure that state is saved");
win.sessionStorage["SESSION_STORAGE_KEY"] = "SESSION_STORAGE_VALUE";
let storageChanged = yield waitForStorageChange(tab);
ok(storageChanged, "Changing sessionStorage triggered the right message");
yield forceWriteState();
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");
info("Change localStorage, ensure that state is not saved");
win.localStorage["LOCAL_STORAGE_KEY"] = "LOCAL_STORAGE_VALUE";
storageChanged = yield waitForStorageChange(tab);
ok(!storageChanged, "Changing localStorage did not trigger a message");
yield forceWriteState();
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);
}
executeSoon(finish);
}
});
}

View File

@ -156,31 +156,28 @@ function waitForTabState(aTab, aState, aCallback) {
ss.setTabState(aTab, JSON.stringify(aState));
}
// waitForSaveState waits for a state write but not necessarily for the state to
// turn dirty.
function waitForSaveState(aSaveStateCallback) {
/**
* Wait for a content -> chrome message.
*/
function waitForContentMessage(aBrowser, aTopic, aTimeout, aCallback) {
let mm = aBrowser.messageManager;
let observing = false;
let topic = "sessionstore-state-write";
let sessionSaveTimeout = 1000 +
Services.prefs.getIntPref("browser.sessionstore.interval");
function removeObserver() {
if (!observing)
return;
Services.obs.removeObserver(observer, topic);
mm.removeMessageListener(aTopic, observer);
observing = false;
}
let timeout = setTimeout(function () {
removeObserver();
aSaveStateCallback();
}, sessionSaveTimeout);
aCallback(false);
}, aTimeout);
function observer(aSubject, aTopic, aData) {
removeObserver();
timeout = clearTimeout(timeout);
executeSoon(aSaveStateCallback);
executeSoon(() => aCallback(true));
}
registerCleanupFunction(function() {
@ -191,8 +188,58 @@ function waitForSaveState(aSaveStateCallback) {
});
observing = true;
Services.obs.addObserver(observer, topic, false);
};
mm.addMessageListener(aTopic, observer);
}
function waitForTopic(aTopic, aTimeout, aCallback) {
let observing = false;
function removeObserver() {
if (!observing)
return;
Services.obs.removeObserver(observer, aTopic);
observing = false;
}
let timeout = setTimeout(function () {
removeObserver();
aCallback(false);
}, aTimeout);
function observer(aSubject, aTopic, aData) {
removeObserver();
timeout = clearTimeout(timeout);
executeSoon(() => aCallback(true));
}
registerCleanupFunction(function() {
removeObserver();
if (timeout) {
clearTimeout(timeout);
}
});
observing = true;
Services.obs.addObserver(observer, aTopic, false);
}
/**
* Wait until session restore has finished collecting its data and is
* getting ready to write that data ("sessionstore-state-write").
*
* This function is meant to be called immediately after the code
* that will trigger the saving.
*
* Note that this does not wait for the disk write to be complete.
*
* @param {function} aCallback If sessionstore-state-write is sent
* within buffering interval + 100 ms, the callback is passed |true|,
* otherwise, it is passed |false|.
*/
function waitForSaveState(aCallback) {
let timeout = 100 +
Services.prefs.getIntPref("browser.sessionstore.interval");
return waitForTopic("sessionstore-state-write", timeout, aCallback);
}
function whenBrowserLoaded(aBrowser, aCallback = next) {
aBrowser.addEventListener("load", function onLoad() {