Bug 1054740 - When a session should be restored, don't load the homepage before the session file has been read. r?mikedeboer draft
authorDão Gottwald <dao@mozilla.com>
Thu, 24 Aug 2017 18:39:39 +0200
changeset 652224 4d7956fd55b17de3b499df3f6c812c8467049ff7
parent 652136 892c8916ba32b7733e06bfbfdd4083ffae3ca028
child 728031 e055cd4e733bd8a8b42745eefa50e317e471da78
push id75996
push userdgottwald@mozilla.com
push dateThu, 24 Aug 2017 16:40:03 +0000
reviewersmikedeboer
bugs1054740
milestone57.0a1
Bug 1054740 - When a session should be restored, don't load the homepage before the session file has been read. r?mikedeboer MozReview-Commit-ID: 7W4ihQZEeWf
accessible/tests/mochitest/common.js
browser/base/content/browser.js
browser/base/content/test/performance/browser_windowopen_reflows.js
browser/components/sessionstore/nsISessionStartup.idl
browser/components/sessionstore/nsSessionStartup.js
dom/tests/mochitest/localstorage/test_localStorageQuotaPrivateBrowsing_perwindowpb.html
--- a/accessible/tests/mochitest/common.js
+++ b/accessible/tests/mochitest/common.js
@@ -163,17 +163,23 @@ function addA11yLoadEvent(aFunc, aWindow
           return waitForDocLoad();
 
         window.setTimeout(aFunc, 0);
       },
       0
     );
   }
 
-  SimpleTest.waitForFocus(waitForDocLoad, aWindow);
+  if (aWindow &&
+      aWindow.document.activeElement &&
+      aWindow.document.activeElement.localName == "browser") {
+    waitForDocLoad();
+  } else {
+    SimpleTest.waitForFocus(waitForDocLoad, aWindow);
+  }
 }
 
 /**
  * Analogy of SimpleTest.is function used to compare objects.
  */
 function isObject(aObj, aExpectedObj, aMsg) {
   if (aObj == aExpectedObj) {
     ok(true, aMsg);
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1374,47 +1374,47 @@ var gBrowserInit = {
         document.documentElement.setAttribute("darkwindowframe", "true");
       }
     }
 
     ToolbarIconColor.init();
 
     gRemoteControl.updateVisualCue(Marionette.running);
 
-    let uriToLoad = this._getUriToLoad();
-    gIdentityHandler.initIdentityBlock(uriToLoad);
+    this._uriToLoadPromise.then(uriToLoad => {
+      gIdentityHandler.initIdentityBlock(uriToLoad);
+    });
 
     // Wait until chrome is painted before executing code not critical to making the window visible
-    this._boundDelayedStartup = this._delayedStartup.bind(this, uriToLoad);
+    this._boundDelayedStartup = this._delayedStartup.bind(this);
     window.addEventListener("MozAfterPaint", this._boundDelayedStartup);
 
     this._loadHandled = true;
   },
 
   _cancelDelayedStartup() {
     window.removeEventListener("MozAfterPaint", this._boundDelayedStartup);
     this._boundDelayedStartup = null;
   },
 
