browser/components/sessionstore/src/nsSessionStartup.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 "use strict";
     7 /**
     8  * Session Storage and Restoration
     9  *
    10  * Overview
    11  * This service reads user's session file at startup, and makes a determination
    12  * as to whether the session should be restored. It will restore the session
    13  * under the circumstances described below.  If the auto-start Private Browsing
    14  * mode is active, however, the session is never restored.
    15  *
    16  * Crash Detection
    17  * The CrashMonitor is used to check if the final session state was successfully
    18  * written at shutdown of the last session. If we did not reach
    19  * 'sessionstore-final-state-write-complete', then it's assumed that the browser
    20  * has previously crashed and we should restore the session.
    21  *
    22  * Forced Restarts
    23  * In the event that a restart is required due to application update or extension
    24  * installation, set the browser.sessionstore.resume_session_once pref to true,
    25  * and the session will be restored the next time the browser starts.
    26  *
    27  * Always Resume
    28  * This service will always resume the session if the integer pref
    29  * browser.startup.page is set to 3.
    30  */
    32 /* :::::::: Constants and Helpers ::::::::::::::: */
    34 const Cc = Components.classes;
    35 const Ci = Components.interfaces;
    36 const Cr = Components.results;
    37 const Cu = Components.utils;
    38 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
    39 Cu.import("resource://gre/modules/Services.jsm");
    40 Cu.import("resource://gre/modules/TelemetryStopwatch.jsm");
    41 Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
    42 Cu.import("resource://gre/modules/Promise.jsm");
    44 XPCOMUtils.defineLazyModuleGetter(this, "console",
    45   "resource://gre/modules/devtools/Console.jsm");
    46 XPCOMUtils.defineLazyModuleGetter(this, "SessionFile",
    47   "resource:///modules/sessionstore/SessionFile.jsm");
    48 XPCOMUtils.defineLazyModuleGetter(this, "CrashMonitor",
    49   "resource://gre/modules/CrashMonitor.jsm");
    51 const STATE_RUNNING_STR = "running";
    53 // 'browser.startup.page' preference value to resume the previous session.
    54 const BROWSER_STARTUP_RESUME_SESSION = 3;
    56 function debug(aMsg) {
    57   aMsg = ("SessionStartup: " + aMsg).replace(/\S{80}/g, "$&\n");
    58   Services.console.logStringMessage(aMsg);
    59 }
    61 let gOnceInitializedDeferred = Promise.defer();
    63 /* :::::::: The Service ::::::::::::::: */
    65 function SessionStartup() {
    66 }
    68 SessionStartup.prototype = {
    70   // the state to restore at startup
    71   _initialState: null,
    72   _sessionType: Ci.nsISessionStartup.NO_SESSION,
    73   _initialized: false,
    75   // Stores whether the previous session crashed.
    76   _previousSessionCrashed: null,
    78 /* ........ Global Event Handlers .............. */
    80   /**
    81    * Initialize the component
    82    */
    83   init: function sss_init() {
    84     Services.obs.notifyObservers(null, "sessionstore-init-started", null);
    86     // do not need to initialize anything in auto-started private browsing sessions
    87     if (PrivateBrowsingUtils.permanentPrivateBrowsing) {
    88       this._initialized = true;
    89       gOnceInitializedDeferred.resolve();
    90       return;
    91     }
    93     SessionFile.read().then(
    94       this._onSessionFileRead.bind(this),
    95       console.error
    96     );
    97   },
    99   // Wrap a string as a nsISupports
   100   _createSupportsString: function ssfi_createSupportsString(aData) {
   101     let string = Cc["@mozilla.org/supports-string;1"]
   102                    .createInstance(Ci.nsISupportsString);
   103     string.data = aData;
   104     return string;
   105   },
   107   /**
   108    * Complete initialization once the Session File has been read
   109    *
   110    * @param stateString
   111    *        string The Session State string read from disk
   112    */
   113   _onSessionFileRead: function (stateString) {
   114     this._initialized = true;
   116     // Let observers modify the state before it is used
   117     let supportsStateString = this._createSupportsString(stateString);
   118     Services.obs.notifyObservers(supportsStateString, "sessionstore-state-read", "");
   119     stateString = supportsStateString.data;
   121     // No valid session found.
   122     if (!stateString) {
   123       this._sessionType = Ci.nsISessionStartup.NO_SESSION;
   124       Services.obs.notifyObservers(null, "sessionstore-state-finalized", "");
   125       gOnceInitializedDeferred.resolve();
   126       return;
   127     }
   129     this._initialState = this._parseStateString(stateString);
   131     let shouldResumeSessionOnce = Services.prefs.getBoolPref("browser.sessionstore.resume_session_once");
   132     let shouldResumeSession = shouldResumeSessionOnce ||
   133           Services.prefs.getIntPref("browser.startup.page") == BROWSER_STARTUP_RESUME_SESSION;
   135     // If this is a normal restore then throw away any previous session
   136     if (!shouldResumeSessionOnce && this._initialState) {
   137       delete this._initialState.lastSessionState;
   138     }
   140     let resumeFromCrash = Services.prefs.getBoolPref("browser.sessionstore.resume_from_crash");
   142     CrashMonitor.previousCheckpoints.then(checkpoints => {
   143       if (checkpoints) {
   144         // If the previous session finished writing the final state, we'll
   145         // assume there was no crash.
   146         this._previousSessionCrashed = !checkpoints["sessionstore-final-state-write-complete"];
   147       } else {
   148         // If the Crash Monitor could not load a checkpoints file it will
   149         // provide null. This could occur on the first run after updating to
   150         // a version including the Crash Monitor, or if the checkpoints file
   151         // was removed.
   152         //
   153         // If this is the first run after an update, sessionstore.js should
   154         // still contain the session.state flag to indicate if the session
   155         // crashed. If it is not present, we will assume this was not the first
   156         // run after update and the checkpoints file was somehow corrupted or
   157         // removed by a crash.
   158         //
   159         // If the session.state flag is present, we will fallback to using it
   160         // for crash detection - If the last write of sessionstore.js had it
   161         // set to "running", we crashed.
   162         let stateFlagPresent = (this._initialState &&
   163                                 this._initialState.session &&
   164                                 this._initialState.session.state);
   167         this._previousSessionCrashed = !stateFlagPresent ||
   168                                        (this._initialState.session.state == STATE_RUNNING_STR);
   169       }
   171       // Report shutdown success via telemetry. Shortcoming here are
   172       // being-killed-by-OS-shutdown-logic, shutdown freezing after
   173       // session restore was written, etc.
   174       Services.telemetry.getHistogramById("SHUTDOWN_OK").add(!this._previousSessionCrashed);
   176       // set the startup type
   177       if (this._previousSessionCrashed && resumeFromCrash)
   178         this._sessionType = Ci.nsISessionStartup.RECOVER_SESSION;
   179       else if (!this._previousSessionCrashed && shouldResumeSession)
   180         this._sessionType = Ci.nsISessionStartup.RESUME_SESSION;
   181       else if (this._initialState)
   182         this._sessionType = Ci.nsISessionStartup.DEFER_SESSION;
   183       else
   184         this._initialState = null; // reset the state
   186       Services.obs.addObserver(this, "sessionstore-windows-restored", true);
   188       if (this._sessionType != Ci.nsISessionStartup.NO_SESSION)
   189         Services.obs.addObserver(this, "browser:purge-session-history", true);
   191       // We're ready. Notify everyone else.
   192       Services.obs.notifyObservers(null, "sessionstore-state-finalized", "");
   193       gOnceInitializedDeferred.resolve();
   194     });
   195   },
   198   /**
   199    * Convert the Session State string into a state object
   200    *
   201    * @param stateString
   202    *        string The Session State string read from disk
   203    * @returns {State} a Session State object
   204    */
   205   _parseStateString: function (stateString) {
   206     let state = null;
   207     let corruptFile = false;
   209     try {
   210       state = JSON.parse(stateString);
   211     } catch (ex) {
   212       debug("The session file contained un-parse-able JSON: " + ex);
   213       corruptFile = true;
   214     }
   215     Services.telemetry.getHistogramById("FX_SESSION_RESTORE_CORRUPT_FILE").add(corruptFile);
   217     return state;
   218   },
   220   /**
   221    * Handle notifications
   222    */
   223   observe: function sss_observe(aSubject, aTopic, aData) {
   224     switch (aTopic) {
   225     case "app-startup":
   226       Services.obs.addObserver(this, "final-ui-startup", true);
   227       Services.obs.addObserver(this, "quit-application", true);
   228       break;
   229     case "final-ui-startup":
   230       Services.obs.removeObserver(this, "final-ui-startup");
   231       Services.obs.removeObserver(this, "quit-application");
   232       this.init();
   233       break;
   234     case "quit-application":
   235       // no reason for initializing at this point (cf. bug 409115)
   236       Services.obs.removeObserver(this, "final-ui-startup");
   237       Services.obs.removeObserver(this, "quit-application");
   238       if (this._sessionType != Ci.nsISessionStartup.NO_SESSION)
   239         Services.obs.removeObserver(this, "browser:purge-session-history");
   240       break;
   241     case "sessionstore-windows-restored":
   242       Services.obs.removeObserver(this, "sessionstore-windows-restored");
   243       // free _initialState after nsSessionStore is done with it
   244       this._initialState = null;
   245       break;
   246     case "browser:purge-session-history":
   247       Services.obs.removeObserver(this, "browser:purge-session-history");
   248       // reset all state on sanitization
   249       this._sessionType = Ci.nsISessionStartup.NO_SESSION;
   250       break;
   251     }
   252   },
   254 /* ........ Public API ................*/
   256   get onceInitialized() {
   257     return gOnceInitializedDeferred.promise;
   258   },
   260   /**
   261    * Get the session state as a jsval
   262    */
   263   get state() {
   264     this._ensureInitialized();
   265     return this._initialState;
   266   },
   268   /**
   269    * Determines whether there is a pending session restore. Should only be
   270    * called after initialization has completed.
   271    * @throws Error if initialization is not complete yet.
   272    * @returns bool
   273    */
   274   doRestore: function sss_doRestore() {
   275     this._ensureInitialized();
   276     return this._willRestore();
   277   },
   279   /**
   280    * Determines whether automatic session restoration is enabled for this
   281    * launch of the browser. This does not include crash restoration. In
   282    * particular, if session restore is configured to restore only in case of
   283    * crash, this method returns false.
   284    * @returns bool
   285    */
   286   isAutomaticRestoreEnabled: function () {
   287     return Services.prefs.getBoolPref("browser.sessionstore.resume_session_once") ||
   288            Services.prefs.getIntPref("browser.startup.page") == BROWSER_STARTUP_RESUME_SESSION;
   289   },
   291   /**
   292    * Determines whether there is a pending session restore.
   293    * @returns bool
   294    */
   295   _willRestore: function () {
   296     return this._sessionType == Ci.nsISessionStartup.RECOVER_SESSION ||
   297            this._sessionType == Ci.nsISessionStartup.RESUME_SESSION;
   298   },
   300   /**
   301    * Returns whether we will restore a session that ends up replacing the
   302    * homepage. The browser uses this to not start loading the homepage if
   303    * we're going to stop its load anyway shortly after.
   304    *
   305    * This is meant to be an optimization for the average case that loading the
   306    * session file finishes before we may want to start loading the default
   307    * homepage. Should this be called before the session file has been read it
   308    * will just return false.
   309    *
   310    * @returns bool
   311    */
   312   get willOverrideHomepage() {
   313     if (this._initialState && this._willRestore()) {
   314       let windows = this._initialState.windows || null;
   315       // If there are valid windows with not only pinned tabs, signal that we
   316       // will override the default homepage by restoring a session.
   317       return windows && windows.some(w => w.tabs.some(t => !t.pinned));
   318     }
   319     return false;
   320   },
   322   /**
   323    * Get the type of pending session store, if any.
   324    */
   325   get sessionType() {
   326     this._ensureInitialized();
   327     return this._sessionType;
   328   },
   330   /**
   331    * Get whether the previous session crashed.
   332    */
   333   get previousSessionCrashed() {
   334     this._ensureInitialized();
   335     return this._previousSessionCrashed;
   336   },
   338   // Ensure that initialization is complete. If initialization is not complete
   339   // yet, something is attempting to use the old synchronous initialization,
   340   // throw an error.
   341   _ensureInitialized: function sss__ensureInitialized() {
   342     if (!this._initialized) {
   343       throw new Error("Session Store is not initialized.");
   344     }
   345   },
   347   /* ........ QueryInterface .............. */
   348   QueryInterface : XPCOMUtils.generateQI([Ci.nsIObserver,
   349                                           Ci.nsISupportsWeakReference,
   350                                           Ci.nsISessionStartup]),
   351   classID:          Components.ID("{ec7a6c20-e081-11da-8ad9-0800200c9a66}")
   352 };
   354 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([SessionStartup]);

mercurial