Bug 898308 - Clean up SessionStore initialization; f=smacleod, r=dao
authorTim Taubert <ttaubert@mozilla.com>
Sat, 27 Jul 2013 07:40:07 +0200
changeset 140214 1e1f3cd07479c0083b09917aa5964e2f2007b983
parent 140213 69c186ac238c4de55dd1d39861a9e9d100191722
child 140215 5f7ad1bfd200acd5b26c8c16a635e7645e8c95a9
push id1947
push userttaubert@mozilla.com
push dateSat, 27 Jul 2013 05:40:21 +0000
treeherderfx-team@1e1f3cd07479 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdao
bugs898308
milestone25.0a1
Bug 898308 - Clean up SessionStore initialization; f=smacleod, r=dao
browser/base/content/browser.js
browser/base/content/test/social/browser_social_window.js
browser/components/nsBrowserGlue.js
browser/components/sessionstore/nsISessionStore.idl
browser/components/sessionstore/src/SessionStore.jsm
browser/components/sessionstore/test/head.js
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -132,16 +132,19 @@ XPCOMUtils.defineLazyModuleGetter(this, 
   "resource:///modules/BrowserNewTabPreloader.jsm", "BrowserNewTabPreloader");
 
 XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
   "resource://gre/modules/PrivateBrowsingUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "SitePermissions",
   "resource:///modules/SitePermissions.jsm");
 
+XPCOMUtils.defineLazyModuleGetter(this, "SessionStore",
+  "resource:///modules/sessionstore/SessionStore.jsm");
+
 let gInitialPages = [
   "about:blank",
   "about:newtab",
   "about:home",
   "about:privatebrowsing",
   "about:welcomeback",
   "about:sessionrestore"
 ];
