Bug 928335 - Disable 'Restore Last Session' menuitem after restoring the last session; r=dao
authorTim Taubert <ttaubert@mozilla.com>
Tue, 22 Oct 2013 08:20:15 +0200
changeset 166840 18ccb2ac1b15fb07cd3b2efbe6f6db51c7438ce1
parent 166839 5ba7172333e2cd5b77ddd734467e871c683ca396
child 166841 677fb0cd248c525381197bd5c0c9b05082e7634f
push id428
push userbbajaj@mozilla.com
push dateTue, 28 Jan 2014 00:16:25 +0000
treeherdermozilla-release@cd72a7ff3a75 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdao
bugs928335
milestone27.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 928335 - Disable 'Restore Last Session' menuitem after restoring the last session; r=dao
browser/base/content/browser.js
browser/components/sessionstore/src/SessionStore.jsm
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1210,19 +1210,17 @@ var gBrowserInit = {
       // Bug 862519 - Backspace doesn't work in electrolysis builds.
       // We bypass the problem by disabling the backspace-to-go-back command.
       document.getElementById("cmd_handleBackspace").setAttribute("disabled", true);
       document.getElementById("key_delete").setAttribute("disabled", true);
     }
 
     SessionStore.promiseInitialized.then(() => {
       // Enable the Restore Last Session command if needed
-      if (SessionStore.canRestoreLastSession &&
-          !PrivateBrowsingUtils.isWindowPrivate(window))
-        goSetCommandEnabled("Browser:RestoreLastSession", true);
+      RestoreLastSessionObserver.init();
 
       TabView.init();
 
       setTimeout(function () { BrowserChromeTest.markAsReady(); }, 0);
     });
 
     Services.obs.notifyObservers(window, "browser-delayed-startup-finished", "");
     TelemetryTimestamps.add("delayedStartupFinished");
@@ -6996,16 +6994,36 @@ function switchToTabHavingURI(aURI, aOpe
       gBrowser.selectedBrowser.loadURI(aURI.spec);
     else
       openUILinkIn(aURI.spec, "tab");
   }
 
   return false;
 }
 
