Bug 867142 - Remove browser.__SS_restoreState and use a WeakMap instead; r=yoric
authorTim Taubert <ttaubert@mozilla.com>
Tue, 21 May 2013 15:02:46 +0200
changeset 132526 d1cd5199bf45
parent 132427 957f5f047a94
child 132527 2d58f11fc294
push id24708
push userttaubert@mozilla.com
push date2013-05-22 07:05 +0000
treeherdermozilla-central@c21ef3664c67 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersyoric
bugs867142
milestone24.0a1
Bug 867142 - Remove browser.__SS_restoreState and use a WeakMap instead; r=yoric
browser/components/sessionstore/src/SessionStore.jsm
browser/components/sessionstore/test/browser_595601-restore_hidden.js
browser/components/sessionstore/test/browser_607016.js
browser/components/sessionstore/test/browser_636279.js
browser/components/sessionstore/test/browser_739805.js
browser/components/sessionstore/test/head.js
browser/components/tabview/test/browser_tabview_bug595601.js
browser/components/tabview/test/head.js
--- a/browser/components/sessionstore/src/SessionStore.jsm
+++ b/browser/components/sessionstore/src/SessionStore.jsm
@@ -11,19 +11,16 @@ const Cr = Components.results;
 
 const STATE_STOPPED = 0;
 const STATE_RUNNING = 1;
 const STATE_QUITTING = -1;
 
 const STATE_STOPPED_STR = "stopped";
 const STATE_RUNNING_STR = "running";
 
-const TAB_STATE_NEEDS_RESTORE = 1;
-const TAB_STATE_RESTORING = 2;
-
 const PRIVACY_NONE = 0;
 const PRIVACY_ENCRYPTED = 1;
 const PRIVACY_FULL = 2;
 
 const NOTIFY_WINDOWS_RESTORED = "sessionstore-windows-restored";
 const NOTIFY_BROWSER_STATE_RESTORED = "sessionstore-browser-state-restored";
 
 // Maximum number of tabs to restore simultaneously. Previously controlled by
