Bug 1279443 - Don't capture session state during startup before we've restored history. r=sebastian draft
authorJan Henning <jh+bugzilla@buttercookie.de>
Wed, 29 Jun 2016 18:24:13 +0200
changeset 385203 682cda2728f09f8f152d21462f6218b5dff42ebe
parent 385089 b644837b2019bc94c4371c3eea8f58065713eec7
child 524869 0b28bc5ebde1e825f9a0343ee52dd0553a51dde6
push id22445
push usermozilla@buttercookie.de
push dateThu, 07 Jul 2016 20:08:17 +0000
reviewerssebastian
bugs1279443
milestone50.0a1
Bug 1279443 - Don't capture session state during startup before we've restored history. r=sebastian When restoring tabs on startup, the Java UI creates tab stubs for the tabs from the previous session. The selected foreground tab then starts loading as soon as Gecko is up and running. Meanwhile, the session store gets initialised, too and starts restoring history and other things for that tab. After history has been restored for an active tab, the session store reloads the current history entry, however by that time, depending on device speed, page size and how many other tabs the session store has to process during startup, the initial page load might have progressed far enough to have already triggered various events monitored by the session store, e.g. "pageshow". If those events arrive before tab restoring has finished, the session store will attempt to capture that tab's state, which will overwrite the values stored from the previous session. Once the page is then reloaded for restoring, wrong values (e.g. form data, scroll position, zoom level) might then be restored. Therefore, we now abort any attempts to capture a tab's state - for all tabs until the "sessionstore-windows-restored" notification has been received as a signal that the initial session restore during startup has finished - for the restored foreground tab until the location change notification is received after reloading MozReview-Commit-ID: HbhXcEUnRXQ
mobile/android/components/SessionStore.js
--- a/mobile/android/components/SessionStore.js
+++ b/mobile/android/components/SessionStore.js
@@ -77,16 +77,17 @@ SessionStore.prototype = {
 
     // Get file references
     this._sessionFile = Services.dirsvc.get("ProfD", Ci.nsILocalFile);
     this._sessionFileBackup = this._sessionFile.clone();
     this._sessionFile.append("sessionstore.js");
     this._sessionFileBackup.append("sessionstore.bak");
 
     this._loadState = STATE_STOPPED;
+    this._startupRestoreFinished = false;
 
     this._interval = Services.prefs.getIntPref("browser.sessionstore.interval");
     this._maxTabsUndo = Services.prefs.getIntPref("browser.sessionstore.max_tabs_undo");
 
     // Copy changes in Gecko settings to their Java counterparts,
     // so the startup code can access them
     Services.prefs.addObserver(PREFS_RESTORE_FROM_CRASH, function() {
       SharedPreferences.forApp().setBoolPref(PREFS_RESTORE_FROM_CRASH,
@@ -209,31 +210,43 @@ SessionStore.prototype = {
             observe: function (aSubject, aTopic, aData) {
               Services.obs.removeObserver(restoreCleanup, "sessionstore-windows-restored");
 
               if (window.BrowserApp.tabs.length == 0) {
                 window.BrowserApp.addTab("about:home", {
                   selected: true
                 });
               }
+              // Normally, _restoreWindow() will have set this to true already,
+              // but we want to make sure it's set even in case of a restore failure.
+              this._startupRestoreFinished = true;
+              log("startupRestoreFinished = true (through notification)");
             }.bind(this)
           };
           Services.obs.addObserver(restoreCleanup, "sessionstore-windows-restored", false);
 
           // Do a restore, triggered by Java
           let data = JSON.parse(aData);
           this.restoreLastSession(data.sessionString);
         } else {
           // Not doing a restore; just send restore message
+          this._startupRestoreFinished = true;
+          log("startupRestoreFinished = true");
           Services.obs.notifyObservers(null, "sessionstore-windows-restored", "");
         }
         break;
       }
       case "Session:NotifyLocationChange": {
         let browser = aSubject;
+
+        if (browser.__SS_restoreReloadPending && this._startupRestoreFinished) {
+          delete browser.__SS_restoreReloadPending;
+          log("remove restoreReloadPending");
+        }
+
         if (browser.__SS_restoreDataOnLocationChange) {
           delete browser.__SS_restoreDataOnLocationChange;
           this._restoreZoom(browser.__SS_data.scrolldata, browser);
         }
         break;
       }
       case "Tabs:OpenMultiple": {
         let data = JSON.parse(aData);
@@ -537,18 +550,19 @@ SessionStore.prototype = {
 
       log("onTabClose() ran for tab " + aWindow.BrowserApp.getTabForBrowser(aBrowser).id);
       let evt = new Event("SSTabCloseProcessed", {"bubbles":true, "cancelable":false});
       aBrowser.dispatchEvent(evt);
     }
   },
 
   onTabLoad: function ss_onTabLoad(aWindow, aBrowser) {
-    // If this browser is being restored, skip any session save activity
-    if (aBrowser.__SS_restore) {
+    // If this browser belongs to a zombie tab or the initial restore hasn't yet finished,
+    // skip any session save activity.
+    if (aBrowser.__SS_restore || !this._startupRestoreFinished || aBrowser.__SS_restoreReloadPending) {
       return;
     }
 
     // Ignore a transient "about:blank"
     if (!aBrowser.canGoBack && aBrowser.currentURI.spec == "about:blank") {
       return;
     }
 
@@ -632,18 +646,19 @@ SessionStore.prototype = {
     // notifications, we may have switched between different private browsing
     // modes.
     if (this._notifyClosedTabs) {
       this._sendClosedTabsToJava(aWindow);
     }
   },
 
   onTabInput: function ss_onTabInput(aWindow, aBrowser) {
-    // If this browser is being restored, skip any session save activity
-    if (aBrowser.__SS_restore) {
+    // If this browser belongs to a zombie tab or the initial restore hasn't yet finished,
+    // skip any session save activity.
+    if (aBrowser.__SS_restore || !this._startupRestoreFinished || aBrowser.__SS_restoreReloadPending) {
       return;
     }
 
     // Don't bother trying to save text data if we don't have history yet
     let data = aBrowser.__SS_data;
     if (!data || data.entries.length == 0) {
       return;
     }
@@ -691,18 +706,19 @@ SessionStore.prototype = {
   onTabScroll: function ss_onTabScroll(aWindow, aBrowser) {
     // If we've been called directly, cancel any pending timeouts.
     if (this._scrollSavePending) {
       aWindow.clearTimeout(this._scrollSavePending);
       this._scrollSavePending = null;
       log("onTabScroll() clearing pending timeout");
     }
 
-    // If this browser is being restored, skip any session save activity.
-    if (aBrowser.__SS_restore) {
+    // If this browser belongs to a zombie tab or the initial restore hasn't yet finished,
+    // skip any session save activity.
+    if (aBrowser.__SS_restore || !this._startupRestoreFinished || aBrowser.__SS_restoreReloadPending) {
       return;
     }
 
     // Don't bother trying to save scroll positions if we don't have history yet.
     let data = aBrowser.__SS_data;
     if (!data || data.entries.length == 0) {
       return;
     }
@@ -1474,16 +1490,23 @@ SessionStore.prototype = {
       }
 
       tab.browser.__SS_data = tabData;
       tab.browser.__SS_extdata = tabData.extData;
 
       if (window.BrowserApp.selectedTab == tab) {
         this._restoreTab(tabData, tab.browser);
 
+        // We can now lift the general ban on tab data capturing,
+        // but we still need to protect the foreground tab until we're
+        // sure it's actually reloading after history restoring has finished.
+        tab.browser.__SS_restoreReloadPending = true;
+        this._startupRestoreFinished = true;
+        log("startupRestoreFinished = true");
+
         delete tab.browser.__SS_restore;
         tab.browser.removeAttribute("pending");
       } else {
         // Mark the browser for delay loading
         tab.browser.__SS_restore = true;
         tab.browser.setAttribute("pending", "true");
       }
     }