+let RestoreLastSessionObserver = {
+  init: function () {
+    if (SessionStore.canRestoreLastSession &&
+        !PrivateBrowsingUtils.isWindowPrivate(window)) {
+      Services.obs.addObserver(this, "sessionstore-last-session-cleared", true);
+      goSetCommandEnabled("Browser:RestoreLastSession", true);
+    }
+  },
+
+  observe: function () {
+    // The last session can only be restored once so there's
+    // no way we need to re-enable our menu item.
+    Services.obs.removeObserver(this, "sessionstore-last-session-cleared");
+    goSetCommandEnabled("Browser:RestoreLastSession", false);
+  },
+
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
+                                         Ci.nsISupportsWeakReference])
+};
+
 function restoreLastSession() {
   SessionStore.restoreLastSession();
 }
 
 var TabContextMenu = {
   contextTab: null,
   updateContextMenu: function updateContextMenu(aPopupMenu) {
     this.contextTab = aPopupMenu.triggerNode.localName == "tab" ?
--- a/browser/components/sessionstore/src/SessionStore.jsm
+++ b/browser/components/sessionstore/src/SessionStore.jsm
@@ -18,16 +18,17 @@ 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 NOTIFY_WINDOWS_RESTORED = "sessionstore-windows-restored";
 const NOTIFY_BROWSER_STATE_RESTORED = "sessionstore-browser-state-restored";
+const NOTIFY_LAST_SESSION_CLEARED = "sessionstore-last-session-cleared";
 
 // Maximum number of tabs to restore simultaneously. Previously controlled by
 // the browser.sessionstore.max_concurrent_tabs pref.
 const MAX_CONCURRENT_TAB_RESTORES = 3;
 
 // global notifications observed
 const OBSERVING = [
   "domwindowopened", "domwindowclosed",
@@ -324,22 +325,16 @@ let SessionStoreInternal = {
   _recentCrashes: 0,
 
   // whether the last window was closed and should be restored
   _restoreLastWindow: false,
 
   // number of tabs currently restoring
   _tabsRestoringCount: 0,
 
-  // The state from the previous session (after restoring pinned tabs). This
-  // state is persisted and passed through to the next session during an app
-  // restart to make the third party add-on warning not trash the deferred
-  // session
-  _lastSessionState: null,
-
   // When starting Firefox with a single private window, this is the place
   // where we keep the session we actually wanted to restore in case the user
   // decides to later open a non-private window as well.
   _deferredInitialState: null,
 
   // A promise resolved once initialization is complete
   _deferredInitialized: Promise.defer(),
 
@@ -356,26 +351,25 @@ let SessionStoreInternal = {
 
   /**
    * A promise fulfilled once initialization is complete.
    */
   get promiseInitialized() {
     return this._deferredInitialized.promise;
   },
 
-  /* ........ Public Getters .............. */
   get canRestoreLastSession() {
-    return !!this._lastSessionState;
+    return LastSession.canRestore;
   },
 
   set canRestoreLastSession(val) {
     // Cheat a bit; only allow false.
-    if (val)
-      return;
-    this._lastSessionState = null;
+    if (!val) {
+      LastSession.clear();
+    }
   },
 
   /**
    * Initialize the sessionstore service.
    */
   init: function () {
     if (this._initialized) {
       throw new Error("SessionStore.init() must only be called once!");
@@ -414,23 +408,25 @@ let SessionStoreInternal = {
         if (ss.sessionType == Ci.nsISessionStartup.DEFER_SESSION) {
           let [iniState, remainingState] = this._prepDataForDeferredRestore(state);
           // If we have a iniState with windows, that means that we have windows
           // with app tabs to restore.
           if (iniState.windows.length)
             state = iniState;
           else
             state = null;
-          if (remainingState.windows.length)
-            this._lastSessionState = remainingState;
+
+          if (remainingState.windows.length) {
+            LastSession.setState(remainingState);
+          }
         }
         else {
           // Get the last deferred session in case the user still wants to
           // restore it
-          this._lastSessionState = state.lastSessionState;
+          LastSession.setState(state.lastSessionState);
 
           let lastSessionCrashed =
             state.session && state.session.state &&
             state.session.state == STATE_RUNNING_STR;
           if (lastSessionCrashed) {
             this._recentCrashes = (state.session &&
                                    state.session.recentCrashes || 0) + 1;
 
@@ -1057,34 +1053,34 @@ let SessionStoreInternal = {
       // session data on disk on a restart.  It is also unnecessary to
       // perform any other sanitization processing on a restart as the
       // browser is about to exit anyway.
       Services.obs.removeObserver(this, "browser:purge-session-history");
     }
 
     if (aData != "restart") {
       // Throw away the previous session on shutdown
-      this._lastSessionState = null;
+      LastSession.clear();
     }
 
     this._loadState = STATE_QUITTING; // just to be sure
     this._uninit();
   },
 
   /**
    * On purge of session history
    */
   onPurgeSessionHistory: function ssi_onPurgeSessionHistory() {
     _SessionFile.wipe();
     // If the browser is shutting down, simply return after clearing the
     // session data on disk as this notification fires after the
     // quit-application notification so the browser is about to exit.
     if (this._loadState == STATE_QUITTING)
       return;
-    this._lastSessionState = null;
+    LastSession.clear();
     let openWindows = {};
     this._forEachBrowserWindow(function(aWindow) {
       Array.forEach(aWindow.gBrowser.tabs, function(aTab) {
         TabStateCache.delete(aTab);
         delete aTab.linkedBrowser.__SS_data;
         delete aTab.linkedBrowser.__SS_tabStillLoading;
         if (aTab.linkedBrowser.__SS_restoreState)
           this._resetTabRestoringState(aTab);
@@ -1760,17 +1756,17 @@ let SessionStoreInternal = {
   persistTabAttribute: function ssi_persistTabAttribute(aName) {
     if (TabAttributes.persist(aName)) {
       TabStateCache.clear();
       this.saveStateDelayed();
     }
   },
 
   /**
-   * Restores the session state stored in _lastSessionState. This will attempt
+   * Restores the session state stored in LastSession. This will attempt
    * to merge data into the current session. If a window was opened at startup
    * with pinned tab(s), then the remaining data from the previous session for
    * that window will be opened into that winddow. Otherwise new windows will
    * be opened.
    */
   restoreLastSession: function ssi_restoreLastSession() {
     // Use the public getter since it also checks PB mode
     if (!this.canRestoreLastSession)
@@ -1778,17 +1774,17 @@ let SessionStoreInternal = {
 
     // First collect each window with its id...
     let windows = {};
     this._forEachBrowserWindow(function(aWindow) {
       if (aWindow.__SS_lastSessionWindowID)
         windows[aWindow.__SS_lastSessionWindowID] = aWindow;
     });
 
-    let lastSessionState = this._lastSessionState;
+    let lastSessionState = LastSession.getState();
 
     // This shouldn't ever be the case...
     if (!lastSessionState.windows.length)
       throw (Components.returnCode = Cr.NS_ERROR_UNEXPECTED);
 
     // We're technically doing a restore, so set things up so we send the
     // notification when we're done. We want to send "sessionstore-browser-state-restored".
     this._restoreCount = lastSessionState.windows.length;
@@ -1857,17 +1853,17 @@ let SessionStoreInternal = {
 
     // Set data that persists between sessions
     this._recentCrashes = lastSessionState.session &&
                           lastSessionState.session.recentCrashes || 0;
 
     // Update the session start time using the restored session state.
     this._updateSessionStartTime(lastSessionState);
 
-    this._lastSessionState = null;
+    LastSession.clear();
   },
 
   /**
    * See if aWindow is usable for use when restoring a previous session via
    * restoreLastSession. If usable, prepare it for use.
    *
    * @param aWindow
    *        the window to inspect & prepare
@@ -2118,18 +2114,18 @@ let SessionStoreInternal = {
       windows: total,
       selectedWindow: ix + 1,
       _closedWindows: lastClosedWindowsCopy,
       session: session,
       scratchpads: scratchpads
     };
 
     // Persist the last session if we deferred restoring it
-    if (this._lastSessionState) {
-      state.lastSessionState = this._lastSessionState;
+    if (LastSession.canRestore) {
+      state.lastSessionState = LastSession.getState();
     }
 
     // If we were called by the SessionSaver and started with only a private
     // window we want to pass the deferred initial state to not lose the
     // previous session.
     if (this._deferredInitialState) {
       state.deferredInitialState = this._deferredInitialState;
     }
@@ -3517,18 +3513,18 @@ let SessionStoreInternal = {
    * This is going to take a state as provided at startup (via
    * nsISessionStartup.state) and split it into 2 parts. The first part
    * (defaultState) will be a state that should still be restored at startup,
    * while the second part (state) is a state that should be saved for later.
    * defaultState will be comprised of windows with only pinned tabs, extracted
    * from state. It will contain the cookies that go along with the history
    * entries in those tabs. It will also contain window position information.
    *
-   * defaultState will be restored at startup. state will be placed into
-   * this._lastSessionState and will be kept in case the user explicitly wants
+   * defaultState will be restored at startup. state will be passed into
+   * LastSession and will be kept in case the user explicitly wants
    * to restore the previous session (publicly exposed as restoreLastSession).
    *
    * @param state
    *        The state, presumably from nsISessionStartup.state
    * @returns [defaultState, state]
    */
   _prepDataForDeferredRestore: function ssi_prepDataForDeferredRestore(state) {
     // Make sure that we don't modify the global state as provided by
@@ -4528,8 +4524,35 @@ let TabState = {
     TextAndScrollData.updateFrame(tabData.entries[tabIndex],
                                   browser.contentWindow,
                                   !!tabData.pinned,
                                   {includePrivateData: includePrivateData});
 
     return true;
   },
 };
+
+// The state from the previous session (after restoring pinned tabs). This
+// state is persisted and passed through to the next session during an app
+// restart to make the third party add-on warning not trash the deferred
+// session
+let LastSession = {
+  _state: null,
+
+  get canRestore() {
+    return !!this._state;
+  },
+
+  getState: function () {
+    return this._state;
+  },
+
+  setState: function (state) {
+    this._state = state;
+  },
+
+  clear: function () {
+    if (this._state) {
+      this._state = null;
+      Services.obs.notifyObservers(null, NOTIFY_LAST_SESSION_CLEARED, null);
+    }
+  }
+};