home *** CD-ROM | disk | FTP | other *** search
/ Freelog 121 / FreelogMagazineJuilletAout2014-No121.iso / Internet / Waterfox / Waterfox.exe / components / nsSessionStartup.js < prev    next >
Text File  |  2010-01-01  |  11KB  |  310 lines

  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/. */
  4.  
  5. "use strict";
  6.  
  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 session file stores a session.state property, that
  18.  * indicates whether the browser is currently running. When the browser shuts
  19.  * down, the field is changed to "stopped". At startup, this field is read, and
  20.  * if its value is "running", then it's assumed that the browser had previously
  21.  * crashed, or at the very least that something bad happened, and that we should
  22.  * restore the session.
  23.  *
  24.  * Forced Restarts
  25.  * In the event that a restart is required due to application update or extension
  26.  * installation, set the browser.sessionstore.resume_session_once pref to true,
  27.  * and the session will be restored the next time the browser starts.
  28.  *
  29.  * Always Resume
  30.  * This service will always resume the session if the integer pref
  31.  * browser.startup.page is set to 3.
  32.  */
  33.  
  34. /* :::::::: Constants and Helpers ::::::::::::::: */
  35.  
  36. const Cc = Components.classes;
  37. const Ci = Components.interfaces;
  38. const Cr = Components.results;
  39. const Cu = Components.utils;
  40. Cu.import("resource://gre/modules/XPCOMUtils.jsm");
  41. Cu.import("resource://gre/modules/Services.jsm");
  42. Cu.import("resource://gre/modules/TelemetryStopwatch.jsm");
  43. Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
  44. Cu.import("resource://gre/modules/Promise.jsm");
  45.  
  46. XPCOMUtils.defineLazyModuleGetter(this, "SessionFile",
  47.   "resource:///modules/sessionstore/SessionFile.jsm");
  48.  
  49. const STATE_RUNNING_STR = "running";
  50.  
  51. // 'browser.startup.page' preference value to resume the previous session.
  52. const BROWSER_STARTUP_RESUME_SESSION = 3;
  53.  
  54. function debug(aMsg) {
  55.   aMsg = ("SessionStartup: " + aMsg).replace(/\S{80}/g, "$&\n");
  56.   Services.console.logStringMessage(aMsg);
  57. }
  58.  
  59. let gOnceInitializedDeferred = Promise.defer();
  60.  
  61. /* :::::::: The Service ::::::::::::::: */
  62.  
  63. function SessionStartup() {
  64. }
  65.  
  66. SessionStartup.prototype = {
  67.  
  68.   // the state to restore at startup
  69.   _initialState: null,
  70.   _sessionType: Ci.nsISessionStartup.NO_SESSION,
  71.   _initialized: false,
  72.  
  73. /* ........ Global Event Handlers .............. */
  74.  
  75.   /**
  76.    * Initialize the component
  77.    */
  78.   init: function sss_init() {
  79.     // do not need to initialize anything in auto-started private browsing sessions
  80.     if (PrivateBrowsingUtils.permanentPrivateBrowsing) {
  81.       this._initialized = true;
  82.       gOnceInitializedDeferred.resolve();
  83.       return;
  84.     }
  85.  
  86.     SessionFile.read().then(
  87.       this._onSessionFileRead.bind(this),
  88.       Cu.reportError
  89.     );
  90.   },
  91.  
  92.   // Wrap a string as a nsISupports
  93.   _createSupportsString: function ssfi_createSupportsString(aData) {
  94.     let string = Cc["@mozilla.org/supports-string;1"]
  95.                    .createInstance(Ci.nsISupportsString);
  96.     string.data = aData;
  97.     return string;
  98.   },
  99.  
  100.   _onSessionFileRead: function sss_onSessionFileRead(aStateString) {
  101.     if (this._initialized) {
  102.       // Initialization is complete, nothing else to do
  103.       return;
  104.     }
  105.     try {
  106.       this._initialized = true;
  107.  
  108.       // Let observers modify the state before it is used
  109.       let supportsStateString = this._createSupportsString(aStateString);
  110.       Services.obs.notifyObservers(supportsStateString, "sessionstore-state-read", "");
  111.       aStateString = supportsStateString.data;
  112.  
  113.       // No valid session found.
  114.       if (!aStateString) {
  115.         this._sessionType = Ci.nsISessionStartup.NO_SESSION;
  116.         return;
  117.       }
  118.  
  119.       // parse the session state into a JS object
  120.       // remove unneeded braces (added for compatibility with Firefox 2.0 and 3.0)
  121.       if (aStateString.charAt(0) == '(')
  122.         aStateString = aStateString.slice(1, -1);
  123.       let corruptFile = false;
  124.       try {
  125.         this._initialState = JSON.parse(aStateString);
  126.       }
  127.       catch (ex) {
  128.         debug("The session file contained un-parse-able JSON: " + ex);
  129.         // This is not valid JSON, but this might still be valid JavaScript,
  130.         // as used in FF2/FF3, so we need to eval.
  131.         // evalInSandbox will throw if aStateString is not parse-able.
  132.         try {
  133.           var s = new Cu.Sandbox("about:blank", {sandboxName: 'nsSessionStartup'});
  134.           this._initialState = Cu.evalInSandbox("(" + aStateString + ")", s);
  135.         } catch(ex) {
  136.           debug("The session file contained un-eval-able JSON: " + ex);
  137.           corruptFile = true;
  138.         }
  139.       }
  140.       Services.telemetry.getHistogramById("FX_SESSION_RESTORE_CORRUPT_FILE").add(corruptFile);
  141.  
  142.       let doResumeSessionOnce = Services.prefs.getBoolPref("browser.sessionstore.resume_session_once");
  143.       let doResumeSession = doResumeSessionOnce ||
  144.             Services.prefs.getIntPref("browser.startup.page") == BROWSER_STARTUP_RESUME_SESSION;
  145.  
  146.       // If this is a normal restore then throw away any previous session
  147.       if (!doResumeSessionOnce)
  148.         delete this._initialState.lastSessionState;
  149.  
  150.       let resumeFromCrash = Services.prefs.getBoolPref("browser.sessionstore.resume_from_crash");
  151.       let lastSessionCrashed =
  152.         this._initialState && this._initialState.session &&
  153.         this._initialState.session.state &&
  154.         this._initialState.session.state == STATE_RUNNING_STR;
  155.  
  156.       // Report shutdown success via telemetry. Shortcoming here are
  157.       // being-killed-by-OS-shutdown-logic, shutdown freezing after
  158.       // session restore was written, etc.
  159.       Services.telemetry.getHistogramById("SHUTDOWN_OK").add(!lastSessionCrashed);
  160.  
  161.       // set the startup type
  162.       if (lastSessionCrashed && resumeFromCrash)
  163.         this._sessionType = Ci.nsISessionStartup.RECOVER_SESSION;
  164.       else if (!lastSessionCrashed && doResumeSession)
  165.         this._sessionType = Ci.nsISessionStartup.RESUME_SESSION;
  166.       else if (this._initialState)
  167.         this._sessionType = Ci.nsISessionStartup.DEFER_SESSION;
  168.       else
  169.         this._initialState = null; // reset the state
  170.  
  171.       Services.obs.addObserver(this, "sessionstore-windows-restored", true);
  172.  
  173.       if (this._sessionType != Ci.nsISessionStartup.NO_SESSION)
  174.         Services.obs.addObserver(this, "browser:purge-session-history", true);
  175.  
  176.     } finally {
  177.       // We're ready. Notify everyone else.
  178.       Services.obs.notifyObservers(null, "sessionstore-state-finalized", "");
  179.       gOnceInitializedDeferred.resolve();
  180.     }
  181.   },
  182.  
  183.   /**
  184.    * Handle notifications
  185.    */
  186.   observe: function sss_observe(aSubject, aTopic, aData) {
  187.     switch (aTopic) {
  188.     case "app-startup":
  189.       Services.obs.addObserver(this, "final-ui-startup", true);
  190.       Services.obs.addObserver(this, "quit-application", true);
  191.       break;
  192.     case "final-ui-startup":
  193.       Services.obs.removeObserver(this, "final-ui-startup");
  194.       Services.obs.removeObserver(this, "quit-application");
  195.       this.init();
  196.       break;
  197.     case "quit-application":
  198.       // no reason for initializing at this point (cf. bug 409115)
  199.       Services.obs.removeObserver(this, "final-ui-startup");
  200.       Services.obs.removeObserver(this, "quit-application");
  201.       if (this._sessionType != Ci.nsISessionStartup.NO_SESSION)
  202.         Services.obs.removeObserver(this, "browser:purge-session-history");
  203.       break;
  204.     case "sessionstore-windows-restored":
  205.       Services.obs.removeObserver(this, "sessionstore-windows-restored");
  206.       // free _initialState after nsSessionStore is done with it
  207.       this._initialState = null;
  208.       break;
  209.     case "browser:purge-session-history":
  210.       Services.obs.removeObserver(this, "browser:purge-session-history");
  211.       // reset all state on sanitization
  212.       this._sessionType = Ci.nsISessionStartup.NO_SESSION;
  213.       break;
  214.     }
  215.   },
  216.  
  217. /* ........ Public API ................*/
  218.  
  219.   get onceInitialized() {
  220.     return gOnceInitializedDeferred.promise;
  221.   },
  222.  
  223.   /**
  224.    * Get the session state as a jsval
  225.    */
  226.   get state() {
  227.     this._ensureInitialized();
  228.     return this._initialState;
  229.   },
  230.  
  231.   /**
  232.    * Determines whether there is a pending session restore. Should only be
  233.    * called after initialization has completed.
  234.    * @throws Error if initialization is not complete yet.
  235.    * @returns bool
  236.    */
  237.   doRestore: function sss_doRestore() {
  238.     this._ensureInitialized();
  239.     return this._willRestore();
  240.   },
  241.  
  242.   /**
  243.    * Determines whether automatic session restoration is enabled for this
  244.    * launch of the browser. This does not include crash restoration. In
  245.    * particular, if session restore is configured to restore only in case of
  246.    * crash, this method returns false.
  247.    * @returns bool
  248.    */
  249.   isAutomaticRestoreEnabled: function () {
  250.     return Services.prefs.getBoolPref("browser.sessionstore.resume_session_once") ||
  251.            Services.prefs.getIntPref("browser.startup.page") == BROWSER_STARTUP_RESUME_SESSION;
  252.   },
  253.  
  254.   /**
  255.    * Determines whether there is a pending session restore.
  256.    * @returns bool
  257.    */
  258.   _willRestore: function () {
  259.     return this._sessionType == Ci.nsISessionStartup.RECOVER_SESSION ||
  260.            this._sessionType == Ci.nsISessionStartup.RESUME_SESSION;
  261.   },
  262.  
  263.   /**
  264.    * Returns whether we will restore a session that ends up replacing the
  265.    * homepage. The browser uses this to not start loading the homepage if
  266.    * we're going to stop its load anyway shortly after.
  267.    *
  268.    * This is meant to be an optimization for the average case that loading the
  269.    * session file finishes before we may want to start loading the default
  270.    * homepage. Should this be called before the session file has been read it
  271.    * will just return false.
  272.    *
  273.    * @returns bool
  274.    */
  275.   get willOverrideHomepage() {
  276.     if (this._initialState && this._willRestore()) {
  277.       let windows = this._initialState.windows || null;
  278.       // If there are valid windows with not only pinned tabs, signal that we
  279.       // will override the default homepage by restoring a session.
  280.       return windows && windows.some(w => w.tabs.some(t => !t.pinned));
  281.     }
  282.     return false;
  283.   },
  284.  
  285.   /**
  286.    * Get the type of pending session store, if any.
  287.    */
  288.   get sessionType() {
  289.     this._ensureInitialized();
  290.     return this._sessionType;
  291.   },
  292.  
  293.   // Ensure that initialization is complete. If initialization is not complete
  294.   // yet, something is attempting to use the old synchronous initialization,
  295.   // throw an error.
  296.   _ensureInitialized: function sss__ensureInitialized() {
  297.     if (!this._initialized) {
  298.       throw new Error("Session Store is not initialized.");
  299.     }
  300.   },
  301.  
  302.   /* ........ QueryInterface .............. */
  303.   QueryInterface : XPCOMUtils.generateQI([Ci.nsIObserver,
  304.                                           Ci.nsISupportsWeakReference,
  305.                                           Ci.nsISessionStartup]),
  306.   classID:          Components.ID("{ec7a6c20-e081-11da-8ad9-0800200c9a66}")
  307. };
  308.  
  309. this.NSGetFactory = XPCOMUtils.generateNSGetFactory([SessionStartup]);
  310.