mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-06-02 21:19:57 +00:00
07458c5a05
The "average case" is clearly not that loading the session file finishes before the load of the initial tab is started (quite the opposite). Instead of defaulting to false when the session store has not yet been loaded, we make this dependent on the user preference instead which is a definitive flag that the home page in the first tab WILL be overridden. This resolves #1310.
287 lines
9.7 KiB
JavaScript
287 lines
9.7 KiB
JavaScript
/* 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);
|
|
}
|
|
|
|
let 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 sss_init() {
|
|
// do not need to initialize anything in auto-started private browsing sessions
|
|
if (PrivateBrowsingUtils.permanentPrivateBrowsing) {
|
|
this._initialized = true;
|
|
gOnceInitializedDeferred.resolve();
|
|
return;
|
|
}
|
|
|
|
_SessionFile.read().then(
|
|
this._onSessionFileRead.bind(this)
|
|
);
|
|
},
|
|
|
|
// Wrap a string as a nsISupports
|
|
_createSupportsString: function ssfi_createSupportsString(aData) {
|
|
let string = Cc["@mozilla.org/supports-string;1"]
|
|
.createInstance(Ci.nsISupportsString);
|
|
string.data = aData;
|
|
return string;
|
|
},
|
|
|
|
_onSessionFileRead: function sss_onSessionFileRead(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 sss_observe(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 sss_doRestore() {
|
|
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.
|
|
*
|
|
* @returns bool
|
|
*/
|
|
get willOverrideHomepage() {
|
|
if (Services.prefs.getIntPref("browser.startup.page") == 3) {
|
|
// "Restore windows and tabs from last time" selected. This will
|
|
// always override the default home page.
|
|
return true;
|
|
}
|
|
// Other settings won't override.
|
|
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 sss__ensureInitialized() {
|
|
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]);
|