Bug 944557 - Remove sessionstore-state-write. r=ttaubert

This commit is contained in:
David Rajchenbach-Teller 2014-03-27 11:38:01 -04:00
parent e5f3b4f65a
commit 213d2f3a40
9 changed files with 106 additions and 262 deletions

View File

@ -43,14 +43,6 @@ XPCOMUtils.defineLazyGetter(this, "gInterval", function () {
return Services.prefs.getIntPref(PREF);
});
// Wrap a string as a nsISupports.
function createSupportsString(data) {
let string = Cc["@mozilla.org/supports-string;1"]
.createInstance(Ci.nsISupportsString);
string.data = data;
return string;
}
// Notify observers about a given topic with a given subject.
function notify(subject, topic) {
Services.obs.notifyObservers(subject, topic, "");
@ -252,19 +244,13 @@ let SessionSaverInternal = {
* Write the given state object to disk.
*/
_writeState: function (state) {
// Inform observers
notify(null, "sessionstore-state-write");
stopWatchStart("SERIALIZE_DATA_MS", "SERIALIZE_DATA_LONGEST_OP_MS", "WRITE_STATE_LONGEST_OP_MS");
let data = JSON.stringify(state);
stopWatchFinish("SERIALIZE_DATA_MS", "SERIALIZE_DATA_LONGEST_OP_MS");
// Give observers a chance to modify session data.
data = this._notifyObserversBeforeStateWrite(data);
// Don't touch the file if an observer has deleted all state data.
if (!data) {
stopWatchCancel("WRITE_STATE_LONGEST_OP_MS");
return Promise.resolve();
}
// We update the time stamp before writing so that we don't write again
// too soon, if saving is requested before the write completes. Without
// this update we may save repeatedly if actions cause a runDelayed
@ -285,14 +271,4 @@ let SessionSaverInternal = {
return promise;
},
/**
* Notify sessionstore-state-write observer and give them a
* chance to modify session data before we'll write it to disk.
*/
_notifyObserversBeforeStateWrite: function (data) {
let stateString = createSupportsString(data);
notify(stateString, "sessionstore-state-write");
return stateString.data;
}
};

View File

@ -98,7 +98,6 @@ skip-if = true
[browser_394759_purge.js]
[browser_423132.js]
[browser_447951.js]
[browser_448741.js]
[browser_454908.js]
[browser_456342.js]
[browser_461634.js]

View File

@ -75,15 +75,13 @@ function test() {
// Wait for the sessionstore.js file to be written before going on.
// Note: we don't wait for the complete event, since if asyncCopy fails we
// would timeout.
Services.obs.addObserver(function (aSubject, aTopic, aData) {
Services.obs.removeObserver(arguments.callee, aTopic);
info("sessionstore.js is being written");
waitForSaveState(function(writing) {
ok(writing, "sessionstore.js is being written");
closedWindowCount = ss.getClosedWindowCount();
is(closedWindowCount, 0, "Correctly set window count");
executeSoon(aCallback);
}, "sessionstore-state-write", false);
});
// Remove the sessionstore.js file before setting the interval to 0
let profilePath = Services.dirsvc.get("ProfD", Ci.nsIFile);

View File

@ -1,62 +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 448741 **/
waitForExplicitFinish();
let uniqueName = "bug 448741";
let uniqueValue = "as good as unique: " + Date.now();
// set a unique value on a new, blank tab
var tab = gBrowser.addTab();
tab.linkedBrowser.stop();
ss.setTabValue(tab, uniqueName, uniqueValue);
let valueWasCleaned = false;
// prevent our value from being written to disk
function cleaningObserver(aSubject, aTopic, aData) {
ok(aTopic == "sessionstore-state-write", "observed correct topic?");
ok(aSubject instanceof Ci.nsISupportsString, "subject is a string?");
ok(aSubject.data.indexOf(uniqueValue) > -1, "data contains our value?");
// find the data for the newly added tab and delete it
let state = JSON.parse(aSubject.data);
state.windows.forEach(function (winData) {
winData.tabs.forEach(function (tabData) {
if (tabData.extData && uniqueName in tabData.extData &&
tabData.extData[uniqueName] == uniqueValue) {
delete tabData.extData[uniqueName];
valueWasCleaned = true;
}
});
});
ok(valueWasCleaned, "found and removed the specific tab value");
aSubject.data = JSON.stringify(state);
Services.obs.removeObserver(cleaningObserver, aTopic);
}
// make sure that all later observers don't see that value any longer
function checkingObserver(aSubject, aTopic, aData) {
ok(valueWasCleaned && aSubject instanceof Ci.nsISupportsString,
"ready to check the cleaned state?");
ok(aSubject.data.indexOf(uniqueValue) == -1, "data no longer contains our value?");
// clean up
gBrowser.removeTab(tab);
Services.obs.removeObserver(checkingObserver, aTopic);
if (gPrefService.prefHasUserValue("browser.sessionstore.interval"))
gPrefService.clearUserPref("browser.sessionstore.interval");
finish();
}
// last added observers are invoked first
Services.obs.addObserver(checkingObserver, "sessionstore-state-write", false);
Services.obs.addObserver(cleaningObserver, "sessionstore-state-write", false);
// trigger an immediate save operation
gPrefService.setIntPref("browser.sessionstore.interval", 0);
}

View File

@ -20,11 +20,12 @@ function testBug600545() {
Services.prefs.clearUserPref("browser.sessionstore.interval");
});
// This tests the following use case:
// When multiple windows are open and browser.sessionstore.resume_from_crash
// preference is false, tab session data for non-active window is stripped for
// non-pinned tabs. This occurs after "sessionstore-state-write" fires which
// will only fire in this case if there is at least one pinned tab.
// This tests the following use case: When multiple windows are open
// and browser.sessionstore.resume_from_crash preference is false,
// tab session data for non-active window is stripped for non-pinned
// tabs. This occurs after "sessionstore-state-write-complete"
// fires which will only fire in this case if there is at least one
// pinned tab.
let state = { windows: [
{
tabs: [

View File

@ -1,125 +1,86 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
let newWin;
let newTab;
function test() {
add_task(function* setup() {
/** Test for Bug 625016 - Restore windows closed in succession to quit (non-OSX only) **/
// We'll test this by opening a new window, waiting for the save event, then
// closing that window. We'll observe the "sessionstore-state-write" notification
// and check that the state contains no _closedWindows. We'll then add a new
// tab and make sure that the state following that was reset and the closed
// window is now in _closedWindows.
// We'll test this by opening a new window, waiting for the save
// event, then closing that window. We'll observe the
// "sessionstore-state-write-complete" notification and check that
// the state contains no _closedWindows. We'll then add a new tab
// and make sure that the state following that was reset and the
// closed window is now in _closedWindows.
waitForExplicitFinish();
requestLongerTimeout(2);
// We speed up the interval between session saves to ensure that the test
// runs quickly.
Services.prefs.setIntPref("browser.sessionstore.interval", 4000);
registerCleanupFunction(function () {
Services.prefs.clearUserPref("browser.sessionstore.interval");
});
yield forceSaveState();
waitForSaveState(setup);
}
function setup() {
// We'll clear all closed windows to make sure our state is clean
// forgetClosedWindow doesn't trigger a delayed save
while (ss.getClosedWindowCount()) {
ss.forgetClosedWindow(0);
}
is(ss.getClosedWindowCount(), 0, "starting with no closed windows");
});
// Open a new window, which should trigger a save event soon.
waitForSaveState(onSaveState);
newWin = openDialog(location, "_blank", "chrome,all,dialog=no", "about:rights");
}
function onSaveState() {
add_task(function* new_window() {
let newWin;
try {
ss.getWindowValue(newWin, "foobar");
} catch (e) {
// The window is untracked which means that the saveState() call isn't the
// one we're waiting for. It's most likely been triggered by an async
// collection running in the background.
waitForSaveState(onSaveState);
return;
newWin = yield promiseNewWindowLoaded();
let tab = newWin.gBrowser.addTab("http://example.com/browser_625016.js?" + Math.random());
yield promiseBrowserLoaded(tab.linkedBrowser);
// Double check that we have no closed windows
is(ss.getClosedWindowCount(), 0, "no closed windows on first save");
yield promiseWindowClosed(newWin);
newWin = null;
let state = JSON.parse((yield promiseSaveFileContents()));
is(state.windows.length, 2,
"observe1: 2 windows in data written to disk");
is(state._closedWindows.length, 0,
"observe1: no closed windows in data written to disk");
// The API still treats the closed window as closed, so ensure that window is there
is(ss.getClosedWindowCount(), 1,
"observe1: 1 closed window according to API");
} finally {
if (newWin) {
yield promiseWindowClosed(newWin);
}
yield forceSaveState();
}
// Double check that we have no closed windows
is(ss.getClosedWindowCount(), 0, "no closed windows on first save");
Services.obs.addObserver(observe1, "sessionstore-state-write", false);
// Now close the new window, which should trigger another save event
newWin.close();
}
function observe1(aSubject, aTopic, aData) {
info("observe1: " + aTopic);
switch (aTopic) {
case "sessionstore-state-write":
aSubject.QueryInterface(Ci.nsISupportsString);
let state = JSON.parse(aSubject.data);
is(state.windows.length, 2,
"observe1: 2 windows in data being writted to disk");
is(state._closedWindows.length, 0,
"observe1: no closed windows in data being writted to disk");
// The API still treats the closed window as closed, so ensure that window is there
is(ss.getClosedWindowCount(), 1,
"observe1: 1 closed window according to API");
Services.obs.removeObserver(observe1, "sessionstore-state-write");
Services.obs.addObserver(observe1, "sessionstore-state-write-complete", false);
break;
case "sessionstore-state-write-complete":
Services.obs.removeObserver(observe1, "sessionstore-state-write-complete");
openTab();
break;
}
}
function observe2(aSubject, aTopic, aData) {
info("observe2: " + aTopic);
switch (aTopic) {
case "sessionstore-state-write":
aSubject.QueryInterface(Ci.nsISupportsString);
let state = JSON.parse(aSubject.data);
is(state.windows.length, 1,
"observe2: 1 window in data being writted to disk");
is(state._closedWindows.length, 1,
"observe2: 1 closed window in data being writted to disk");
// The API still treats the closed window as closed, so ensure that window is there
is(ss.getClosedWindowCount(), 1,
"observe2: 1 closed window according to API");
Services.obs.removeObserver(observe2, "sessionstore-state-write");
Services.obs.addObserver(observe2, "sessionstore-state-write-complete", false);
break;
case "sessionstore-state-write-complete":
Services.obs.removeObserver(observe2, "sessionstore-state-write-complete");
done();
break;
}
}
});
// We'll open a tab, which should trigger another state save which would wipe
// the _shouldRestore attribute from the closed window
function openTab() {
Services.obs.addObserver(observe2, "sessionstore-state-write", false);
newTab = gBrowser.addTab("about:mozilla");
}
add_task(function* new_tab() {
let newTab;
try {
newTab = gBrowser.addTab("about:mozilla");
function done() {
gBrowser.removeTab(newTab);
let state = JSON.parse((yield promiseSaveFileContents()));
is(state.windows.length, 1,
"observe2: 1 window in data being written to disk");
is(state._closedWindows.length, 1,
"observe2: 1 closed window in data being written to disk");
// The API still treats the closed window as closed, so ensure that window is there
is(ss.getClosedWindowCount(), 1,
"observe2: 1 closed window according to API");
} finally {
gBrowser.removeTab(newTab);
}
});
add_task(function* done() {
// The API still represents the closed window as closed, so we can clear it
// with the API, but just to make sure...
is(ss.getClosedWindowCount(), 1, "1 closed window according to API");
ss.forgetClosedWindow(0);
executeSoon(finish);
}
// is(ss.getClosedWindowCount(), 1, "1 closed window according to API");
while (ss.getClosedWindowCount()) {
ss.forgetClosedWindow(0);
}
Services.prefs.clearUserPref("browser.sessionstore.interval");
});

View File

@ -162,15 +162,9 @@ function waitForWindowClose(aWin, aCallback) {
}
function forceWriteState(aCallback) {
Services.obs.addObserver(function observe(aSubject, aTopic, aData) {
if (aTopic == "sessionstore-state-write") {
Services.obs.removeObserver(observe, aTopic);
Services.prefs.clearUserPref("browser.sessionstore.interval");
aSubject.QueryInterface(Ci.nsISupportsString);
aCallback(JSON.parse(aSubject.data));
}
}, "sessionstore-state-write", false);
Services.prefs.setIntPref("browser.sessionstore.interval", 0);
return promiseSaveFileContents().then(function(data) {
aCallback(JSON.parse(data));
});
}
function testOnWindow(aIsPrivate, aCallback) {

View File

@ -24,43 +24,9 @@ let gDecoder = new TextDecoder();
let gSSData;
let gSSBakData;
// Wait for a state write to complete and then execute a callback.
function waitForSaveStateComplete(aSaveStateCallback) {
let topic = "sessionstore-state-write-complete";
function observer() {
Services.prefs.clearUserPref(PREF_SS_INTERVAL);
Services.obs.removeObserver(observer, topic);
executeSoon(function taskCallback() {
Task.spawn(aSaveStateCallback);
});
}
Services.obs.addObserver(observer, topic, false);
}
// Register next test callback and trigger state saving change.
function nextTest(testFunc) {
waitForSaveStateComplete(testFunc);
// We set the interval for session store state saves to be zero
// to cause a save ASAP.
Services.prefs.setIntPref(PREF_SS_INTERVAL, 0);
}
registerCleanupFunction(function() {
// Cleaning up after the test: removing the sessionstore.bak file.
Task.spawn(function cleanupTask() {
yield OS.File.remove(backupPath);
});
});
function test() {
waitForExplicitFinish();
nextTest(testAfterFirstWrite);
}
function testAfterFirstWrite() {
add_task(function* testAfterFirstWrite() {
// Ensure sessionstore.bak is not created. We start with a clean
// profile so there was nothing to move to sessionstore.bak before
// initially writing sessionstore.js
@ -78,10 +44,10 @@ function testAfterFirstWrite() {
// and a backup would not be triggered again.
yield OS.File.move(path, backupPath);
nextTest(testReadBackup);
}
yield forceSaveState();
});
function testReadBackup() {
add_task(function* testReadBackup() {
// Ensure sessionstore.bak is finally created.
let ssExists = yield OS.File.exists(path);
let ssBackupExists = yield OS.File.exists(backupPath);
@ -114,10 +80,10 @@ function testReadBackup() {
is(ssDataRead, gSSBakData,
"SessionFile.read read sessionstore.bak correctly.");
nextTest(testBackupUnchanged);
}
yield forceSaveState();
});
function testBackupUnchanged() {
add_task(function* testBackupUnchanged() {
// Ensure sessionstore.bak is backed up only once.
// Read sessionstore.bak data.
@ -125,6 +91,9 @@ function testBackupUnchanged() {
let ssBakData = gDecoder.decode(array);
// Ensure the sessionstore.bak did not change.
is(ssBakData, gSSBakData, "sessionstore.bak is unchanged.");
});
executeSoon(finish);
}
add_task(function* cleanup() {
// Cleaning up after the test: removing the sessionstore.bak file.
yield OS.File.remove(backupPath);
});

View File

@ -243,12 +243,7 @@ function waitForTopic(aTopic, aTimeout, aCallback) {
/**
* 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.
* has written that data ("sessionstore-state-write-complete").
*
* @param {function} aCallback If sessionstore-state-write is sent
* within buffering interval + 100 ms, the callback is passed |true|,
@ -257,7 +252,7 @@ function waitForTopic(aTopic, aTimeout, aCallback) {
function waitForSaveState(aCallback) {
let timeout = 100 +
Services.prefs.getIntPref("browser.sessionstore.interval");
return waitForTopic("sessionstore-state-write", timeout, aCallback);
return waitForTopic("sessionstore-state-write-complete", timeout, aCallback);
}
function promiseSaveState() {
let deferred = Promise.defer();
@ -272,22 +267,30 @@ function promiseSaveState() {
function forceSaveState() {
let promise = promiseSaveState();
const PREF = "browser.sessionstore.interval";
let original = Services.prefs.getIntPref(PREF);
// Set interval to an arbitrary non-0 duration
// to ensure that setting it to 0 will notify observers
Services.prefs.setIntPref(PREF, 1000);
Services.prefs.setIntPref(PREF, 0);
return promise.then(
function onSuccess(x) {
Services.prefs.clearUserPref(PREF);
Services.prefs.setIntPref(PREF, original);
return x;
},
function onError(x) {
Services.prefs.clearUserPref(PREF);
Services.prefs.setIntPref(PREF, original);
throw x;
}
);
}
function promiseSaveFileContents() {
let promise = forceSaveState();
return promise.then(function() {
return OS.File.read(OS.Path.join(OS.Constants.Path.profileDir, "sessionstore.js"), { encoding: "utf-8" });
});
}
function whenBrowserLoaded(aBrowser, aCallback = next, ignoreSubFrames = true) {
aBrowser.addEventListener("load", function onLoad(event) {
if (!ignoreSubFrames || event.target == aBrowser.contentDocument) {
@ -321,6 +324,11 @@ function whenWindowLoaded(aWindow, aCallback = next) {
});
}, false);
}
function promiseWindowLoaded(aWindow) {
let deferred = Promise.defer();
whenWindowLoaded(aWindow, deferred.resolve);
return deferred.promise;
}
function whenTabRestored(aTab, aCallback = next) {
aTab.addEventListener("SSTabRestored", function onRestored(aEvent) {