-  _delayedStartup(uriToLoad) {
+  _delayedStartup() {
     let tmp = {};
     Cu.import("resource://gre/modules/TelemetryTimestamps.jsm", tmp);
     let TelemetryTimestamps = tmp.TelemetryTimestamps;
     TelemetryTimestamps.add("delayedStartupStarted");
 
     this._cancelDelayedStartup();
 
     // We need to set the OfflineApps message listeners up before we
     // load homepages, which might need them.
     OfflineApps.init();
 
     // This pageshow listener needs to be registered before we may call
     // swapBrowsersAndCloseOther() to receive pageshow events fired by that.
-    let mm = window.messageManager;
-    mm.addMessageListener("PageVisibility:Show", function(message) {
+    window.messageManager.addMessageListener("PageVisibility:Show", function(message) {
       if (message.target == gBrowser.selectedBrowser) {
         setTimeout(pageShowEventHandlers, 0, message.data.persisted);
       }
     });
 
     gBrowser.addEventListener("AboutTabCrashedLoad", function(event) {
       let ownerDoc = event.originalTarget;
 
@@ -1435,99 +1435,17 @@ var gBrowserInit = {
       gIdentityHandler.refreshIdentityBlock();
     });
 
     // Get the service so that it initializes and registers listeners for new
     // tab pages in order to be ready for any early-loading about:newtab pages,
     // e.g., start/home page, command line / startup uris to load, sessionstore
     gAboutNewTabService.QueryInterface(Ci.nsISupports);
 
-    if (uriToLoad && uriToLoad != "about:blank") {
-      if (uriToLoad instanceof Ci.nsIArray) {
-        let count = uriToLoad.length;
-        let specs = [];
-        for (let i = 0; i < count; i++) {
-          let urisstring = uriToLoad.queryElementAt(i, Ci.nsISupportsString);
-          specs.push(urisstring.data);
-        }
-
-        // This function throws for certain malformed URIs, so use exception handling
-        // so that we don't disrupt startup
-        try {
-          gBrowser.loadTabs(specs, {
-            inBackground: false,
-            replace: true,
-            triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
-          });
-        } catch (e) {}
-      } else if (uriToLoad instanceof XULElement) {
-        // swap the given tab with the default about:blank tab and then close
-        // the original tab in the other window.
-        let tabToOpen = uriToLoad;
-
-        // If this tab was passed as a window argument, clear the
-        // reference to it from the arguments array.
-        if (window.arguments[0] == tabToOpen) {
-          window.arguments[0] = null;
-        }
-
-        // Stop the about:blank load
-        gBrowser.stop();
-        // make sure it has a docshell
-        gBrowser.docShell;
-
-        // We must set usercontextid before updateBrowserRemoteness()
-        // so that the newly created remote tab child has correct usercontextid
-        if (tabToOpen.hasAttribute("usercontextid")) {
-          let usercontextid = tabToOpen.getAttribute("usercontextid");
-          gBrowser.selectedBrowser.setAttribute("usercontextid", usercontextid);
-        }
-
-        try {
-          // Make sure selectedBrowser has the same remote settings as the one
-          // we are swapping in.
-          gBrowser.updateBrowserRemoteness(gBrowser.selectedBrowser,
-                                           tabToOpen.linkedBrowser.isRemoteBrowser,
-                                           { remoteType: tabToOpen.linkedBrowser.remoteType });
-          gBrowser.swapBrowsersAndCloseOther(gBrowser.selectedTab, tabToOpen);
-        } catch (e) {
-          Cu.reportError(e);
-        }
-      } else if (window.arguments.length >= 3) {
-        // window.arguments[2]: referrer (nsIURI | string)
-        //                 [3]: postData (nsIInputStream)
-        //                 [4]: allowThirdPartyFixup (bool)
-        //                 [5]: referrerPolicy (int)
-        //                 [6]: userContextId (int)
-        //                 [7]: originPrincipal (nsIPrincipal)
-        //                 [8]: triggeringPrincipal (nsIPrincipal)
-        let referrerURI = window.arguments[2];
-        if (typeof(referrerURI) == "string") {
-          try {
-            referrerURI = makeURI(referrerURI);
-          } catch (e) {
-            referrerURI = null;
-          }
-        }
-        let referrerPolicy = (window.arguments[5] != undefined ?
-            window.arguments[5] : Ci.nsIHttpChannel.REFERRER_POLICY_UNSET);
-        let userContextId = (window.arguments[6] != undefined ?
-            window.arguments[6] : Ci.nsIScriptSecurityManager.DEFAULT_USER_CONTEXT_ID);
-        loadURI(uriToLoad, referrerURI, window.arguments[3] || null,
-                window.arguments[4] || false, referrerPolicy, userContextId,
-                // pass the origin principal (if any) and force its use to create
-                // an initial about:blank viewer if present:
-                window.arguments[7], !!window.arguments[7], window.arguments[8]);
-        window.focus();
-      } else {
-        // Note: loadOneOrMoreURIs *must not* be called if window.arguments.length >= 3.
-        // Such callers expect that window.arguments[0] is handled as a single URI.
-        loadOneOrMoreURIs(uriToLoad, Services.scriptSecurityManager.getSystemPrincipal());
-      }
-    }
+    this._handleURIToLoad();
 
     Services.obs.addObserver(gIdentityHandler, "perm-changed");
     Services.obs.addObserver(gRemoteControl, "remote-active");
     Services.obs.addObserver(gSessionHistoryObserver, "browser:purge-session-history");
     Services.obs.addObserver(gStoragePressureObserver, "QuotaManager::StoragePressure");
     Services.obs.addObserver(gXPInstallObserver, "addon-install-disabled");
     Services.obs.addObserver(gXPInstallObserver, "addon-install-started");
     Services.obs.addObserver(gXPInstallObserver, "addon-install-blocked");
@@ -1546,37 +1464,16 @@ var gBrowserInit = {
     // Initialize the full zoom setting.
     // We do this before the session restore service gets initialized so we can
     // apply full zoom settings to tabs restored by the session restore service.
     FullZoom.init();
     PanelUI.init();
 
     UpdateUrlbarSearchSplitterState();
 
-    if (!(isBlankPageURL(uriToLoad) || uriToLoad == "about:privatebrowsing") ||
-        !focusAndSelectUrlBar()) {
-      if (gBrowser.selectedBrowser.isRemoteBrowser) {
-        // If the initial browser is remote, in order to optimize for first paint,
-        // we'll defer switching focus to that browser until it has painted.
-        let focusedElement = document.commandDispatcher.focusedElement;
-        mm.addMessageListener("Browser:FirstPaint", function onFirstPaint() {
-          mm.removeMessageListener("Browser:FirstPaint", onFirstPaint);
-          // If focus didn't move while we were waiting for first paint, we're okay
-          // to move to the browser.
-          if (document.commandDispatcher.focusedElement == focusedElement) {
-            gBrowser.selectedBrowser.focus();
-          }
-        });
-      } else {
-        // If the initial browser is not remote, we can focus the browser
-        // immediately with no paint performance impact.
-        gBrowser.selectedBrowser.focus();
-      }
-    }
-
     // Enable/Disable auto-hide tabbar
     gBrowser.tabContainer.updateVisibility();
 
     BookmarkingUI.init();
     AutoShowBookmarksToolbar.init();
 
     gPrefService.addObserver(gHomeButton.prefDomain, gHomeButton);
 
@@ -1688,16 +1585,142 @@ var gBrowserInit = {
       this._schedulePerWindowIdleTasks();
       document.documentElement.setAttribute("sessionrestored", "true");
     });
 
     Services.obs.notifyObservers(window, "browser-delayed-startup-finished");
     TelemetryTimestamps.add("delayedStartupFinished");
   },
 
+  _handleURIToLoad() {
+    let initiallyFocusedElement = document.commandDispatcher.focusedElement;
+
+    let firstBrowserPaintDeferred = {};
+    firstBrowserPaintDeferred.promise = new Promise(resolve => {
+      firstBrowserPaintDeferred.resolve = resolve;
+    });
+
+    let mm = window.messageManager;
+    mm.addMessageListener("Browser:FirstPaint", function onFirstPaint() {
+      mm.removeMessageListener("Browser:FirstPaint", onFirstPaint);
+      firstBrowserPaintDeferred.resolve();
+    });
+
+    this._uriToLoadPromise.then(uriToLoad => {
+      if (!uriToLoad || uriToLoad == "about:blank") {
+        return;
+      }
+
+      if (uriToLoad instanceof Ci.nsIArray) {
+        let count = uriToLoad.length;
+        let specs = [];
+        for (let i = 0; i < count; i++) {
+          let urisstring = uriToLoad.queryElementAt(i, Ci.nsISupportsString);
+          specs.push(urisstring.data);
+        }
+
+        // This function throws for certain malformed URIs, so use exception handling
+        // so that we don't disrupt startup
+        try {
+          gBrowser.loadTabs(specs, {
+            inBackground: false,
+            replace: true,
+            triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
+          });
+        } catch (e) {}
+      } else if (uriToLoad instanceof XULElement) {
+        // swap the given tab with the default about:blank tab and then close
+        // the original tab in the other window.
+        let tabToOpen = uriToLoad;
+
+        // If this tab was passed as a window argument, clear the
+        // reference to it from the arguments array.
+        if (window.arguments[0] == tabToOpen) {
+          window.arguments[0] = null;
+        }
+
+        // Stop the about:blank load
+        gBrowser.stop();
+        // make sure it has a docshell
+        gBrowser.docShell;
+
+        // We must set usercontextid before updateBrowserRemoteness()
+        // so that the newly created remote tab child has correct usercontextid
+        if (tabToOpen.hasAttribute("usercontextid")) {
+          let usercontextid = tabToOpen.getAttribute("usercontextid");
+          gBrowser.selectedBrowser.setAttribute("usercontextid", usercontextid);
+        }
+
+        try {
+          // Make sure selectedBrowser has the same remote settings as the one
+          // we are swapping in.
+          gBrowser.updateBrowserRemoteness(gBrowser.selectedBrowser,
+                                           tabToOpen.linkedBrowser.isRemoteBrowser,
+                                           { remoteType: tabToOpen.linkedBrowser.remoteType });
+          gBrowser.swapBrowsersAndCloseOther(gBrowser.selectedTab, tabToOpen);
+        } catch (e) {
+          Cu.reportError(e);
+        }
+      } else if (window.arguments.length >= 3) {
+        // window.arguments[2]: referrer (nsIURI | string)
+        //                 [3]: postData (nsIInputStream)
+        //                 [4]: allowThirdPartyFixup (bool)
+        //                 [5]: referrerPolicy (int)
+        //                 [6]: userContextId (int)
+        //                 [7]: originPrincipal (nsIPrincipal)
+        //                 [8]: triggeringPrincipal (nsIPrincipal)
+        let referrerURI = window.arguments[2];
+        if (typeof(referrerURI) == "string") {
+          try {
+            referrerURI = makeURI(referrerURI);
+          } catch (e) {
+            referrerURI = null;
+          }
+        }
+        let referrerPolicy = (window.arguments[5] != undefined ?
+            window.arguments[5] : Ci.nsIHttpChannel.REFERRER_POLICY_UNSET);
+        let userContextId = (window.arguments[6] != undefined ?
+            window.arguments[6] : Ci.nsIScriptSecurityManager.DEFAULT_USER_CONTEXT_ID);
+        loadURI(uriToLoad, referrerURI, window.arguments[3] || null,
+                window.arguments[4] || false, referrerPolicy, userContextId,
+                // pass the origin principal (if any) and force its use to create
+                // an initial about:blank viewer if present:
+                window.arguments[7], !!window.arguments[7], window.arguments[8]);
+        window.focus();
+      } else {
+        // Note: loadOneOrMoreURIs *must not* be called if window.arguments.length >= 3.
+        // Such callers expect that window.arguments[0] is handled as a single URI.
+        loadOneOrMoreURIs(uriToLoad, Services.scriptSecurityManager.getSystemPrincipal());
+      }
+    });
+
+    this._uriToLoadPromise.then(uriToLoad => {
+      if ((isBlankPageURL(uriToLoad) || uriToLoad == "about:privatebrowsing") &&
+          focusAndSelectUrlBar()) {
+        return;
+      }
+
+      if (gBrowser.selectedBrowser.isRemoteBrowser) {
+        // If the initial browser is remote, in order to optimize for first paint,
+        // we'll defer switching focus to that browser until it has painted.
+        firstBrowserPaintDeferred.promise.then(() => {
+          // If focus didn't move while we were waiting for first paint, we're okay
+          // to move to the browser.
+          if (document.commandDispatcher.focusedElement == initiallyFocusedElement) {
+            gBrowser.selectedBrowser.focus();
+          }
+        });
+      } else {
+        // If the initial browser is not remote, we can focus the browser
+        // immediately with no paint performance impact.
+        gBrowser.selectedBrowser.focus();
+      }
+    });
+  },
+
   /**
    * Use this function as an entry point to schedule tasks that
    * need to run once per window after startup, and can be scheduled
    * by using an idle callback.
    *
    * The functions scheduled here will fire from idle callbacks
    * once every window has finished being restored by session
    * restore, and after the equivalent only-once tasks
@@ -1750,38 +1773,48 @@ var gBrowserInit = {
           .DownloadsTaskbar.registerIndicator(window);
       } catch (ex) {
         Cu.reportError(ex);
       }
     }, {timeout: 10000});
   },
 
   // Returns the URI(s) to load at startup.
-  _getUriToLoad() {
-    // window.arguments[0]: URI to load (string), or an nsIArray 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;
+  get _uriToLoadPromise() {
+    delete this._uriToLoadPromise;
+    return this._uriToLoadPromise = new Promise(resolve => {
+      // window.arguments[0]: URI to load (string), or an nsIArray 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]) {
+        resolve(null);
+        return;
+      }
+
+      let uri = window.arguments[0];
+      let defaultArgs = Cc["@mozilla.org/browser/clh;1"]
+                          .getService(Ci.nsIBrowserHandler)
+                          .defaultArgs;
+
+      // If the given URI is different from the homepage, we want to load it.
+      if (uri != defaultArgs) {
+        resolve(uri);
+        return;
+      }
+
+      // The URI appears to be the the homepage. We want to load it only if
+      // session restore isn't about to override the homepage.
+      let sessionStartup = Cc["@mozilla.org/browser/sessionstartup;1"]
+                             .getService(Ci.nsISessionStartup);
+      sessionStartup.willOverrideHomepagePromise.then(willOverrideHomepage => {
+        resolve(willOverrideHomepage ? null : uri);
+      });
+    });
   },
 
   onUnload() {
     // 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
     // load completes). In that case, there's nothing to do here.
     if (!this._loadHandled)
       return;
@@ -7721,16 +7754,21 @@ var gIdentityHandler = {
   },
 
   /**
    * Used to initialize the identity block before first paint to avoid
    * flickering when opening a new window showing a secure internal page
    * (eg. about:home)
    */
   initIdentityBlock(initialURI) {
+    if (this._uri) {
+      // Apparently we already loaded something, so there's nothing to do here.
+      return;
+    }
+
     if ((typeof initialURI != "string") || !initialURI.startsWith("about:"))
       return;
 
     let uri = Services.io.newURI(initialURI);
     if (this._secureInternalUIWhitelist.test(uri.pathQueryRef)) {
       this._isSecureInternalUI = true;
       this._ignoreAboutBlankUntilFirstLoad = true;
       this.refreshIdentityBlock();
--- a/browser/base/content/test/performance/browser_windowopen_reflows.js
+++ b/browser/base/content/test/performance/browser_windowopen_reflows.js
@@ -12,17 +12,16 @@
  * See https://developer.mozilla.org/en-US/Firefox/Performance_best_practices_for_Firefox_fe_engineers
  * for tips on how to do that.
  */
 const EXPECTED_REFLOWS = [
   {
     stack: [
       "select@chrome://global/content/bindings/textbox.xml",
       "focusAndSelectUrlBar@chrome://browser/content/browser.js",
-      "_delayedStartup@chrome://browser/content/browser.js",
     ],
   },
 ];
 
 if (Services.appinfo.OS == "Linux") {
   if (gMultiProcessBrowser) {
     EXPECTED_REFLOWS.push({
       stack: [
--- a/browser/components/sessionstore/nsISessionStartup.idl
+++ b/browser/components/sessionstore/nsISessionStartup.idl
@@ -31,32 +31,21 @@ interface nsISessionStartup: nsISupports
   /**
    * Determines whether automatic session restoration is enabled for this
    * launch of the browser. This does not include crash restoration, and will
    * return false if restoration will only be caused by a crash.
    */
   boolean isAutomaticRestoreEnabled();
 
   /**
-   * 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;
-
-  /**
-   * Returns a promise that resolves to a boolean indicating 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.
+   * Returns a promise that resolves to a boolean, indicating whether we will
+   * restore a session that ends up replacing the homepage. True guarantees
+   * that we'll restore a session; false means that we /probably/ won't do so.
+   * The browser uses this to avoid unnecessarily loading the homepage when
+   * restoring a session.
    */
   readonly attribute jsval willOverrideHomepagePromise;
 
   /**
    * What type of session we're restoring.
    * NO_SESSION       There is no data available from the previous session
    * RECOVER_SESSION  The last session crashed. It will either be restored or
    *                  about:sessionrestore will be shown.
--- a/browser/components/sessionstore/nsSessionStartup.js
+++ b/browser/components/sessionstore/nsSessionStartup.js
@@ -85,32 +85,38 @@ SessionStartup.prototype = {
   // the state to restore at startup
   _initialState: null,
   _sessionType: Ci.nsISessionStartup.NO_SESSION,
   _initialized: false,
 
   // Stores whether the previous session crashed.
   _previousSessionCrashed: null,
 
+  _resumeSessionEnabled: null,
+
 /* ........ Global Event Handlers .............. */
 
   /**
    * Initialize the component
    */
   init: function sss_init() {
     Services.obs.notifyObservers(null, "sessionstore-init-started");
     StartupPerformance.init();
 
     // do not need to initialize anything in auto-started private browsing sessions
     if (PrivateBrowsingUtils.permanentPrivateBrowsing) {
       this._initialized = true;
       gOnceInitializedDeferred.resolve();
       return;
     }
 
+    this._resumeSessionEnabled =
+      Services.prefs.getBoolPref("browser.sessionstore.resume_session_once") ||
+      Services.prefs.getIntPref("browser.startup.page") == BROWSER_STARTUP_RESUME_SESSION;
+
     SessionFile.read().then(
       this._onSessionFileRead.bind(this),
       console.error
     );
   },
 
   // Wrap a string as a nsISupports
   _createSupportsString: function ssfi_createSupportsString(aData) {
@@ -161,22 +167,18 @@ SessionStartup.prototype = {
       let pinnedTabCount = initialState.windows.reduce((winAcc, win) => {
         return winAcc + win.tabs.reduce((tabAcc, tab) => {
           return tabAcc + (tab.pinned ? 1 : 0);
         }, 0);
       }, 0);
       Services.telemetry.scalarSet("browser.engagement.restored_pinned_tabs_count", pinnedTabCount);
     }, 60000);
 
-    let shouldResumeSessionOnce = Services.prefs.getBoolPref("browser.sessionstore.resume_session_once");
-    let shouldResumeSession = shouldResumeSessionOnce ||
-          Services.prefs.getIntPref("browser.startup.page") == BROWSER_STARTUP_RESUME_SESSION;
-
     // If this is a normal restore then throw away any previous session
-    if (!shouldResumeSessionOnce && this._initialState) {
+    if (!this._resumeSessionEnabled && this._initialState) {
       delete this._initialState.lastSessionState;
     }
 
     let resumeFromCrash = Services.prefs.getBoolPref("browser.sessionstore.resume_from_crash");
 
     CrashMonitor.previousCheckpoints.then(checkpoints => {
       if (checkpoints) {
         // If the previous session finished writing the final state, we'll
@@ -211,17 +213,17 @@ SessionStartup.prototype = {
       // Report shutdown success via telemetry. Shortcoming here are
       // being-killed-by-OS-shutdown-logic, shutdown freezing after
       // session restore was written, etc.
       Services.telemetry.getHistogramById("SHUTDOWN_OK").add(!this._previousSessionCrashed);
 
       // set the startup type
       if (this._previousSessionCrashed && resumeFromCrash)
         this._sessionType = Ci.nsISessionStartup.RECOVER_SESSION;
-      else if (!this._previousSessionCrashed && shouldResumeSession)
+      else if (!this._previousSessionCrashed && this._resumeSessionEnabled)
         this._sessionType = Ci.nsISessionStartup.RESUME_SESSION;
       else if (this._initialState)
         this._sessionType = Ci.nsISessionStartup.DEFER_SESSION;
       else
         this._initialState = null; // reset the state
 
       Services.obs.addObserver(this, "sessionstore-windows-restored", true);
 
@@ -307,46 +309,41 @@ SessionStartup.prototype = {
    * @returns bool
    */
   _willRestore() {
     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;
-  },
-
-  /**
-   * Returns a promise that resolves to a boolean indicating 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.
+   * Returns a promise that resolves to a boolean, indicating whether we will
+   * restore a session that ends up replacing the homepage. True guarantees
+   * that we'll restore a session; false means that we /probably/ won't do so.
+   * The browser uses this to avoid unnecessarily loading the homepage when
+   * restoring a session.
    */
   get willOverrideHomepagePromise() {
+    // If the session file hasn't been read yet and resuming the session isn't
+    // enabled via prefs, go ahead and load the homepage. We may still replace
+    // it when recovering from a crash, which we'll only know after reading the
+    // session file, but waiting for that would delay loading the homepage in
+    // the non-crash case.
+    if (!this._initialState && !this._resumeSessionEnabled) {
+      return Promise.resolve(false);
+    }
+
     return new Promise(resolve => {
-      resolve(this.willOverrideHomepage);
+      this.onceInitialized.then(() => {
+        // If there are valid windows with not only pinned tabs, signal that we
+        // will override the default homepage by restoring a session.
+        resolve(this._willRestore() &&
+                this._initialState &&
+                this._initialState.windows &&
+                this._initialState.windows.some(w => w.tabs.some(t => !t.pinned)));
+      });
     });
   },
 
   /**
    * Get the type of pending session store, if any.
    */
   get sessionType() {
     return this._sessionType;
--- a/dom/tests/mochitest/localstorage/test_localStorageQuotaPrivateBrowsing_perwindowpb.html
+++ b/dom/tests/mochitest/localstorage/test_localStorageQuotaPrivateBrowsing_perwindowpb.html
@@ -146,40 +146,34 @@ function onMessageReceived(event, aWindo
       break;
     // Any other message indicates error or succes message of a test
     default:
       SimpleTest.ok(!event.data.match(failureRegExp), event.data);
       break;
   }
 }
 
-function whenDelayedStartupFinished(aWindow, aCallback) {
+function whenDelayedStartupFinished(aCallback) {
   Services.obs.addObserver(function observer(aSubject, aTopic) {
-    if (aWindow == aSubject) {
-      Services.obs.removeObserver(observer, aTopic);
-    }
+    Services.obs.removeObserver(observer, aTopic);
 
-    if (aWindow.content == null || aWindow.content.location.href != CONTENT_PAGE ) {
-      aWindow.addEventListener("DOMContentLoaded", function() {
-        SimpleTest.executeSoon(function() { aCallback(aWindow); });
-      }, {capture: true, once: true});
-
-      aWindow.gBrowser.loadURI(CONTENT_PAGE);
-    }
+    aSubject.addEventListener("DOMContentLoaded", function() {
+      SimpleTest.executeSoon(function() { aCallback(aSubject); });
+    }, {capture: true, once: true});
   }, "browser-delayed-startup-finished");
 }
 
 function testOnWindow(aIsPrivate, callback) {
   var mainWindow = window.QueryInterface(Ci.nsIInterfaceRequestor)
                          .getInterface(Ci.nsIWebNavigation)
                          .QueryInterface(Ci.nsIDocShellTreeItem)
                          .rootTreeItem
                          .QueryInterface(Ci.nsIInterfaceRequestor)
                          .getInterface(Ci.nsIDOMWindow);
 
-  var win = mainWindow.OpenBrowserWindow({private: aIsPrivate});
-  whenDelayedStartupFinished(win, function() { callback(win); });
+  mainWindow.openUILinkIn(CONTENT_PAGE, "window", {private: aIsPrivate});
+  whenDelayedStartupFinished(callback);
 };
 </script>
 </head>
 <body onload="startTest();">
 </body>
 </html>