Bug 1002843 - Wait until important parts have been initialized before restoring tabs into windows r=smacleod

This commit is contained in:
Tim Taubert 2014-05-03 08:11:43 +02:00
parent e5149d6a1e
commit 297e906969
11 changed files with 95 additions and 124 deletions

View File

@ -858,9 +858,11 @@ var gBrowserInit = {
}
}
// Certain kinds of automigration rely on this notification to complete their
// tasks BEFORE the browser window is shown.
Services.obs.notifyObservers(null, "browser-window-before-show", "");
// Certain kinds of automigration rely on this notification to complete
// their tasks BEFORE the browser window is shown. SessionStore uses it to
// restore tabs into windows AFTER important parts like gMultiProcessBrowser
// have been initialized.
Services.obs.notifyObservers(window, "browser-window-before-show", "");
// Set a sane starting width/height for all resolutions on new profiles.
if (!document.documentElement.hasAttribute("width")) {

View File

@ -30,7 +30,7 @@ const MAX_CONCURRENT_TAB_RESTORES = 3;
// global notifications observed
const OBSERVING = [
"domwindowopened", "domwindowclosed",
"browser-window-before-show", "domwindowclosed",
"quit-application-requested", "quit-application-granted",
"browser-lastwindow-close-granted",
"quit-application", "browser:purge-session-history",
@ -540,8 +540,8 @@ let SessionStoreInternal = {
*/
observe: function ssi_observe(aSubject, aTopic, aData) {
switch (aTopic) {
case "domwindowopened": // catch new windows
this.onOpen(aSubject);
case "browser-window-before-show": // catch new windows
this.onBeforeBrowserWindowShown(aSubject);
break;
case "domwindowclosed": // catch closed windows
this.onClose(aSubject);
@ -919,71 +919,59 @@ let SessionStoreInternal = {
},
/**
* On window open
* Called right before a new browser window is shown.
* @param aWindow
* Window reference
*/
onOpen: function ssi_onOpen(aWindow) {
let onload = () => {
aWindow.removeEventListener("load", onload);
onBeforeBrowserWindowShown: function (aWindow) {
// Just call onLoad() directly if we're initialized already.
if (this._sessionInitialized) {
this.onLoad(aWindow);
return;
}
let windowType = aWindow.document.documentElement.getAttribute("windowtype");
// The very first window that is opened creates a promise that is then
// re-used by all subsequent windows. The promise will be used to tell
// when we're ready for initialization.
if (!this._promiseReadyForInitialization) {
let deferred = Promise.defer();
// Ignore non-browser windows.
if (windowType != "navigator:browser") {
// Wait for the given window's delayed startup to be finished.
Services.obs.addObserver(function obs(subject, topic) {
if (aWindow == subject) {
Services.obs.removeObserver(obs, topic);
deferred.resolve();
}
}, "browser-delayed-startup-finished", false);
// We are ready for initialization as soon as the session file has been
// read from disk and the initial window's delayed startup has finished.
this._promiseReadyForInitialization =
Promise.all([deferred.promise, gSessionStartup.onceInitialized]);
}
// We can't call this.onLoad since initialization
// hasn't completed, so we'll wait until it is done.
// Even if additional windows are opened and wait
// for initialization as well, the first opened
// window should execute first, and this.onLoad
// will be called with the initialState.
this._promiseReadyForInitialization.then(() => {
if (aWindow.closed) {
return;
}
if (this._sessionInitialized) {
this.onLoad(aWindow);
return;
} else {
let initialState = this.initSession();
this._sessionInitialized = true;
this.onLoad(aWindow, initialState);
// Let everyone know we're done.
this._deferredInitialized.resolve();
}
// The very first window that is opened creates a promise that is then
// re-used by all subsequent windows. The promise will be used to tell
// when we're ready for initialization.
if (!this._promiseReadyForInitialization) {
let deferred = Promise.defer();
// Wait for the given window's delayed startup to be finished.
Services.obs.addObserver(function obs(subject, topic) {
if (aWindow == subject) {
Services.obs.removeObserver(obs, topic);
deferred.resolve();
}
}, "browser-delayed-startup-finished", false);
// We are ready for initialization as soon as the session file has been
// read from disk and the initial window's delayed startup has finished.
this._promiseReadyForInitialization =
Promise.all([deferred.promise, gSessionStartup.onceInitialized]);
}
// We can't call this.onLoad since initialization
// hasn't completed, so we'll wait until it is done.
// Even if additional windows are opened and wait
// for initialization as well, the first opened
// window should execute first, and this.onLoad
// will be called with the initialState.
this._promiseReadyForInitialization.then(() => {
if (aWindow.closed) {
return;
}
if (this._sessionInitialized) {
this.onLoad(aWindow);
} else {
let initialState = this.initSession();
this._sessionInitialized = true;
this.onLoad(aWindow, initialState);
// Let everyone know we're done.
this._deferredInitialized.resolve();
}
}, console.error);
};
aWindow.addEventListener("load", onload);
}, console.error);
},
/**

View File

@ -31,9 +31,7 @@ function test() {
// open a window and add the above closed tab list
let newWin = openDialog(location, "", "chrome,all,dialog=no");
newWin.addEventListener("load", function(aEvent) {
newWin.removeEventListener("load", arguments.callee, false);
promiseWindowLoaded(newWin).then(() => {
gPrefService.setIntPref("browser.sessionstore.max_tabs_undo",
test_state.windows[0]._closedTabs.length);
ss.setWindowState(newWin, JSON.stringify(test_state), true);
@ -71,5 +69,5 @@ function test() {
newWin.close();
gPrefService.clearUserPref("browser.sessionstore.max_tabs_undo");
finish();
}, false);
});
}

View File

@ -52,9 +52,7 @@ function test() {
// open a window and add the above closed tab list
let newWin = openDialog(location, "", "chrome,all,dialog=no");
newWin.addEventListener("load", function(aEvent) {
newWin.removeEventListener("load", arguments.callee, false);
promiseWindowLoaded(newWin).then(() => {
gPrefService.setIntPref("browser.sessionstore.max_tabs_undo",
test_state.windows[0]._closedTabs.length);
ss.setWindowState(newWin, JSON.stringify(test_state), true);
@ -83,5 +81,5 @@ function test() {
gPrefService.clearUserPref("browser.sessionstore.max_tabs_undo");
finish();
});
}, false);
});
}

View File

@ -15,9 +15,7 @@ function test() {
// open a window and set a value on it
let newWin = openDialog(location, "_blank", "chrome,all,dialog=no");
newWin.addEventListener("load", function(aEvent) {
newWin.removeEventListener("load", arguments.callee, false);
promiseWindowLoaded(newWin).then(() => {
ss.setWindowValue(newWin, uniqueKey1, uniqueValue1);
let newState = { windows: [{ tabs:[{ entries: [] }], extData: {} }] };
@ -44,5 +42,5 @@ function test() {
// clean up
newWin.close();
finish();
}, false);
});
}

View File

@ -7,9 +7,7 @@ function test() {
waitForExplicitFinish();
let newWin = openDialog(location, "_blank", "chrome,all,dialog=no");
newWin.addEventListener("load", function(aEvent) {
newWin.removeEventListener("load", arguments.callee, false);
promiseWindowLoaded(newWin).then(() => {
let newState = { windows: [{
tabs: [{ entries: [] }],
_closedTabs: [{
@ -59,5 +57,5 @@ function test() {
}, 0);
}, 0);
}, 0);
}, false);
});
}

View File

@ -80,8 +80,7 @@ function test() {
// open a window and add the above closed window list
let newWin = openDialog(location, "_blank", "chrome,all,dialog=no");
newWin.addEventListener("load", function(aEvent) {
this.removeEventListener("load", arguments.callee, false);
promiseWindowLoaded(newWin).then(() => {
gPrefService.setIntPref("browser.sessionstore.max_windows_undo",
test_state._closedWindows.length);
ss.setWindowState(newWin, JSON.stringify(test_state), true);
@ -117,5 +116,5 @@ function test() {
newWin.close();
gPrefService.clearUserPref("browser.sessionstore.max_windows_undo");
finish();
}, false);
});
}

View File

@ -8,47 +8,38 @@ function test() {
waitForExplicitFinish();
let newWin = openDialog(location, "_blank", "chrome,all,dialog=no,toolbar=yes");
newWin.addEventListener("load", function() {
newWin.removeEventListener("load", arguments.callee, false);
promiseWindowLoaded(newWin).then(() => {
let state1 = ss.getWindowState(newWin);
newWin.close();
executeSoon(function() {
let state1 = ss.getWindowState(newWin);
newWin = openDialog(location, "_blank",
"chrome,extrachrome,menubar,resizable,scrollbars,status,toolbar=no,location,personal,directories,dialog=no");
promiseWindowLoaded(newWin).then(() => {
let state2 = ss.getWindowState(newWin);
newWin.close();
newWin = openDialog(location, "_blank",
"chrome,extrachrome,menubar,resizable,scrollbars,status,toolbar=no,location,personal,directories,dialog=no");
newWin.addEventListener("load", function() {
newWin.removeEventListener("load", arguments.callee, false);
function testState(state, expected, callback) {
let win = openDialog(location, "_blank", "chrome,all,dialog=no");
promiseWindowLoaded(win).then(() => {
executeSoon(function() {
let state2 = ss.getWindowState(newWin);
newWin.close();
is(win.gURLBar.readOnly, false,
"URL bar should not be read-only before setting the state");
is(win.gURLBar.getAttribute("enablehistory"), "true",
"URL bar autocomplete should be enabled before setting the state");
ss.setWindowState(win, state, true);
is(win.gURLBar.readOnly, expected.readOnly,
"URL bar read-only state should be restored correctly");
is(win.gURLBar.getAttribute("enablehistory"), expected.enablehistory,
"URL bar autocomplete state should be restored correctly");
function testState(state, expected, callback) {
let win = openDialog(location, "_blank", "chrome,all,dialog=no");
win.addEventListener("load", function() {
win.removeEventListener("load", arguments.callee, false);
is(win.gURLBar.readOnly, false,
"URL bar should not be read-only before setting the state");
is(win.gURLBar.getAttribute("enablehistory"), "true",
"URL bar autocomplete should be enabled before setting the state");
ss.setWindowState(win, state, true);
is(win.gURLBar.readOnly, expected.readOnly,
"URL bar read-only state should be restored correctly");
is(win.gURLBar.getAttribute("enablehistory"), expected.enablehistory,
"URL bar autocomplete state should be restored correctly");
win.close();
executeSoon(callback);
}, false);
}
testState(state1, {readOnly: false, enablehistory: "true"}, function() {
testState(state2, {readOnly: true, enablehistory: "false"}, finish);
});
win.close();
executeSoon(callback);
});
}, false);
}
testState(state1, {readOnly: false, enablehistory: "true"}, function() {
testState(state2, {readOnly: true, enablehistory: "false"}, finish);
});
});
}, false);
});
}

View File

@ -17,11 +17,10 @@ function test() {
browserWindowsCount(1);
var win = openDialog(location, "", "chrome,all,dialog=no");
win.addEventListener("load", function () {
win.removeEventListener("load", arguments.callee, false);
promiseWindowLoaded(win).then(() => {
browserWindowsCount(2);
win.close();
browserWindowsCount(1);
finish();
}, false);
});
}

View File

@ -10,9 +10,7 @@ function test() {
// open a new window and setup the window state.
newWin = openDialog(getBrowserURL(), "_blank", "chrome,all,dialog=no");
newWin.addEventListener("load", function onLoad(event) {
this.removeEventListener("load", onLoad, false);
whenWindowLoaded(newWin, function () {
let newState = {
windows: [{
tabs: [{
@ -69,5 +67,5 @@ function test() {
}
newWin.addEventListener("tabviewshown", onTabViewShow, false);
waitForFocus(function() { newWin.TabView.toggle(); });
}, false);
});
}

View File

@ -142,8 +142,10 @@ function afterAllTabsLoaded(callback, win) {
browser.__SS_restoreState &&
browser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE);
if (isRestorable && browser.contentDocument.readyState != "complete" ||
browser.webProgress.isLoadingDocument) {
let isLoading = browser.webProgress.isLoadingDocument ||
browser.contentDocument.readyState != "complete";
if (isRestorable && isLoading) {
stillToLoad++;
browser.addEventListener("load", onLoad, true);
}