@@ -230,16 +227,39 @@ this.SessionStore = {
   },
 
   restoreLastSession: function ss_restoreLastSession() {
     SessionStoreInternal.restoreLastSession();
   },
 
   checkPrivacyLevel: function ss_checkPrivacyLevel(aIsHTTPS, aUseDefaultPref) {
     return SessionStoreInternal.checkPrivacyLevel(aIsHTTPS, aUseDefaultPref);
+  },
+
+  /**
+   * Returns whether a given browser is waiting to be restored. That means its
+   * history and state is ready but we wait until it's higher up in the priority
+   * queue or until it's made visible (if restore_on_demand=true).
+   *
+   * @param aBrowser Browser reference
+   * @returns bool
+   */
+  isTabStateNeedsRestore: function ss_isTabStateNeedsRestore(aBrowser) {
+    return TabRestoreStates.isNeedsRestore(aBrowser);
+  },
+
+  /**
+   * Returns whether a given browser is currently restoring, i.e. we wait for
+   * the actual page to load and will restore form data when it's finished.
+   *
+   * @param aBrowser Browser reference
+   * @returns bool
+   */
+  isTabStateRestoring: function ss_isTabStateRestoring(aBrowser) {
+    return TabRestoreStates.isRestoring(aBrowser);
   }
 };
 
 // Freeze the SessionStore object. We don't want anyone to modify it.
 Object.freeze(SessionStore);
 
 let SessionStoreInternal = {
   QueryInterface: XPCOMUtils.generateQI([
@@ -1052,17 +1072,17 @@ let SessionStoreInternal = {
       return;
     this._lastSessionState = null;
     let openWindows = {};
     this._forEachBrowserWindow(function(aWindow) {
       Array.forEach(aWindow.gBrowser.tabs, aTab => {
         RestoringTabsData.remove(aTab.linkedBrowser);
         delete aTab.linkedBrowser.__SS_formDataSaved;
         delete aTab.linkedBrowser.__SS_hostSchemeData;
-        if (aTab.linkedBrowser.__SS_restoreState)
+        if (TabRestoreStates.has(aTab.linkedBrowser))
           this._resetTabRestoringState(aTab);
       });
       openWindows[aWindow.__SSi] = true;
     });
     // also clear all data about closed tabs and windows
     for (let ix in this._windows) {
       if (ix in openWindows) {
         this._windows[ix]._closedTabs = [];
@@ -1247,20 +1267,20 @@ let SessionStoreInternal = {
 
     RestoringTabsData.remove(aTab.linkedBrowser);
     delete browser.__SS_formDataSaved;
     delete browser.__SS_hostSchemeData;
 
     // If this tab was in the middle of restoring or still needs to be restored,
     // we need to reset that state. If the tab was restoring, we will attempt to
     // restore the next tab.
-    let previousState = browser.__SS_restoreState;
-    if (previousState) {
+    if (TabRestoreStates.has(browser)) {
+      let wasRestoring = TabRestoreStates.isRestoring(browser);
       this._resetTabRestoringState(aTab);
-      if (previousState == TAB_STATE_RESTORING)
+      if (wasRestoring)
         this.restoreNextTab();
     }
 
     if (!aNoNotification) {
       this.saveStateDelayed(aWindow);
     }
   },
 
@@ -1312,18 +1332,17 @@ let SessionStoreInternal = {
    * @param aBrowser
    *        Browser reference
    */
   onTabLoad: function ssi_onTabLoad(aWindow, aBrowser) {
     // react on "load" and solitary "pageshow" events (the first "pageshow"
     // following "load" is too late for deleting the data caches)
     // It's possible to get a load event after calling stop on a browser (when
     // overwriting tabs). We want to return early if the tab hasn't been restored yet.
-    if (aBrowser.__SS_restoreState &&
-        aBrowser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE) {
+    if (TabRestoreStates.isNeedsRestore(aBrowser)) {
       return;
     }
 
     RestoringTabsData.remove(aBrowser);
     delete aBrowser.__SS_formDataSaved;
     this.saveStateDelayed(aWindow);
 
     // attempt to update the current URL we send in a crash report
@@ -1349,48 +1368,43 @@ let SessionStoreInternal = {
    * @param aWindow
    *        Window reference
    */
   onTabSelect: function ssi_onTabSelect(aWindow) {
     if (this._loadState == STATE_RUNNING) {
       this._windows[aWindow.__SSi].selected = aWindow.gBrowser.tabContainer.selectedIndex;
 
       let tab = aWindow.gBrowser.selectedTab;
-      // If __SS_restoreState is still on the browser and it is
-      // TAB_STATE_NEEDS_RESTORE, then then we haven't restored
-      // this tab yet. Explicitly call restoreTab to kick off the restore.
-      if (tab.linkedBrowser.__SS_restoreState &&
-          tab.linkedBrowser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE)
+      // Explicitly call restoreTab() to to restore the tab if we need to.
+      if (TabRestoreStates.isNeedsRestore(tab.linkedBrowser))
         this.restoreTab(tab);
 
       // attempt to update the current URL we send in a crash report
       this._updateCrashReportURL(aWindow);
     }
   },
 
   onTabShow: function ssi_onTabShow(aWindow, aTab) {
     // If the tab hasn't been restored yet, move it into the right bucket
-    if (aTab.linkedBrowser.__SS_restoreState &&
-        aTab.linkedBrowser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE) {
+    if (TabRestoreStates.isNeedsRestore(aTab.linkedBrowser)) {
       TabRestoreQueue.hiddenToVisible(aTab);
 
       // let's kick off tab restoration again to ensure this tab gets restored
       // with "restore_hidden_tabs" == false (now that it has become visible)
       this.restoreNextTab();
     }
 
     // Default delay of 2 seconds gives enough time to catch multiple TabShow
     // events due to changing groups in Panorama.
     this.saveStateDelayed(aWindow);
   },
 
   onTabHide: function ssi_onTabHide(aWindow, aTab) {
     // If the tab hasn't been restored yet, move it into the right bucket
-    if (aTab.linkedBrowser.__SS_restoreState &&
-        aTab.linkedBrowser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE) {
+    if (TabRestoreStates.isNeedsRestore(aTab.linkedBrowser)) {
       TabRestoreQueue.visibleToHidden(aTab);
     }
 
     // Default delay of 2 seconds gives enough time to catch multiple TabHide
     // events due to changing groups in Panorama.
     this.saveStateDelayed(aWindow);
   },
 
@@ -2751,17 +2765,17 @@ let SessionStoreInternal = {
     }
 
     // If overwriting tabs, we want to reset each tab's "restoring" state. Since
     // we're overwriting those tabs, they should no longer be restoring. The
     // tabs will be rebuilt and marked if they need to be restored after loading
     // state (in restoreHistoryPrecursor).
     if (aOverwriteTabs) {
       for (let i = 0; i < tabbrowser.tabs.length; i++) {
-        if (tabbrowser.browsers[i].__SS_restoreState)
+        if (TabRestoreStates.has(tabbrowser.browsers[i]))
           this._resetTabRestoringState(tabbrowser.tabs[i]);
       }
     }
 
     // We want to set up a counter on the window that indicates how many tabs
     // in this window are unrestored. This will be used in restoreNextTab to
     // determine if gRestoreTabsProgressListener should be removed from the window.
     // If we aren't overwriting existing tabs, then we want to add to the existing
@@ -2981,17 +2995,17 @@ let SessionStoreInternal = {
       if ("attributes" in tabData) {
         // Ensure that we persist tab attributes restored from previous sessions.
         Object.keys(tabData.attributes).forEach(a => TabAttributes.persist(a));
       }
 
       // keep the data around to prevent dataloss in case
       // a tab gets closed before it's been properly restored
       RestoringTabsData.set(browser, tabData);
-      browser.__SS_restoreState = TAB_STATE_NEEDS_RESTORE;
+      TabRestoreStates.setNeedsRestore(browser);
       browser.setAttribute("pending", "true");
       tab.setAttribute("pending", "true");
 
       // Make sure that set/getTabValue will set/read the correct data by
       // wiping out any current value in tab.__SS_extdata.
       delete tab.__SS_extdata;
 
       if (!tabData.entries || tabData.entries.length == 0) {
@@ -3171,17 +3185,17 @@ let SessionStoreInternal = {
 
     // Make sure that this tab is removed from the priority queue.
     TabRestoreQueue.remove(aTab);
 
     // Increase our internal count.
     this._tabsRestoringCount++;
 
     // Set this tab's state to restoring
-    browser.__SS_restoreState = TAB_STATE_RESTORING;
+    TabRestoreStates.setIsRestoring(browser);
     browser.removeAttribute("pending");
     aTab.removeAttribute("pending");
 
     // Remove the history listener, since we no longer need it once we start restoring
     this._removeSHistoryListener(aTab);
 
     let activeIndex = (tabData.index || tabData.entries.length) - 1;
     if (activeIndex >= tabData.entries.length)
@@ -4393,36 +4407,37 @@ let SessionStoreInternal = {
    * @param aTab
    *        The tab that will be "reset"
    */
   _resetTabRestoringState: function ssi_resetTabRestoringState(aTab) {
     let window = aTab.ownerDocument.defaultView;
     let browser = aTab.linkedBrowser;
 
     // Keep the tab's previous state for later in this method
-    let previousState = browser.__SS_restoreState;
+    let wasRestoring = TabRestoreStates.isRestoring(browser);
+    let wasNeedsRestore = TabRestoreStates.isNeedsRestore(browser);
 
     // The browser is no longer in any sort of restoring state.
-    delete browser.__SS_restoreState;
+    TabRestoreStates.remove(browser);
 
     aTab.removeAttribute("pending");
     browser.removeAttribute("pending");
 
     // We want to decrement window.__SS_tabsToRestore here so that we always
     // decrement it AFTER a tab is done restoring or when a tab gets "reset".
     window.__SS_tabsToRestore--;
 
     // Remove the progress listener if we should.
     this._removeTabsProgressListener(window);
 
-    if (previousState == TAB_STATE_RESTORING) {
+    if (wasRestoring) {
       if (this._tabsRestoringCount)
         this._tabsRestoringCount--;
     }
-    else if (previousState == TAB_STATE_NEEDS_RESTORE) {
+    else if (wasNeedsRestore) {
       // Make sure the session history listener is removed. This is normally
       // done in restoreTab, but this tab is being removed before that gets called.
       this._removeSHistoryListener(aTab);
 
       // Make sure that the tab is removed from the list of tabs to restore.
       // Again, this is normally done in restoreTab, but that isn't being called
       // for this tab.
       TabRestoreQueue.remove(aTab);
@@ -4665,25 +4680,59 @@ let TabAttributes = {
 
     // Set attributes.
     for (let name in data) {
       tab.setAttribute(name, data[name]);
     }
   }
 };
 
+// A map keeping track of all tab restore states. A tab might be 'needs-restore'
+// if it waits until the restoration process is kicked off. This might start
+// when the tab reaches a higher position in the priority queue or when it's
+// made visible (when restore_on_demand=true). If a tab is 'restoring' we wait
+// for its actual page to load and will then restore form data etc. If has()
+// returns false the tab has not been restored from previous data or it has
+// already finished restoring and is thus now seen as a valid and complete tab.
+let TabRestoreStates = {
+  _states: new WeakMap(),
+
+  has: function (browser) {
+    return this._states.has(browser);
+  },
+
+  isNeedsRestore: function ss_isNeedsRestore(browser) {
+    return this._states.get(browser) === "needs-restore";
+  },
+
+  setNeedsRestore: function (browser) {
+    this._states.set(browser, "needs-restore");
+  },
+
+  isRestoring: function ss_isRestoring(browser) {
+    return this._states.get(browser) === "restoring";
+  },
+
+  setIsRestoring: function (browser) {
+    this._states.set(browser, "restoring");
+  },
+
+  remove: function (browser) {
+    this._states.delete(browser);
+  }
+};
+
 // This is used to help meter the number of restoring tabs. This is the control
 // point for telling the next tab to restore. It gets attached to each gBrowser
 // via gBrowser.addTabsProgressListener
 let gRestoreTabsProgressListener = {
   onStateChange: function(aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
     // Ignore state changes on browsers that we've already restored and state
     // changes that aren't applicable.
-    if (aBrowser.__SS_restoreState &&
-        aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
+    if (TabRestoreStates.isRestoring(aBrowser) &&
         aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
         aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
         aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW) {
       // We need to reset the tab before starting the next restore.
       let tab = SessionStoreInternal._getTabForBrowser(aBrowser);
       SessionStoreInternal._resetTabRestoringState(tab);
       SessionStoreInternal.restoreNextTab();
     }
--- a/browser/components/sessionstore/test/browser_595601-restore_hidden.js
+++ b/browser/components/sessionstore/test/browser_595601-restore_hidden.js
@@ -68,31 +68,31 @@ let TabsProgressListener = {
     delete this.callback;
   },
 
   setCallback: function (callback) {
     this.callback = callback;
   },
 
   onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
-    if (this.callback && aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
+    if (this.callback && SessionStore.isTabStateRestoring(aBrowser) &&
         aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
         aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
         aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
       this.callback.apply(null, [this.window].concat(this.countTabs()));
   },
 
   countTabs: function () {
     let needsRestore = 0, isRestoring = 0;
 
     for (let i = 0; i < this.window.gBrowser.tabs.length; i++) {
       let browser = this.window.gBrowser.tabs[i].linkedBrowser;
-      if (browser.__SS_restoreState == TAB_STATE_RESTORING)
+      if (SessionStore.isTabStateRestoring(browser))
         isRestoring++;
-      else if (browser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE)
+      else if (SessionStore.isTabStateNeedsRestore(browser))
         needsRestore++;
     }
 
     return [needsRestore, isRestoring];
   }
 }
 
 // ----------
--- a/browser/components/sessionstore/test/browser_607016.js
+++ b/browser/components/sessionstore/test/browser_607016.js
@@ -17,40 +17,29 @@ function test() {
   /** Bug 607016 - If a tab is never restored, attributes (eg. hidden) aren't updated correctly **/
   waitForExplicitFinish();
   ignoreAllUncaughtExceptions();
 
   // Set the pref to true so we know exactly how many tabs should be restoring at
   // any given time. This guarantees that a finishing load won't start another.
   Services.prefs.setBoolPref("browser.sessionstore.restore_on_demand", true);
 
-  // We have our own progress listener for this test, which we'll attach before our state is set
-  let progressListener = {
-    onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
-      if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
-          aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
-          aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
-          aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
-        progressCallback(aBrowser);
-    }
-  }
-
   let state = { windows: [{ tabs: [
     { entries: [{ url: "http://example.org#1" }], extData: { "uniq": r() } },
     { entries: [{ url: "http://example.org#2" }], extData: { "uniq": r() } }, // overwriting
     { entries: [{ url: "http://example.org#3" }], extData: { "uniq": r() } }, // hiding
     { entries: [{ url: "http://example.org#4" }], extData: { "uniq": r() } }, // adding
     { entries: [{ url: "http://example.org#5" }], extData: { "uniq": r() } }, // deleting
     { entries: [{ url: "http://example.org#6" }] } // creating
   ], selected: 1 }] };
 
-  function progressCallback(aBrowser) {
+  gProgressListener.setCallback(function progressCallback(aBrowser) {
     // We'll remove the progress listener after the first one because we aren't
     // loading any other tabs
-    window.gBrowser.removeTabsProgressListener(progressListener);
+    gProgressListener.unsetCallback();
 
     let curState = JSON.parse(ss.getBrowserState());
     for (let i = 0; i < curState.windows[0].tabs.length; i++) {
       let tabState = state.windows[0].tabs[i];
       let tabCurState = curState.windows[0].tabs[i];
       if (tabState.extData) {
         is(tabCurState.extData["uniq"], tabState.extData["uniq"],
            "sanity check that tab has correct extData");
@@ -104,14 +93,13 @@ function test() {
     let newUniq2 = r();
     ss.setTabValue(gBrowser.tabs[1], "uniq", newUniq2);
     gBrowser.removeTab(gBrowser.tabs[1]);
     closedTabData = (JSON.parse(ss.getClosedTabData(window)))[0];
     is(closedTabData.state.extData.uniq, newUniq2,
        "(creating) new data is stored in extData where there was none");
 
     cleanup();
-  }
+  });
 
-  window.gBrowser.addTabsProgressListener(progressListener);
   ss.setBrowserState(JSON.stringify(state));
 }
 
--- a/browser/components/sessionstore/test/browser_636279.js
+++ b/browser/components/sessionstore/test/browser_636279.js
@@ -13,87 +13,35 @@ let state = {windows:[{tabs:[
   {entries:[{url:"http://example.com#3"}]},
   {entries:[{url:"http://example.com#4"}]},
 ]}]};
 
 function test() {
   waitForExplicitFinish();
 
   registerCleanupFunction(function () {
-    TabsProgressListener.uninit();
     ss.setBrowserState(stateBackup);
   });
 
-
-  TabsProgressListener.init();
-
   window.addEventListener("SSWindowStateReady", function onReady() {
     window.removeEventListener("SSWindowStateReady", onReady, false);
 
     let firstProgress = true;
 
-    TabsProgressListener.setCallback(function (needsRestore, isRestoring) {
+    gProgressListener.setCallback(function (browser, needsRestore, isRestoring) {
       if (firstProgress) {
         firstProgress = false;
         is(isRestoring, 3, "restoring 3 tabs concurrently");
       } else {
         ok(isRestoring <= 3, "restoring max. 2 tabs concurrently");
       }
 
       if (0 == needsRestore) {
-        TabsProgressListener.unsetCallback();
+        gProgressListener.unsetCallback();
         waitForFocus(finish);
       }
     });
 
     ss.setBrowserState(JSON.stringify(state));
   }, false);
 
   ss.setBrowserState(JSON.stringify(statePinned));
 }
-
-function countTabs() {
-  let needsRestore = 0, isRestoring = 0;
-  let windowsEnum = Services.wm.getEnumerator("navigator:browser");
-
-  while (windowsEnum.hasMoreElements()) {
-    let window = windowsEnum.getNext();
-    if (window.closed)
-      continue;
-
-    for (let i = 0; i < window.gBrowser.tabs.length; i++) {
-      let browser = window.gBrowser.tabs[i].linkedBrowser;
-      if (browser.__SS_restoreState == TAB_STATE_RESTORING)
-        isRestoring++;
-      else if (browser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE)
-        needsRestore++;
-    }
-  }
-
-  return [needsRestore, isRestoring];
-}
-
-let TabsProgressListener = {
-  init: function () {
-    gBrowser.addTabsProgressListener(this);
-  },
-
-  uninit: function () {
-    this.unsetCallback();
-    gBrowser.removeTabsProgressListener(this);
- },
-
-  setCallback: function (callback) {
-    this.callback = callback;
-  },
-
-  unsetCallback: function () {
-    delete this.callback;
-  },
-
-  onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
-    if (this.callback && aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
-        aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
-        aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
-        aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
-      this.callback.apply(null, countTabs());
-  }
-}
--- a/browser/components/sessionstore/test/browser_739805.js
+++ b/browser/components/sessionstore/test/browser_739805.js
@@ -17,17 +17,17 @@ function test() {
 
   let tab = gBrowser.addTab("about:blank");
   let browser = tab.linkedBrowser;
 
   whenBrowserLoaded(browser, function () {
     isnot(gBrowser.selectedTab, tab, "newly created tab is not selected");
 
     ss.setTabState(tab, JSON.stringify(tabState));
-    is(browser.__SS_restoreState, TAB_STATE_NEEDS_RESTORE, "tab needs restoring");
+    ok(SessionStore.isTabStateNeedsRestore(browser), "tab needs restoring");
 
     let state = JSON.parse(ss.getTabState(tab));
     let formdata = state.entries[0].formdata;
     is(formdata && formdata.id["foo"], "bar", "tab state's formdata is valid");
 
     whenTabRestored(tab, function () {
       let input = browser.contentDocument.getElementById("foo");
       is(input.value, "bar", "formdata has been restored correctly");
--- a/browser/components/sessionstore/test/head.js
+++ b/browser/components/sessionstore/test/head.js
@@ -1,15 +1,12 @@
 /* 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/. */
 
-const TAB_STATE_NEEDS_RESTORE = 1;
-const TAB_STATE_RESTORING = 2;
-
 let tmp = {};
 Cu.import("resource:///modules/sessionstore/SessionStore.jsm", tmp);
 let SessionStore = tmp.SessionStore;
 
 let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
 
 // Some tests here assume that all restored tabs are loaded without waiting for
 // the user to bring them to the foreground. We ensure this by resetting the
@@ -236,34 +233,34 @@ let gProgressListener = {
       window.gBrowser.removeTabsProgressListener(this);
     }
   },
 
   onStateChange:
   function gProgressListener_onStateChange(aBrowser, aWebProgress, aRequest,
                                            aStateFlags, aStatus) {
     if ((!this._checkRestoreState ||
-         aBrowser.__SS_restoreState == TAB_STATE_RESTORING) &&
+         SessionStore.isTabStateRestoring(aBrowser)) &&
         aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
         aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
         aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW) {
       let args = [aBrowser].concat(this._countTabs());
       this._callback.apply(this, args);
     }
   },
 
   _countTabs: function gProgressListener_countTabs() {
     let needsRestore = 0, isRestoring = 0, wasRestored = 0;
 
     for (let win in BrowserWindowIterator()) {
       for (let i = 0; i < win.gBrowser.tabs.length; i++) {
         let browser = win.gBrowser.tabs[i].linkedBrowser;
-        if (browser.__SS_restoreState == TAB_STATE_RESTORING)
+        if (SessionStore.isTabStateRestoring(browser))
           isRestoring++;
-        else if (browser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE)
+        else if (SessionStore.isTabStateNeedsRestore(browser))
           needsRestore++;
         else
           wasRestored++;
       }
     }
     return [needsRestore, isRestoring, wasRestored];
   }
 };
--- a/browser/components/tabview/test/browser_tabview_bug595601.js
+++ b/browser/components/tabview/test/browser_tabview_bug595601.js
@@ -1,16 +1,13 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
 
-const TAB_STATE_NEEDS_RESTORE = 1;
-const TAB_STATE_RESTORING = 2;
-
 let stateBackup = ss.getBrowserState();
 
 let state = {windows:[{tabs:[
   // first group
   {entries:[{url:"http://example.com#1"}],extData:{"tabview-tab":"{\"bounds\":{\"left\":20,\"top\":20,\"width\":20,\"height\":20},\"url\":\"http://example.com#1\",\"groupID\":2}"}},
   {entries:[{url:"http://example.com#2"}],extData:{"tabview-tab":"{\"bounds\":{\"left\":20,\"top\":20,\"width\":20,\"height\":20},\"url\":\"http://example.com#2\",\"groupID\":2}"}},
   {entries:[{url:"http://example.com#3"}],extData:{"tabview-tab":"{\"bounds\":{\"left\":20,\"top\":20,\"width\":20,\"height\":20},\"url\":\"http://example.com#3\",\"groupID\":2}"}},
   {entries:[{url:"http://example.com#4"}],extData:{"tabview-tab":"{\"bounds\":{\"left\":20,\"top\":20,\"width\":20,\"height\":20},\"url\":\"http://example.com#4\",\"groupID\":2}"}},
@@ -120,19 +117,19 @@ function countTabs() {
 
   while (windowsEnum.hasMoreElements()) {
     let window = windowsEnum.getNext();
     if (window.closed)
       continue;
 
     for (let i = 0; i < window.gBrowser.tabs.length; i++) {
       let browser = window.gBrowser.tabs[i].linkedBrowser;
-      if (browser.__SS_restoreState == TAB_STATE_RESTORING)
+      if (SessionStore.isTabStateRestoring(browser))
         isRestoring++;
-      else if (browser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE)
+      else if (SessionStore.isTabStateNeedsRestore(browser))
         needsRestore++;
     }
   }
 
   return [needsRestore, isRestoring];
 }
 
 let TabsProgressListener = {
@@ -163,18 +160,19 @@ let TabsProgressListener = {
     let self = this;
     let finalize = function () {
       if (wasRestoring)
         delete aBrowser.__wasRestoring;
 
       self.callback.apply(null, countTabs());
     };
 
-    let isRestoring = aBrowser.__SS_restoreState == TAB_STATE_RESTORING;
-    let wasRestoring = !aBrowser.__SS_restoreState && aBrowser.__wasRestoring;
+    let isRestoring = SessionStore.isTabStateRestoring(aBrowser);
+    let needsRestore = SessionStore.isTabStateNeedsRestore(aBrowser);
+    let wasRestoring = !isRestoring && !needsRestore && aBrowser.__wasRestoring;
     let hasStopped = aStateFlags & Ci.nsIWebProgressListener.STATE_STOP;
 
     if (isRestoring && !hasStopped)
       aBrowser.__wasRestoring = true;
 
     if (hasStopped && (isRestoring || wasRestoring))
       finalize();
   }
--- a/browser/components/tabview/test/head.js
+++ b/browser/components/tabview/test/head.js
@@ -1,11 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
+let tmp = {};
+Cu.import("resource:///modules/sessionstore/SessionStore.jsm", tmp);
+let SessionStore = tmp.SessionStore;
+
 // Some tests here assume that all restored tabs are loaded without waiting for
 // the user to bring them to the foreground. We ensure this by resetting the
 // related preference (see the "firefox.js" defaults file for details).
 Services.prefs.setBoolPref("browser.sessionstore.restore_on_demand", false);
 registerCleanupFunction(function () {
   Services.prefs.clearUserPref("browser.sessionstore.restore_on_demand");
 });
 
@@ -112,18 +116,16 @@ function newWindowWithTabView(shownCallb
 
   whenDelayedStartupFinished(win, function () {
     showTabView(function () shownCallback(win), win);
   });
 }
 
 // ----------
 function afterAllTabsLoaded(callback, win) {
-  const TAB_STATE_NEEDS_RESTORE = 1;
-
   win = win || window;
 
   let stillToLoad = 0;
   let restoreHiddenTabs = Services.prefs.getBoolPref(
                           "browser.sessionstore.restore_hidden_tabs");
 
   function onLoad() {
     this.removeEventListener("load", onLoad, true);
@@ -132,18 +134,17 @@ function afterAllTabsLoaded(callback, wi
       executeSoon(callback);
   }
 
   for (let a = 0; a < win.gBrowser.tabs.length; a++) {
     let tab = win.gBrowser.tabs[a];
     let browser = tab.linkedBrowser;
 
     let isRestorable = !(tab.hidden && !restoreHiddenTabs &&
-                         browser.__SS_restoreState &&
-                         browser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE);
+                         SessionStore.isTabStateNeedsRestore(browser));
 
     if (isRestorable && browser.contentDocument.readyState != "complete" ||
         browser.webProgress.isLoadingDocument) {
       stillToLoad++;
       browser.addEventListener("load", onLoad, true);
     }
   }