123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297 |
- /* 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/. */
- /**
- * Session Storage and Restoration
- *
- * Overview
- * This service reads user's session file at startup, and makes a determination
- * as to whether the session should be restored. It will restore the session
- * under the circumstances described below. If the auto-start Private Browsing
- * mode is active, however, the session is never restored.
- *
- * Crash Detection
- * The session file stores a session.state property, that
- * indicates whether the browser is currently running. When the browser shuts
- * down, the field is changed to "stopped". At startup, this field is read, and
- * if its value is "running", then it's assumed that the browser had previously
- * crashed, or at the very least that something bad happened, and that we should
- * restore the session.
- *
- * Forced Restarts
- * In the event that a restart is required due to application update or extension
- * installation, set the browser.sessionstore.resume_session_once pref to true,
- * and the session will be restored the next time the browser starts.
- *
- * Always Resume
- * This service will always resume the session if the integer pref
- * browser.startup.page is set to 3.
- */
- /* :::::::: Constants and Helpers ::::::::::::::: */
- const Cc = Components.classes;
- const Ci = Components.interfaces;
- const Cr = Components.results;
- const Cu = Components.utils;
- Cu.import("resource://gre/modules/XPCOMUtils.jsm");
- Cu.import("resource://gre/modules/Services.jsm");
- Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
- Cu.import("resource://gre/modules/Promise.jsm");
- XPCOMUtils.defineLazyModuleGetter(this, "_SessionFile",
- "resource:///modules/sessionstore/_SessionFile.jsm");
- const STATE_RUNNING_STR = "running";
- function debug(aMsg) {
- aMsg = ("SessionStartup: " + aMsg).replace(/\S{80}/g, "$&\n");
- Services.console.logStringMessage(aMsg);
- }
- var gOnceInitializedDeferred = Promise.defer();
- /* :::::::: The Service ::::::::::::::: */
- function SessionStartup() {
- }
- SessionStartup.prototype = {
- // the state to restore at startup
- _initialState: null,
- _sessionType: Ci.nsISessionStartup.NO_SESSION,
- _initialized: false,
- /* ........ Global Event Handlers .............. */
- /**
- * Initialize the component
- */
- init: function() {
- // do not need to initialize anything in auto-started private browsing sessions
- if (PrivateBrowsingUtils.permanentPrivateBrowsing) {
- this._initialized = true;
- gOnceInitializedDeferred.resolve();
- return;
- }
- if (Services.prefs.getBoolPref("browser.sessionstore.resume_session_once") ||
- Services.prefs.getIntPref("browser.startup.page") == 3) {
- this._ensureInitialized();
- } else {
- _SessionFile.read().then(
- this._onSessionFileRead.bind(this)
- );
- }
- },
- // Wrap a string as a nsISupports
- _createSupportsString: function(aData) {
- let string = Cc["@mozilla.org/supports-string;1"]
- .createInstance(Ci.nsISupportsString);
- string.data = aData;
- return string;
- },
- _onSessionFileRead: function(aStateString) {
- if (this._initialized) {
- // Initialization is complete, nothing else to do
- return;
- }
- try {
- this._initialized = true;
- // Let observers modify the state before it is used
- let supportsStateString = this._createSupportsString(aStateString);
- Services.obs.notifyObservers(supportsStateString, "sessionstore-state-read", "");
- aStateString = supportsStateString.data;
- // No valid session found.
- if (!aStateString) {
- this._sessionType = Ci.nsISessionStartup.NO_SESSION;
- return;
- }
- // parse the session state into a JS object
- // remove unneeded braces (added for compatibility with Firefox 2.0 and 3.0)
- if (aStateString.charAt(0) == '(')
- aStateString = aStateString.slice(1, -1);
- let corruptFile = false;
- try {
- this._initialState = JSON.parse(aStateString);
- }
- catch (ex) {
- debug("The session file contained un-parse-able JSON: " + ex);
- // This is not valid JSON, but this might still be valid JavaScript,
- // as used in FF2/FF3, so we need to eval.
- // evalInSandbox will throw if aStateString is not parse-able.
- try {
- var s = new Cu.Sandbox("about:blank", {sandboxName: 'nsSessionStartup'});
- this._initialState = Cu.evalInSandbox("(" + aStateString + ")", s);
- } catch(ex) {
- debug("The session file contained un-eval-able JSON: " + ex);
- corruptFile = true;
- }
- }
- let doResumeSessionOnce = Services.prefs.getBoolPref("browser.sessionstore.resume_session_once");
- let doResumeSession = doResumeSessionOnce ||
- Services.prefs.getIntPref("browser.startup.page") == 3;
- // If this is a normal restore then throw away any previous session
- if (!doResumeSessionOnce)
- delete this._initialState.lastSessionState;
- let resumeFromCrash = Services.prefs.getBoolPref("browser.sessionstore.resume_from_crash");
- let lastSessionCrashed =
- this._initialState && this._initialState.session &&
- this._initialState.session.state &&
- this._initialState.session.state == STATE_RUNNING_STR;
- // set the startup type
- if (lastSessionCrashed && resumeFromCrash)
- this._sessionType = Ci.nsISessionStartup.RECOVER_SESSION;
- else if (!lastSessionCrashed && doResumeSession)
- this._sessionType = Ci.nsISessionStartup.RESUME_SESSION;
- else if (this._initialState)
- this._sessionType = Ci.nsISessionStartup.DEFER_SESSION;
- else
- this._initialState = null; // reset the state
- Services.obs.addObserver(this, "sessionstore-windows-restored", true);
- if (this._sessionType != Ci.nsISessionStartup.NO_SESSION)
- Services.obs.addObserver(this, "browser:purge-session-history", true);
- } finally {
- // We're ready. Notify everyone else.
- Services.obs.notifyObservers(null, "sessionstore-state-finalized", "");
- gOnceInitializedDeferred.resolve();
- }
- },
- /**
- * Handle notifications
- */
- observe: function(aSubject, aTopic, aData) {
- switch (aTopic) {
- case "app-startup":
- Services.obs.addObserver(this, "final-ui-startup", true);
- Services.obs.addObserver(this, "quit-application", true);
- break;
- case "final-ui-startup":
- Services.obs.removeObserver(this, "final-ui-startup");
- Services.obs.removeObserver(this, "quit-application");
- this.init();
- break;
- case "quit-application":
- // no reason for initializing at this point (cf. bug 409115)
- Services.obs.removeObserver(this, "final-ui-startup");
- Services.obs.removeObserver(this, "quit-application");
- if (this._sessionType != Ci.nsISessionStartup.NO_SESSION)
- Services.obs.removeObserver(this, "browser:purge-session-history");
- break;
- case "sessionstore-windows-restored":
- Services.obs.removeObserver(this, "sessionstore-windows-restored");
- // free _initialState after nsSessionStore is done with it
- this._initialState = null;
- break;
- case "browser:purge-session-history":
- Services.obs.removeObserver(this, "browser:purge-session-history");
- // reset all state on sanitization
- this._sessionType = Ci.nsISessionStartup.NO_SESSION;
- break;
- }
- },
- /* ........ Public API ................*/
- get onceInitialized() {
- return gOnceInitializedDeferred.promise;
- },
- /**
- * Get the session state as a jsval
- */
- get state() {
- this._ensureInitialized();
- return this._initialState;
- },
- /**
- * Determines whether there is a pending session restore and makes sure that
- * we're initialized before returning. If we're not yet this will read the
- * session file synchronously.
- * @returns bool
- */
- doRestore: function() {
- this._ensureInitialized();
- return this._willRestore();
- },
- /**
- * Determines whether there is a pending session restore.
- * @returns bool
- */
- _willRestore: function() {
- return this._sessionType == Ci.nsISessionStartup.RECOVER_SESSION ||
- this._sessionType == Ci.nsISessionStartup.RESUME_SESSION;
- },
- /**
- * Returns whether we will restore a session that ends up replacing the
- * homepage. The browser uses this to not start loading the homepage if
- * we're going to stop its load anyway shortly after.
- *
- * This is meant to be an optimization for the average case that loading the
- * session file finishes before we may want to start loading the default
- * homepage. Should this be called before the session file has been read it
- * will just return false.
- *
- * @returns bool
- */
- get willOverrideHomepage() {
- if (this._initialState && this._willRestore()) {
- let windows = this._initialState.windows || null;
- // If there are valid windows with not only pinned tabs, signal that we
- // will override the default homepage by restoring a session.
- return windows && windows.some(w => w.tabs.some(t => !t.pinned));
- }
- return false;
- },
- /**
- * Get the type of pending session store, if any.
- */
- get sessionType() {
- this._ensureInitialized();
- return this._sessionType;
- },
- // Ensure that initialization is complete.
- // If initialization is not complete yet, fall back to a synchronous
- // initialization and kill ongoing asynchronous initialization
- _ensureInitialized: function() {
- try {
- if (this._initialized) {
- // Initialization is complete, nothing else to do
- return;
- }
- let contents = _SessionFile.syncRead();
- this._onSessionFileRead(contents);
- } catch(ex) {
- debug("ensureInitialized: could not read session " + ex + ", " + ex.stack);
- throw ex;
- }
- },
- /* ........ QueryInterface .............. */
- QueryInterface : XPCOMUtils.generateQI([Ci.nsIObserver,
- Ci.nsISupportsWeakReference,
- Ci.nsISessionStartup]),
- classID: Components.ID("{ec7a6c20-e081-11da-8ad9-0800200c9a66}")
- };
- this.NSGetFactory = XPCOMUtils.generateNSGetFactory([SessionStartup]);
|