2007-09-18 09:36:17 -07:00
|
|
|
/*
|
|
|
|
# ***** BEGIN LICENSE BLOCK *****
|
|
|
|
# * Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
|
|
# *
|
|
|
|
# * The contents of this file are subject to the Mozilla Public License Version
|
|
|
|
# * 1.1 (the "License"); you may not use this file except in compliance with
|
|
|
|
# * the License. You may obtain a copy of the License at
|
|
|
|
# * http://www.mozilla.org/MPL/
|
|
|
|
# *
|
|
|
|
# * Software distributed under the License is distributed on an "AS IS" basis,
|
|
|
|
# * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
|
|
# * for the specific language governing rights and limitations under the
|
|
|
|
# * License.
|
|
|
|
# *
|
|
|
|
# * The Original Code is the nsSessionStore component.
|
|
|
|
# *
|
|
|
|
# * The Initial Developer of the Original Code is
|
|
|
|
# * Simon Bünzli <zeniko@gmail.com>
|
|
|
|
# * Portions created by the Initial Developer are Copyright (C) 2006
|
|
|
|
# * the Initial Developer. All Rights Reserved.
|
|
|
|
# *
|
|
|
|
# * Contributor(s):
|
|
|
|
# * Dietrich Ayala <autonome@gmail.com>
|
|
|
|
# *
|
|
|
|
# * Alternatively, the contents of this file may be used under the terms of
|
|
|
|
# * either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
|
|
# * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
|
|
# * in which case the provisions of the GPL or the LGPL are applicable instead
|
|
|
|
# * of those above. If you wish to allow use of your version of this file only
|
|
|
|
# * under the terms of either the GPL or the LGPL, and not to allow others to
|
|
|
|
# * use your version of this file under the terms of the MPL, indicate your
|
|
|
|
# * decision by deleting the provisions above and replace them with the notice
|
|
|
|
# * and other provisions required by the GPL or the LGPL. If you do not delete
|
|
|
|
# * the provisions above, a recipient may use your version of this file under
|
|
|
|
# * the terms of any one of the MPL, the GPL or the LGPL.
|
|
|
|
# *
|
|
|
|
# * ***** END LICENSE BLOCK *****
|
|
|
|
*/
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
/**
|
2007-09-18 09:36:17 -07:00
|
|
|
# * 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
|
2009-05-01 22:17:22 -07:00
|
|
|
# * under the circumstances described below. If the auto-start Private Browsing
|
|
|
|
# * mode is active, however, the session is never restored.
|
2007-09-18 09:36:17 -07:00
|
|
|
# *
|
|
|
|
# * 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
|
2008-10-01 23:49:45 -07:00
|
|
|
# * if its value is "running", then it's assumed that the browser had previously
|
2007-09-18 09:36:17 -07:00
|
|
|
# * 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.
|
|
|
|
*/
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
/* :::::::: Constants and Helpers ::::::::::::::: */
|
|
|
|
|
|
|
|
const Cc = Components.classes;
|
|
|
|
const Ci = Components.interfaces;
|
|
|
|
const Cr = Components.results;
|
2010-01-22 12:21:41 -08:00
|
|
|
const Cu = Components.utils;
|
|
|
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
2010-03-19 15:43:01 -07:00
|
|
|
Cu.import("resource://gre/modules/Services.jsm");
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
const STATE_RUNNING_STR = "running";
|
2009-12-15 10:40:25 -08:00
|
|
|
const MAX_FILE_SIZE = 100 * 1024 * 1024; // 100 megabytes
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
function debug(aMsg) {
|
|
|
|
aMsg = ("SessionStartup: " + aMsg).replace(/\S{80}/g, "$&\n");
|
2010-03-19 15:43:01 -07:00
|
|
|
Services.console.logStringMessage(aMsg);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* :::::::: The Service ::::::::::::::: */
|
|
|
|
|
|
|
|
function SessionStartup() {
|
|
|
|
}
|
|
|
|
|
|
|
|
SessionStartup.prototype = {
|
|
|
|
|
|
|
|
// the state to restore at startup
|
2011-07-15 09:42:21 -07:00
|
|
|
_initialState: null,
|
2008-02-14 13:53:33 -08:00
|
|
|
_sessionType: Ci.nsISessionStartup.NO_SESSION,
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
/* ........ Global Event Handlers .............. */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Initialize the component
|
|
|
|
*/
|
|
|
|
init: function sss_init() {
|
2009-05-01 22:17:22 -07:00
|
|
|
// do not need to initialize anything in auto-started private browsing sessions
|
|
|
|
let pbs = Cc["@mozilla.org/privatebrowsing;1"].
|
|
|
|
getService(Ci.nsIPrivateBrowsingService);
|
2010-10-02 08:53:37 -07:00
|
|
|
if (pbs.autoStarted || pbs.lastChangedByCommandLine)
|
2009-05-01 22:17:22 -07:00
|
|
|
return;
|
|
|
|
|
2008-10-11 11:47:31 -07:00
|
|
|
let prefBranch = Cc["@mozilla.org/preferences-service;1"].
|
|
|
|
getService(Ci.nsIPrefService).getBranch("browser.");
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
// get file references
|
|
|
|
var dirService = Cc["@mozilla.org/file/directory_service;1"].
|
|
|
|
getService(Ci.nsIProperties);
|
2008-09-07 05:33:26 -07:00
|
|
|
let sessionFile = dirService.get("ProfD", Ci.nsILocalFile);
|
|
|
|
sessionFile.append("sessionstore.js");
|
2011-08-04 15:22:08 -07:00
|
|
|
|
|
|
|
let doResumeSessionOnce = prefBranch.getBoolPref("sessionstore.resume_session_once");
|
|
|
|
let doResumeSession = doResumeSessionOnce ||
|
2008-10-11 11:47:31 -07:00
|
|
|
prefBranch.getIntPref("startup.page") == 3;
|
2011-02-07 17:50:31 -08:00
|
|
|
|
|
|
|
// only continue if the session file exists
|
|
|
|
if (!sessionFile.exists())
|
2008-09-07 05:33:26 -07:00
|
|
|
return;
|
2011-02-07 17:50:31 -08:00
|
|
|
|
2008-09-07 05:33:26 -07:00
|
|
|
// get string containing session state
|
2011-07-15 09:42:21 -07:00
|
|
|
let iniString = this._readStateFile(sessionFile);
|
|
|
|
if (!iniString)
|
2008-09-07 05:33:26 -07:00
|
|
|
return;
|
2010-06-09 13:55:45 -07:00
|
|
|
|
|
|
|
// parse the session state into a JS object
|
2008-09-07 05:33:26 -07:00
|
|
|
try {
|
2010-06-09 13:55:45 -07:00
|
|
|
// remove unneeded braces (added for compatibility with Firefox 2.0 and 3.0)
|
2011-07-15 09:42:21 -07:00
|
|
|
if (iniString.charAt(0) == '(')
|
|
|
|
iniString = iniString.slice(1, -1);
|
2010-06-09 13:55:45 -07:00
|
|
|
try {
|
2011-07-15 09:42:21 -07:00
|
|
|
this._initialState = JSON.parse(iniString);
|
2010-06-09 13:55:45 -07:00
|
|
|
}
|
|
|
|
catch (exJSON) {
|
2011-08-21 16:02:24 -07:00
|
|
|
var s = new Cu.Sandbox("about:blank", {sandboxName: 'nsSessionStartup'});
|
2011-07-15 09:42:21 -07:00
|
|
|
this._initialState = Cu.evalInSandbox("(" + iniString + ")", s);
|
2010-06-09 13:55:45 -07:00
|
|
|
}
|
2011-08-04 15:22:08 -07:00
|
|
|
|
|
|
|
// If this is a normal restore then throw away any previous session
|
|
|
|
if (!doResumeSessionOnce)
|
|
|
|
delete this._initialState.lastSessionState;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
2010-06-09 13:55:45 -07:00
|
|
|
catch (ex) { debug("The session file is invalid: " + ex); }
|
|
|
|
|
2011-02-07 17:50:31 -08:00
|
|
|
let resumeFromCrash = prefBranch.getBoolPref("sessionstore.resume_from_crash");
|
2008-09-07 05:33:26 -07:00
|
|
|
let lastSessionCrashed =
|
2011-07-15 09:42:21 -07:00
|
|
|
this._initialState && this._initialState.session &&
|
|
|
|
this._initialState.session.state &&
|
|
|
|
this._initialState.session.state == STATE_RUNNING_STR;
|
2011-02-07 17:50:31 -08:00
|
|
|
|
2011-06-28 16:54:36 -07:00
|
|
|
// Report shutdown success via telemetry. Shortcoming here are
|
|
|
|
// being-killed-by-OS-shutdown-logic, shutdown freezing after
|
|
|
|
// session restore was written, etc.
|
|
|
|
let Telemetry = Cc["@mozilla.org/base/telemetry;1"].getService(Ci.nsITelemetry);
|
|
|
|
Telemetry.getHistogramById("SHUTDOWN_OK").add(!lastSessionCrashed);
|
|
|
|
|
2008-09-07 05:33:26 -07:00
|
|
|
// set the startup type
|
2008-10-11 11:47:31 -07:00
|
|
|
if (lastSessionCrashed && resumeFromCrash)
|
2008-09-07 05:33:26 -07:00
|
|
|
this._sessionType = Ci.nsISessionStartup.RECOVER_SESSION;
|
|
|
|
else if (!lastSessionCrashed && doResumeSession)
|
|
|
|
this._sessionType = Ci.nsISessionStartup.RESUME_SESSION;
|
2011-07-15 09:42:21 -07:00
|
|
|
else if (this._initialState)
|
2010-09-10 10:57:28 -07:00
|
|
|
this._sessionType = Ci.nsISessionStartup.DEFER_SESSION;
|
2008-09-07 05:33:26 -07:00
|
|
|
else
|
2011-07-15 09:42:21 -07:00
|
|
|
this._initialState = null; // reset the state
|
2008-02-14 13:53:33 -08:00
|
|
|
|
2011-06-17 12:37:39 -07:00
|
|
|
// wait for the first browser window to open
|
|
|
|
// Don't reset the initial window's default args (i.e. the home page(s))
|
|
|
|
// if all stored tabs are pinned.
|
|
|
|
if (this.doRestore() &&
|
2011-07-15 09:42:21 -07:00
|
|
|
(!this._initialState.windows ||
|
|
|
|
!this._initialState.windows.every(function (win)
|
2011-06-17 12:37:39 -07:00
|
|
|
win.tabs.every(function (tab) tab.pinned))))
|
|
|
|
Services.obs.addObserver(this, "domwindowopened", true);
|
|
|
|
|
|
|
|
Services.obs.addObserver(this, "sessionstore-windows-restored", true);
|
2007-03-22 10:30:00 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handle notifications
|
|
|
|
*/
|
|
|
|
observe: function sss_observe(aSubject, aTopic, aData) {
|
|
|
|
switch (aTopic) {
|
|
|
|
case "app-startup":
|
2010-03-19 15:43:01 -07:00
|
|
|
Services.obs.addObserver(this, "final-ui-startup", true);
|
|
|
|
Services.obs.addObserver(this, "quit-application", true);
|
2007-03-22 10:30:00 -07:00
|
|
|
break;
|
|
|
|
case "final-ui-startup":
|
2010-03-19 15:43:01 -07:00
|
|
|
Services.obs.removeObserver(this, "final-ui-startup");
|
|
|
|
Services.obs.removeObserver(this, "quit-application");
|
2007-03-22 10:30:00 -07:00
|
|
|
this.init();
|
|
|
|
break;
|
2008-08-19 10:08:30 -07:00
|
|
|
case "quit-application":
|
2008-09-05 03:05:34 -07:00
|
|
|
// no reason for initializing at this point (cf. bug 409115)
|
2010-03-19 15:43:01 -07:00
|
|
|
Services.obs.removeObserver(this, "final-ui-startup");
|
|
|
|
Services.obs.removeObserver(this, "quit-application");
|
2008-08-19 10:08:30 -07:00
|
|
|
break;
|
2007-03-22 10:30:00 -07:00
|
|
|
case "domwindowopened":
|
|
|
|
var window = aSubject;
|
|
|
|
var self = this;
|
|
|
|
window.addEventListener("load", function() {
|
|
|
|
self._onWindowOpened(window);
|
|
|
|
window.removeEventListener("load", arguments.callee, false);
|
|
|
|
}, false);
|
|
|
|
break;
|
2011-01-20 13:43:32 -08:00
|
|
|
case "sessionstore-windows-restored":
|
|
|
|
Services.obs.removeObserver(this, "sessionstore-windows-restored");
|
2011-07-15 09:42:21 -07:00
|
|
|
// free _initialState after nsSessionStore is done with it
|
|
|
|
this._initialState = null;
|
2008-10-11 11:58:21 -07:00
|
|
|
this._sessionType = Ci.nsISessionStartup.NO_SESSION;
|
|
|
|
break;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Removes the default arguments from the first browser window
|
2007-03-26 07:22:14 -07:00
|
|
|
* (and removes the "domwindowopened" observer afterwards).
|
2007-03-22 10:30:00 -07:00
|
|
|
*/
|
|
|
|
_onWindowOpened: function sss_onWindowOpened(aWindow) {
|
|
|
|
var wType = aWindow.document.documentElement.getAttribute("windowtype");
|
|
|
|
if (wType != "navigator:browser")
|
|
|
|
return;
|
|
|
|
|
2007-03-26 07:22:14 -07:00
|
|
|
/**
|
|
|
|
* Note: this relies on the fact that nsBrowserContentHandler will return
|
2007-03-26 07:23:21 -07:00
|
|
|
* a different value the first time its getter is called after an update,
|
2007-03-26 07:22:14 -07:00
|
|
|
* due to its needHomePageOverride() logic. We don't want to remove the
|
|
|
|
* default arguments in the update case, since they include the "What's
|
|
|
|
* New" page.
|
|
|
|
*
|
|
|
|
* Since we're garanteed to be at least the second caller of defaultArgs
|
|
|
|
* (nsBrowserContentHandler calls it to determine which arguments to pass
|
|
|
|
* at startup), we know that if the window's arguments don't match the
|
|
|
|
* current defaultArguments, we're either in the update case, or we're
|
|
|
|
* launching a non-default browser window, so we shouldn't remove the
|
|
|
|
* window's arguments.
|
|
|
|
*/
|
2007-03-22 10:30:00 -07:00
|
|
|
var defaultArgs = Cc["@mozilla.org/browser/clh;1"].
|
|
|
|
getService(Ci.nsIBrowserHandler).defaultArgs;
|
|
|
|
if (aWindow.arguments && aWindow.arguments[0] &&
|
|
|
|
aWindow.arguments[0] == defaultArgs)
|
|
|
|
aWindow.arguments[0] = null;
|
2009-12-15 10:40:14 -08:00
|
|
|
|
2010-10-02 08:53:37 -07:00
|
|
|
try {
|
|
|
|
Services.obs.removeObserver(this, "domwindowopened");
|
|
|
|
} catch (e) {
|
|
|
|
// This might throw if we're removing the observer multiple times,
|
|
|
|
// but this is safe to ignore.
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
/* ........ Public API ................*/
|
|
|
|
|
|
|
|
/**
|
2011-07-25 16:29:57 -07:00
|
|
|
* Get the session state as a jsval
|
2007-03-22 10:30:00 -07:00
|
|
|
*/
|
|
|
|
get state() {
|
2011-07-15 09:42:21 -07:00
|
|
|
return this._initialState;
|
2007-03-22 10:30:00 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
2007-03-26 07:22:14 -07:00
|
|
|
* Determine whether there is a pending session restore.
|
2007-03-22 10:30:00 -07:00
|
|
|
* @returns bool
|
|
|
|
*/
|
|
|
|
doRestore: function sss_doRestore() {
|
2010-09-10 10:57:28 -07:00
|
|
|
return this._sessionType == Ci.nsISessionStartup.RECOVER_SESSION ||
|
|
|
|
this._sessionType == Ci.nsISessionStartup.RESUME_SESSION;
|
2008-02-14 13:53:33 -08:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the type of pending session store, if any.
|
|
|
|
*/
|
|
|
|
get sessionType() {
|
|
|
|
return this._sessionType;
|
2007-03-22 10:30:00 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
/* ........ Storage API .............. */
|
|
|
|
|
2008-08-19 23:39:09 -07:00
|
|
|
/**
|
|
|
|
* Reads a session state file into a string and lets
|
|
|
|
* observers modify the state before it's being used
|
|
|
|
*
|
|
|
|
* @param aFile is any nsIFile
|
|
|
|
* @returns a session state string
|
|
|
|
*/
|
|
|
|
_readStateFile: function sss_readStateFile(aFile) {
|
|
|
|
var stateString = Cc["@mozilla.org/supports-string;1"].
|
|
|
|
createInstance(Ci.nsISupportsString);
|
|
|
|
stateString.data = this._readFile(aFile) || "";
|
2009-12-15 10:40:14 -08:00
|
|
|
|
2010-03-19 15:43:01 -07:00
|
|
|
Services.obs.notifyObservers(stateString, "sessionstore-state-read", "");
|
2009-12-15 10:40:14 -08:00
|
|
|
|
2008-08-19 23:39:09 -07:00
|
|
|
return stateString.data;
|
|
|
|
},
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
/**
|
|
|
|
* reads a file into a string
|
|
|
|
* @param aFile
|
|
|
|
* nsIFile
|
|
|
|
* @returns string
|
|
|
|
*/
|
|
|
|
_readFile: function sss_readFile(aFile) {
|
|
|
|
try {
|
|
|
|
var stream = Cc["@mozilla.org/network/file-input-stream;1"].
|
|
|
|
createInstance(Ci.nsIFileInputStream);
|
|
|
|
stream.init(aFile, 0x01, 0, 0);
|
|
|
|
var cvstream = Cc["@mozilla.org/intl/converter-input-stream;1"].
|
|
|
|
createInstance(Ci.nsIConverterInputStream);
|
2009-12-15 10:40:25 -08:00
|
|
|
|
|
|
|
var fileSize = stream.available();
|
|
|
|
if (fileSize > MAX_FILE_SIZE)
|
|
|
|
throw "SessionStartup: sessionstore.js was not processed because it was too large.";
|
|
|
|
|
|
|
|
cvstream.init(stream, "UTF-8", fileSize, Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
|
2007-03-22 10:30:00 -07:00
|
|
|
var data = {};
|
2009-12-15 10:40:25 -08:00
|
|
|
cvstream.readString(fileSize, data);
|
|
|
|
var content = data.value;
|
2007-03-22 10:30:00 -07:00
|
|
|
cvstream.close();
|
2009-12-15 10:40:25 -08:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
return content.replace(/\r\n?/g, "\n");
|
|
|
|
}
|
2010-01-22 12:21:41 -08:00
|
|
|
catch (ex) { Cu.reportError(ex); }
|
2009-12-15 10:40:25 -08:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
return null;
|
|
|
|
},
|
|
|
|
|
2007-09-18 09:36:17 -07:00
|
|
|
/* ........ QueryInterface .............. */
|
|
|
|
QueryInterface : XPCOMUtils.generateQI([Ci.nsIObserver,
|
|
|
|
Ci.nsISupportsWeakReference,
|
|
|
|
Ci.nsISessionStartup]),
|
|
|
|
classID: Components.ID("{ec7a6c20-e081-11da-8ad9-0800200c9a66}"),
|
2007-03-22 10:30:00 -07:00
|
|
|
};
|
|
|
|
|
2010-06-22 09:59:15 -07:00
|
|
|
var NSGetFactory = XPCOMUtils.generateNSGetFactory([SessionStartup]);
|