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
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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);
     }
   }