From a0f140ad0274be665e8cb766bfb33a8bd1cce21c Mon Sep 17 00:00:00 2001 From: Tim Taubert Date: Fri, 26 Jul 2013 13:16:29 +0200 Subject: [PATCH] Bug 893061 - Prevent the default homepage from loading if we're going to restore a session; r=dao --- browser/base/content/browser.js | 46 +++++++--- .../sessionstore/nsISessionStartup.idl | 18 +++- .../sessionstore/src/nsSessionStartup.js | 88 +++++++------------ 3 files changed, 81 insertions(+), 71 deletions(-) diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index d386360d919..9253f6c1515 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -728,18 +728,6 @@ const gFormSubmitObserver = { var gBrowserInit = { onLoad: function() { - // window.arguments[0]: URI to load (string), or an nsISupportsArray of - // nsISupportsStrings to load, or a xul:tab of - // a tabbrowser, which will be replaced by this - // window (for this case, all other arguments are - // ignored). - // [1]: character set (string) - // [2]: referrer (nsIURI) - // [3]: postData (nsIInputStream) - // [4]: allowThirdPartyFixup (bool) - if ("arguments" in window && window.arguments[0]) - var uriToLoad = window.arguments[0]; - gMultiProcessBrowser = gPrefService.getBoolPref("browser.tabs.remote"); var mustLoadSidebar = false; @@ -780,6 +768,7 @@ var gBrowserInit = { new nsBrowserAccess(); // set default character set if provided + // window.arguments[1]: character set (string) if ("arguments" in window && window.arguments.length > 1 && window.arguments[1]) { if (window.arguments[1].startsWith("charset=")) { var arrayArgComponents = window.arguments[1].split("="); @@ -946,7 +935,7 @@ var gBrowserInit = { retrieveToolbarIconsizesFromTheme(); // Wait until chrome is painted before executing code not critical to making the window visible - this._boundDelayedStartup = this._delayedStartup.bind(this, uriToLoad, mustLoadSidebar); + this._boundDelayedStartup = this._delayedStartup.bind(this, mustLoadSidebar); window.addEventListener("MozAfterPaint", this._boundDelayedStartup); this._loadHandled = true; @@ -957,7 +946,7 @@ var gBrowserInit = { this._boundDelayedStartup = null; }, - _delayedStartup: function(uriToLoad, mustLoadSidebar) { + _delayedStartup: function(mustLoadSidebar) { let tmp = {}; Cu.import("resource://gre/modules/TelemetryTimestamps.jsm", tmp); let TelemetryTimestamps = tmp.TelemetryTimestamps; @@ -976,6 +965,7 @@ var gBrowserInit = { socialBrowser.addEventListener("MozApplicationManifest", OfflineApps, false); + let uriToLoad = this._getUriToLoad(); var isLoadingBlank = isBlankPageURL(uriToLoad); // This pageshow listener needs to be registered before we may call @@ -1012,6 +1002,9 @@ var gBrowserInit = { gBrowser.swapBrowsersAndCloseOther(gBrowser.selectedTab, uriToLoad); } + // window.arguments[2]: referrer (nsIURI) + // [3]: postData (nsIInputStream) + // [4]: allowThirdPartyFixup (bool) else if (window.arguments.length >= 3) { loadURI(uriToLoad, window.arguments[2], window.arguments[3] || null, window.arguments[4] || false); @@ -1293,6 +1286,31 @@ var gBrowserInit = { TelemetryTimestamps.add("delayedStartupFinished"); }, + // Returns the URI(s) to load at startup. + _getUriToLoad: function () { + // window.arguments[0]: URI to load (string), or an nsISupportsArray of + // nsISupportsStrings to load, or a xul:tab of + // a tabbrowser, which will be replaced by this + // window (for this case, all other arguments are + // ignored). + if (!window.arguments || !window.arguments[0]) + return null; + + let uri = window.arguments[0]; + let sessionStartup = Cc["@mozilla.org/browser/sessionstartup;1"] + .getService(Ci.nsISessionStartup); + let defaultArgs = Cc["@mozilla.org/browser/clh;1"] + .getService(Ci.nsIBrowserHandler) + .defaultArgs; + + // If the given URI matches defaultArgs (the default homepage) we want + // to block its load if we're going to restore a session anyway. + if (uri == defaultArgs && sessionStartup.willOverrideHomepage) + return null; + + return uri; + }, + onUnload: function() { // In certain scenarios it's possible for unload to be fired before onload, // (e.g. if the window is being closed after browser.js loads but before the diff --git a/browser/components/sessionstore/nsISessionStartup.idl b/browser/components/sessionstore/nsISessionStartup.idl index d9833cb4dcb..a8e786d03e9 100644 --- a/browser/components/sessionstore/nsISessionStartup.idl +++ b/browser/components/sessionstore/nsISessionStartup.idl @@ -10,7 +10,7 @@ * - and allows to restore everything into one window. */ -[scriptable, uuid(35235b39-7098-4b3b-8e28-cd004a88b06f)] +[scriptable, uuid(51f4b9f0-f3d2-11e2-bb62-2c24dd830245)] interface nsISessionStartup: nsISupports { /** @@ -23,10 +23,24 @@ interface nsISessionStartup: nsISupports readonly attribute jsval state; /** - * Determine if session should be restored + * Determines whether there is a pending session restore and makes sure that + * we're initialized before returning. If we're not yet this will read the + * session file synchronously. */ boolean doRestore(); + /** + * Returns whether we will restore a session that ends up replacing the + * homepage. The browser uses this to not start loading the homepage if + * we're going to stop its load anyway shortly after. + * + * This is meant to be an optimization for the average case that loading the + * session file finishes before we may want to start loading the default + * homepage. Should this be called before the session file has been read it + * will just return false. + */ + readonly attribute bool willOverrideHomepage; + /** * What type of session we're restoring. * NO_SESSION There is no data available from the previous session diff --git a/browser/components/sessionstore/src/nsSessionStartup.js b/browser/components/sessionstore/src/nsSessionStartup.js index 6d8c7925bf1..52de14cac9b 100644 --- a/browser/components/sessionstore/src/nsSessionStartup.js +++ b/browser/components/sessionstore/src/nsSessionStartup.js @@ -162,15 +162,6 @@ SessionStartup.prototype = { else this._initialState = null; // reset the state - // wait for the first browser window to open - // Don't reset the initial window's default args (i.e. the home page(s)) - // if all stored tabs are pinned. - if (this.doRestore() && - (!this._initialState.windows || - !this._initialState.windows.every(function (win) - win.tabs.every(function (tab) tab.pinned)))) - Services.obs.addObserver(this, "domwindowopened", true); - Services.obs.addObserver(this, "sessionstore-windows-restored", true); if (this._sessionType != Ci.nsISessionStartup.NO_SESSION) @@ -204,14 +195,6 @@ SessionStartup.prototype = { if (this._sessionType != Ci.nsISessionStartup.NO_SESSION) Services.obs.removeObserver(this, "browser:purge-session-history"); break; - case "domwindowopened": - var window = aSubject; - var self = this; - window.addEventListener("load", function() { - self._onWindowOpened(window); - window.removeEventListener("load", arguments.callee, false); - }, false); - break; case "sessionstore-windows-restored": Services.obs.removeObserver(this, "sessionstore-windows-restored"); // free _initialState after nsSessionStore is done with it @@ -225,43 +208,6 @@ SessionStartup.prototype = { } }, - /** - * Removes the default arguments from the first browser window - * (and removes the "domwindowopened" observer afterwards). - */ - _onWindowOpened: function sss_onWindowOpened(aWindow) { - var wType = aWindow.document.documentElement.getAttribute("windowtype"); - if (wType != "navigator:browser") - return; - - /** - * Note: this relies on the fact that nsBrowserContentHandler will return - * a different value the first time its getter is called after an update, - * due to its needHomePageOverride() logic. We don't want to remove the - * default arguments in the update case, since they include the "What's - * New" page. - * - * Since we're garanteed to be at least the second caller of defaultArgs - * (nsBrowserContentHandler calls it to determine which arguments to pass - * at startup), we know that if the window's arguments don't match the - * current defaultArguments, we're either in the update case, or we're - * launching a non-default browser window, so we shouldn't remove the - * window's arguments. - */ - var defaultArgs = Cc["@mozilla.org/browser/clh;1"]. - getService(Ci.nsIBrowserHandler).defaultArgs; - if (aWindow.arguments && aWindow.arguments[0] && - aWindow.arguments[0] == defaultArgs) - aWindow.arguments[0] = null; - - try { - Services.obs.removeObserver(this, "domwindowopened"); - } catch (e) { - // This might throw if we're removing the observer multiple times, - // but this is safe to ignore. - } - }, - /* ........ Public API ................*/ get onceInitialized() { @@ -277,15 +223,47 @@ SessionStartup.prototype = { }, /** - * Determine whether there is a pending session restore. + * Determines whether there is a pending session restore and makes sure that + * we're initialized before returning. If we're not yet this will read the + * session file synchronously. * @returns bool */ doRestore: function sss_doRestore() { this._ensureInitialized(); + return this._willRestore(); + }, + + /** + * Determines whether there is a pending session restore. + * @returns bool + */ + _willRestore: function () { return this._sessionType == Ci.nsISessionStartup.RECOVER_SESSION || this._sessionType == Ci.nsISessionStartup.RESUME_SESSION; }, + /** + * Returns whether we will restore a session that ends up replacing the + * homepage. The browser uses this to not start loading the homepage if + * we're going to stop its load anyway shortly after. + * + * This is meant to be an optimization for the average case that loading the + * session file finishes before we may want to start loading the default + * homepage. Should this be called before the session file has been read it + * will just return false. + * + * @returns bool + */ + get willOverrideHomepage() { + if (this._initialState && this._willRestore()) { + let windows = this._initialState.windows || null; + // If there are valid windows with not only pinned tabs, signal that we + // will override the default homepage by restoring a session. + return windows && windows.some(w => w.tabs.some(t => !t.pinned)); + } + return false; + }, + /** * Get the type of pending session store, if any. */