@@ -1028,16 +1031,17 @@ var gBrowserInit = {
     Services.obs.addObserver(gXPInstallObserver, "addon-install-failed", false);
     Services.obs.addObserver(gXPInstallObserver, "addon-install-complete", false);
     Services.obs.addObserver(gFormSubmitObserver, "invalidformsubmit", false);
 
     BrowserOffline.init();
     OfflineApps.init();
     IndexedDBPromptHelper.init();
     gFormSubmitObserver.init();
+    SocialUI.init();
     AddonManager.addAddonListener(AddonsMgrListener);
     WebrtcIndicator.init();
 
     // Ensure login manager is up and running.
     Services.logins;
 
     if (mustLoadSidebar) {
       let sidebar = document.getElementById("sidebar");
@@ -1086,20 +1090,16 @@ var gBrowserInit = {
 
     // Bug 666804 - NetworkPrioritizer support for e10s
     if (!gMultiProcessBrowser) {
       let NP = {};
       Cu.import("resource:///modules/NetworkPrioritizer.jsm", NP);
       NP.trackBrowserWindow(window);
     }
 
-    // initialize the session-restore service (in case it's not already running)
-    let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
-    let ssPromise = ss.init(window);
-
     PlacesToolbarHelper.init();
 
     ctrlTab.readPref();
     gPrefService.addObserver(ctrlTab.prefName, ctrlTab, false);
 
     // Initialize the download manager some time after the app starts so that
     // auto-resume downloads begin (such as after crashing or quitting with
     // active downloads) and speeds up the first-load of the download manager UI.
@@ -1265,24 +1265,23 @@ var gBrowserInit = {
 #ifdef MOZ_METRO
     gMetroPrefs.prefDomain.forEach(function(prefName) {
       gMetroPrefs.pushDesktopControlledPrefToMetro(prefName);
       Services.prefs.addObserver(prefName, gMetroPrefs, false);
     }, this);
 #endif
 #endif
 
-    ssPromise.then(() =>{
+    SessionStore.promiseInitialized.then(() => {
       // Enable the Restore Last Session command if needed
-      if (ss.canRestoreLastSession &&
+      if (SessionStore.canRestoreLastSession &&
           !PrivateBrowsingUtils.isWindowPrivate(window))
         goSetCommandEnabled("Browser:RestoreLastSession", true);
 
       TabView.init();
-      SocialUI.init();
 
       setTimeout(function () { BrowserChromeTest.markAsReady(); }, 0);
     });
 
     Services.obs.notifyObservers(window, "browser-delayed-startup-finished", "");
     TelemetryTimestamps.add("delayedStartupFinished");
   },
 
--- a/browser/base/content/test/social/browser_social_window.js
+++ b/browser/base/content/test/social/browser_social_window.js
@@ -12,32 +12,26 @@ function resetSocial() {
   Social._provider = null;
   Social.providers = [];
   // *sob* - listeners keep getting added...
   let SocialService = Cu.import("resource://gre/modules/SocialService.jsm", {}).SocialService;
   SocialService._providerListeners.clear();
 }
 
 let createdWindows = [];
-let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
 
 function openWindowAndWaitForInit(callback) {
   // this notification tells us SocialUI.init() has been run...
   let topic = "browser-delayed-startup-finished";
   let w = OpenBrowserWindow();
   createdWindows.push(w);
   Services.obs.addObserver(function providerSet(subject, topic, data) {
     Services.obs.removeObserver(providerSet, topic);
     info(topic + " observer was notified - continuing test");
-    // We need to wait for the SessionStore as well, since
-    // SocialUI.init() is also waiting on it.
-    ss.init(w).then(function () {
-      executeSoon(function() {callback(w);});
-    });
-
+    executeSoon(() => callback(w));
   }, topic, false);
 }
 
 function postTestCleanup(cb) {
   for (let w of createdWindows)
     w.close();
   createdWindows = [];
   Services.prefs.clearUserPref("social.enabled");
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -63,16 +63,19 @@ XPCOMUtils.defineLazyModuleGetter(this, 
                                   "resource://gre/modules/Task.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "PlacesBackups",
                                   "resource://gre/modules/PlacesBackups.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "OS",
                                   "resource://gre/modules/osfile.jsm");
 
+XPCOMUtils.defineLazyModuleGetter(this, "SessionStore",
+                                  "resource:///modules/sessionstore/SessionStore.jsm");
+
 const PREF_PLUGINS_NOTIFYUSER = "plugins.update.notifyUser";
 const PREF_PLUGINS_UPDATEURL  = "plugins.update.url";
 
 // We try to backup bookmarks at idle times, to avoid doing that at shutdown.
 // Number of idle seconds before trying to backup bookmarks.  15 minutes.
 const BOOKMARKS_BACKUP_IDLE_TIME = 15 * 60;
 // Minimum interval in milliseconds between backups.
 const BOOKMARKS_BACKUP_INTERVAL = 86400 * 1000;
@@ -168,17 +171,17 @@ BrowserGlue.prototype = {
     switch (topic) {
       case "prefservice:after-app-defaults":
         this._onAppDefaults();
         break;
       case "final-ui-startup":
         this._finalUIStartup();
         break;
       case "browser-delayed-startup-finished":
-        this._onFirstWindowLoaded();
+        this._onFirstWindowLoaded(subject);
         Services.obs.removeObserver(this, "browser-delayed-startup-finished");
         break;
       case "sessionstore-windows-restored":
         this._onWindowsRestored();
         break;
       case "browser:purge-session-history":
         // reset the console service's error buffer
         Services.console.logStringMessage(null); // clear the console (in case it's open)
@@ -574,28 +577,29 @@ BrowserGlue.prototype = {
 
     let nb = win.document.getElementById("global-notificationbox");
     nb.appendNotification(message, "reset-unused-profile",
                           "chrome://global/skin/icons/question-16.png",
                           nb.PRIORITY_INFO_LOW, buttons);
   },
 
   // the first browser window has finished initializing
-  _onFirstWindowLoaded: function BG__onFirstWindowLoaded() {
+  _onFirstWindowLoaded: function BG__onFirstWindowLoaded(aWindow) {
 #ifdef XP_WIN
     // For windows seven, initialize the jump list module.
     const WINTASKBAR_CONTRACTID = "@mozilla.org/windows-taskbar;1";
     if (WINTASKBAR_CONTRACTID in Cc &&
         Cc[WINTASKBAR_CONTRACTID].getService(Ci.nsIWinTaskbar).available) {
       let temp = {};
       Cu.import("resource:///modules/WindowsJumpLists.jsm", temp);
       temp.WinTaskbarJumpList.startup();
     }
 #endif
 
+    SessionStore.init(aWindow);
     this._trackSlowStartup();
 
     // Offer to reset a user's profile if it hasn't been used for 60 days.
     const OFFER_PROFILE_RESET_INTERVAL_MS = 60 * 24 * 60 * 60 * 1000;
     let processStartupTime = Services.startup.getStartupInfo().process;
     let lastUse = Services.appinfo.replacedLockTime;
     if (processStartupTime && lastUse &&
         processStartupTime.getTime() - lastUse >= OFFER_PROFILE_RESET_INTERVAL_MS) {
--- a/browser/components/sessionstore/nsISessionStore.idl
+++ b/browser/components/sessionstore/nsISessionStore.idl
@@ -20,25 +20,20 @@ interface nsIDOMNode;
  * global |window| object to the API, though (or |top| from a sidebar).
  * From elsewhere you can get browser windows through the nsIWindowMediator
  * by looking for "navigator:browser" windows.
  *
  * * "Tabbrowser tabs" are all the child nodes of a browser window's
  * |gBrowser.tabContainer| such as e.g. |gBrowser.selectedTab|.
  */
 
-[scriptable, uuid(092fa0cc-e99b-11e2-a2a3-a25b4f45d8e2)]
+[scriptable, uuid(700756cc-f5c7-11e2-b842-59d9dc830245)]
 interface nsISessionStore : nsISupports
 {
   /**
-   * Initialize the service
-   */
-  jsval init(in nsIDOMWindow aWindow);
-
-  /**
    * Is it possible to restore the previous session. Will always be false when
    * in Private Browsing mode.
    */
   attribute boolean canRestoreLastSession;
 
   /**
    * Restore the previous session if possible. This will not overwrite the
    * current session. Instead the previous session will be merged into the
--- a/browser/components/sessionstore/src/SessionStore.jsm
+++ b/browser/components/sessionstore/src/SessionStore.jsm
@@ -137,29 +137,29 @@ function debug(aMsg) {
   if (gDebuggingEnabled) {
     aMsg = ("SessionStore: " + aMsg).replace(/\S{80}/g, "$&\n");
     Services.console.logStringMessage(aMsg);
   }
 }
 
 this.SessionStore = {
   get promiseInitialized() {
-    return SessionStoreInternal.promiseInitialized.promise;
+    return SessionStoreInternal.promiseInitialized;
   },
 
   get canRestoreLastSession() {
     return SessionStoreInternal.canRestoreLastSession;
   },
 
   set canRestoreLastSession(val) {
     SessionStoreInternal.canRestoreLastSession = val;
   },
 
   init: function ss_init(aWindow) {
-    return SessionStoreInternal.init(aWindow);
+    SessionStoreInternal.init(aWindow);
   },
 
   getBrowserState: function ss_getBrowserState() {
     return SessionStoreInternal.getBrowserState();
   },
 
   setBrowserState: function ss_setBrowserState(aState) {
     SessionStoreInternal.setBrowserState(aState);
@@ -335,17 +335,17 @@ let SessionStoreInternal = {
   _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
-  _promiseInitialization: Promise.defer(),
+  _deferredInitialized: Promise.defer(),
 
   // Whether session has been initialized
   _sessionInitialized: false,
 
   // True if session store is disabled by multi-process browsing.
   // See bug 516755.
   _disabledForMultiProcess: false,
 
@@ -358,56 +358,69 @@ let SessionStoreInternal = {
   // previous session is not always restored when
   // "sessionstore.resume_from_crash" is true.
   _resume_session_once_on_shutdown: null,
 
   /**
    * A promise fulfilled once initialization is complete.
    */
   get promiseInitialized() {
-    return this._promiseInitialization;
+    return this._deferredInitialized.promise;
   },
 
   /* ........ Public Getters .............. */
   get canRestoreLastSession() {
     return this._lastSessionState;
   },
 
   set canRestoreLastSession(val) {
     // Cheat a bit; only allow false.
     if (val)
       return;
     this._lastSessionState = null;
   },
 
-  /* ........ Global Event Handlers .............. */
-
   /**
-   * Initialize the component
+   * Initialize the sessionstore service.
    */
-  initService: function ssi_initService() {
-    if (this._sessionInitialized) {
-      return;
-    }
+  init: function (aWindow) {
+    if (this._initialized) {
+      throw new Error("SessionStore.init() must only be called once!");
+    }
+
+    if (!aWindow) {
+      throw new Error("SessionStore.init() must be called with a valid window.");
+    }
+
     TelemetryTimestamps.add("sessionRestoreInitialized");
     OBSERVING.forEach(function(aTopic) {
       Services.obs.addObserver(this, aTopic, true);
     }, this);
 
     this._initPrefs();
-
+    this._initialized = true;
     this._disabledForMultiProcess = this._prefBranch.getBoolPref("tabs.remote");
 
     // this pref is only read at startup, so no need to observe it
     this._sessionhistory_max_entries =
       this._prefBranch.getIntPref("sessionhistory.max_entries");
 
-    gSessionStartup.onceInitialized.then(
-      this.initSession.bind(this)
-    );
+    // Wait until nsISessionStartup has finished reading the session data.
+    gSessionStartup.onceInitialized.then(() => {
+      // Parse session data and start restoring.
+      this.initSession();
+
+      // Start tracking the given (initial) browser window.
+      if (!aWindow.closed) {
+        this.onLoad(aWindow);
+      }
+
+      // Let everyone know we're done.
+      this._deferredInitialized.resolve();
+    });
   },
 
   initSession: function ssi_initSession() {
     let ss = gSessionStartup;
     try {
       if (ss.doRestore() ||
           ss.sessionType == Ci.nsISessionStartup.DEFER_SESSION)
         this._initialState = ss.state;
@@ -487,17 +500,16 @@ let SessionStoreInternal = {
         this._prefBranch.getBoolPref("sessionstore.resume_session_once"))
       this._prefBranch.setBoolPref("sessionstore.resume_session_once", false);
 
     this._initEncoding();
 
     this._performUpgradeBackup();
 
     this._sessionInitialized = true;
-    this._promiseInitialization.resolve();
   },
 
   /**
    * If this is the first time we launc this build of Firefox,
    * backup sessionstore.js.
    */
   _performUpgradeBackup: function ssi_performUpgradeBackup() {
     // Perform upgrade backup, if necessary
@@ -561,45 +573,28 @@ let SessionStoreInternal = {
 
     XPCOMUtils.defineLazyGetter(this, "_max_windows_undo", function () {
       this._prefBranch.addObserver("sessionstore.max_windows_undo", this, true);
       return this._prefBranch.getIntPref("sessionstore.max_windows_undo");
     });
   },
 
   /**
-   * Start tracking a window.
-   *
-   * This function also initializes the component if it is not
-   * initialized yet.
-   */
-  init: function ssi_init(aWindow) {
-    if (!aWindow) {
-      throw new Error("init() must be called with a valid window.");
-    }
-
-    let self = this;
-    this.initService();
-    return this._promiseInitialization.promise.then(
-      function onSuccess() {
-        if (!aWindow.closed) {
-          self.onLoad(aWindow);
-        }
-      }
-    );
-  },
-
-  /**
    * Called on application shutdown, after notifications:
    * quit-application-granted, quit-application
    */
   _uninit: function ssi_uninit() {
+    if (!this._initialized) {
+      throw new Error("SessionStore is not initialized.");
+    }
+
     // save all data for session resuming
-    if (this._sessionInitialized)
+    if (this._sessionInitialized) {
       this.saveState(true);
+    }
 
     // clear out priority queue in case it's still holding refs
     TabRestoreQueue.reset();
 
     // Make sure to break our cycle with the save timer
     if (this._saveTimer) {
       this._saveTimer.cancel();
       this._saveTimer = null;
--- a/browser/components/sessionstore/test/head.js
+++ b/browser/components/sessionstore/test/head.js
@@ -377,23 +377,21 @@ let TestRunner = {
   backupState: {},
 
   /**
    * Starts the test runner.
    */
   run: function () {
     waitForExplicitFinish();
 
-    SessionStore.promiseInitialized.then(function () {
-      executeSoon(function () {
-        this.backupState = JSON.parse(ss.getBrowserState());
-        this._iter = runTests();
-        this.next();
-      }.bind(this));
-    }.bind(this));
+    SessionStore.promiseInitialized.then(() => {
+      this.backupState = JSON.parse(ss.getBrowserState());
+      this._iter = runTests();
+      this.next();
+    });
   },
 
   /**
    * Runs the next available test or finishes if there's no test left.
    */
   next: function () {
     try {
       TestRunner._iter.next();