Split session restore into 2 services (337320), r=mconnor

This commit is contained in:
dietrich%mozilla.com 2006-06-18 00:42:08 +00:00
Родитель 822d9782f7
Коммит 06967eff63
7 изменённых файлов: 747 добавлений и 150 удалений

Просмотреть файл

@ -1100,6 +1100,27 @@ function delayedStartup()
// Initialize the microsummary service by retrieving it, prompting its factory
// to create its singleton, whose constructor initializes the service.
Cc["@mozilla.org/microsummary/service;1"].getService(Ci.nsIMicrosummaryService);
// initialize the session-restore service
var ssEnabled = true;
var prefBranch = Cc["@mozilla.org/preferences-service;1"].
getService(Ci.nsIPrefBranch);
try {
ssEnabled = prefBranch.getBoolPref("browser.sessionstore.enabled");
} catch (ex) {}
if (ssEnabled) {
var wType = window.document.documentElement.getAttribute("windowtype");
if (wType == "navigator:browser") {
try {
var ss = Cc["@mozilla.org/browser/sessionstore;1"].
getService(Ci.nsISessionStore);
ss.init(window);
} catch(ex) {
dump("nsSessionStore could not be initialized: " + ex + "\n");
}
}
}
}
function BrowserShutdown()

Просмотреть файл

@ -43,7 +43,10 @@ include $(DEPTH)/config/autoconf.mk
MODULE = sessionstore
XPIDL_MODULE = sessionstore
XPIDLSRCS = nsISessionStore.idl
XPIDLSRCS = \
nsISessionStartup.idl \
nsISessionStore.idl \
$(NULL)
DIRS = src

Просмотреть файл

@ -0,0 +1,57 @@
/* ***** 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 Session Restore 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 <dietrich@mozilla.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 ***** */
#include "nsISupports.idl"
/**
* nsISessionStore keeps track of the current browsing state - i.e.
* tab history, cookies, scroll state, form data, POSTDATA and window features
* - and allows to restore everything into one window.
*/
[scriptable, uuid(41cfaf30-f0ce-11da-8ad9-0800200c9a66)]
interface nsISessionStartup: nsISupports
{
// Get session state as string
readonly attribute string state;
/**
* Determine if session should be restored
*/
boolean doRestore();
};

Просмотреть файл

@ -38,17 +38,19 @@
#include "nsISupports.idl"
interface nsIDOMWindow;
/**
* nsISessionStore keeps track of the current browsing state - i.e.
* tab history, cookies, scroll state, form data, POSTDATA and window features
* - and allows to restore everything into one window.
*/
[scriptable, uuid(07fada1e-c3a6-11da-95a7-00e08161165f)]
[scriptable, uuid(ca4e1d40-ec1b-11da-8ad9-0800200c9a66)]
interface nsISessionStore : nsISupports
{
/**
* Initialize the service
*/
void init();
void init(in nsIDOMWindow aWindow);
};

Просмотреть файл

@ -39,6 +39,9 @@ VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
EXTRA_PP_COMPONENTS = nsSessionStore.js
EXTRA_PP_COMPONENTS = \
nsSessionStore.js \
nsSessionStartup.js \
$(NULL)
include $(topsrcdir)/config/rules.mk

Просмотреть файл

@ -0,0 +1,580 @@
/* ***** 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 ***** */
/**
* Session Storage and Restoration
*
* Overview
* This service user's session file at startup, and makes a determination as to
* whether the session should be restored. It fill restore the session under
* the circumstances described below.
*
* Crash Detection
* The initial segment of the INI file is has a single field, "state", 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 it's 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 boolean pref
* browser.sessionstore.resume_session is set to true.
*/
/* :::::::: Constants and Helpers ::::::::::::::: */
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;
const CID = Components.ID("{ec7a6c20-e081-11da-8ad9-0800200c9a66}");
const CONTRACT_ID = "@mozilla.org/browser/sessionstartup;1";
const CLASS_NAME = "Browser Session Startup Service";
const STATE_STOPPED = 0;
const STATE_RUNNING = 1;
const STATE_QUITTING = -1;
const STATE_STOPPED_STR = "stopped";
const STATE_RUNNING_STR = "running";
/* :::::::: Pref Defaults :::::::::::::::::::: */
// whether the service is enabled
const DEFAULT_ENABLED = true;
// resume the current session at startup (otherwise just recover)
const DEFAULT_RESUME_SESSION = false;
// resume the current session at startup just this once
const DEFAULT_RESUME_SESSION_ONCE = false;
// resume the current session at startup if it had previously crashed
const DEFAULT_RESUME_FROM_CRASH = true;
function debug(aMsg) {
aMsg = ("SessionStartup: " + aMsg).replace(/\S{80}/g, "$&\n");
Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService)
.logStringMessage(aMsg);
}
/* :::::::: The Service ::::::::::::::: */
function SessionStartup() {
}
SessionStartup.prototype = {
// set default load state
_loadState: STATE_STOPPED,
/* ........ Global Event Handlers .............. */
/**
* Initialize the component
*/
init: function sss_init() {
debug("startup init");
this._prefBranch = Cc["@mozilla.org/preferences-service;1"].
getService(Ci.nsIPrefService).
getBranch("browser.");
this._prefBranch.QueryInterface(Ci.nsIPrefBranch2);
// if the service is disabled, do not init
if (!this._getPref("sessionstore.enabled", DEFAULT_ENABLED))
return;
var observerService = Cc["@mozilla.org/observer-service;1"].
getService(Ci.nsIObserverService);
// get file references
var dirService = Cc["@mozilla.org/file/directory_service;1"].
getService(Ci.nsIProperties);
this._sessionFile = dirService.get("ProfD", Ci.nsILocalFile);
this._sessionFileBackup = this._sessionFile.clone();
this._sessionFile.append("sessionstore.ini");
this._sessionFileBackup.append("sessionstore.bak");
// only read the session file if config allows possibility of restoring
var resumeFromCrash = this._getPref("sessionstore.resume_from_crash", DEFAULT_RESUME_FROM_CRASH);
if (resumeFromCrash || this._doResumeSession()) {
// get string containing session state
this._iniString = this._readFile(this._getSessionFile());
if (this._iniString) {
try {
// parse the session state into JS objects
this._initialState = IniObjectSerializer.decode(this._iniString);
// set bool detecting crash
this._lastSessionCrashed =
this._initialState.Session && this._initialState.Session.state &&
this._initialState.Session.state == STATE_RUNNING_STR;
// invalid .INI file - nothing can be restored
}
catch (ex) { debug("The session file is invalid: " + ex); }
}
}
// prompt and check prefs
this._doRestore = this._lastSessionCrashed ? this._doRecoverSession() : this._doResumeSession();
if (this._initialState && !this._doRestore) {
delete this._iniString; // delete state string
delete this._initialState; // delete state
}
},
/**
* Handle notifications
*/
observe: function sss_observe(aSubject, aTopic, aData) {
var observerService = Cc["@mozilla.org/observer-service;1"].
getService(Ci.nsIObserverService);
// for event listeners
var _this = this;
switch (aTopic) {
case "app-startup":
observerService.addObserver(this, "final-ui-startup", true);
break;
case "final-ui-startup":
observerService.removeObserver(this, "final-ui-startup");
this.init();
break;
}
},
/* ........ Public API ................*/
/**
* Get the session state as a string
*/
get state() {
return this._iniString;
},
/**
* Determine if session should be restored
* @returns bool
*/
doRestore: function sss_doRestore() {
return this._doRestore;
},
/* ........ Disk Access .............. */
/**
* get session datafile (or its backup)
* @returns nsIFile
*/
_getSessionFile: function sss_getSessionFile(aBackup) {
return aBackup ? this._sessionFileBackup : this._sessionFile;
},
/* ........ Auxiliary Functions .............. */
/**
* Whether or not to resume session, if not recovering from a crash
* Returns true if:
* - pref is set to always resume sessions
* - pref is set to resume session once
* - user configured startup page to be the last-visited page
* @returns bool
*/
_doResumeSession: function sss_doResumeSession() {
return this._getPref("sessionstore.resume_session", DEFAULT_RESUME_SESSION) ||
this._getPref("sessionstore.resume_session_once", DEFAULT_RESUME_SESSION_ONCE) ||
this._getPref("startup.page", 1) == 2;
},
/**
* prompt user whether or not to restore the previous session,
* if the browser crashed
* @returns bool
*/
_doRecoverSession: function sss_doRecoverSession() {
// do not prompt or resume, post-crash
if (!this._getPref("sessionstore.resume_from_crash", DEFAULT_RESUME_FROM_CRASH))
return false;
// if the prompt fails, recover anyway
var recover = true;
// allow extensions to hook in a more elaborate restore prompt
//zeniko: drop this when we're using our own dialog instead of a standard prompt
var dialogURI = this._getPref("sessionstore.restore_prompt_uri");
try {
if (dialogURI) { // extension provided dialog
var params = Cc["@mozilla.org/embedcomp/dialogparam;1"].
createInstance(Ci.nsIDialogParamBlock);
// default to recovering
params.SetInt(0, 0);
Cc["@mozilla.org/embedcomp/window-watcher;1"].
getService(Ci.nsIWindowWatcher).
openWindow(null, dialogURI, "_blank",
"chrome,modal,centerscreen,titlebar", params);
recover = params.GetInt(0) == 0;
}
else { // basic prompt with no options
// get app name from branding properties
var brandStringBundle = this._getStringBundle("chrome://branding/locale/brand.properties");
var brandShortName = brandStringBundle.GetStringFromName("brandShortName");
// create prompt strings
var ssStringBundle = this._getStringBundle("chrome://browser/locale/sessionstore.properties");
var restoreTitle = ssStringBundle.formatStringFromName("restoreTitle", [brandShortName], 1);
var restoreText = ssStringBundle.formatStringFromName("restoreText", [brandShortName], 1);
var buttonTitle = ssStringBundle.GetStringFromName("buttonTitle");
var promptService = Cc["@mozilla.org/embedcomp/prompt-service;1"].
getService(Ci.nsIPromptService);
// set the buttons that will appear on the dialog
var flags = promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_1 +
promptService.BUTTON_TITLE_CANCEL * promptService.BUTTON_POS_0;
var buttonChoice = promptService.confirmEx(null, restoreTitle, restoreText,
flags, null, buttonTitle, null,
null, {});
recover = (buttonChoice == 1);
}
}
catch (ex) { dump(ex + "\n"); } // if the prompt fails, recover anyway
return recover;
},
/**
* Convenience method to get localized string bundles
* @param aURI
* @returns nsIStringBundle
*/
_getStringBundle: function sss_getStringBundle(aURI) {
var bundleService = Cc["@mozilla.org/intl/stringbundle;1"].
getService(Ci.nsIStringBundleService);
var appLocale = Cc["@mozilla.org/intl/nslocaleservice;1"].
getService(Ci.nsILocaleService).getApplicationLocale();
return bundleService.createBundle(aURI, appLocale);
},
/* ........ Storage API .............. */
/**
* basic pref reader
* @param aName
* @param aDefault
* @param aUseRootBranch
*/
_getPref: function sss_getPref(aName, aDefault) {
var pb = this._prefBranch;
try {
switch (pb.getPrefType(aName)) {
case pb.PREF_STRING:
return pb.getCharPref(aName);
case pb.PREF_BOOL:
return pb.getBoolPref(aName);
case pb.PREF_INT:
return pb.getIntPref(aName);
default:
return aDefault;
}
}
catch(ex) {
return aDefault;
}
},
/**
* 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);
cvstream.init(stream, "UTF-8", 1024, Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
var content = "";
var data = {};
while (cvstream.readString(4096, data)) {
content += data.value;
}
cvstream.close();
return content.replace(/\r\n?/g, "\n");
}
catch (ex) { } // inexisting file?
return null;
},
/* ........ QueryInterface .............. */
QueryInterface: function(aIID) {
if (!aIID.equals(Ci.nsISupports) && !aIID.equals(Ci.nsIObserver) &&
!aIID.equals(Ci.nsISupportsWeakReference) &&
!aIID.equals(Ci.nsISessionStartup)) {
Components.returnCode = Cr.NS_ERROR_NO_INTERFACE;
return null;
}
return this;
}
};
/* :::::::: File Format Converter ::::::::::::::: */
/**
* Sessions are serialized to and restored from .INI files
*
* The serialization can store nested JS objects and arrays. Each object or array
* gets a session header containing the "path" to it (using dots as name separators).
* Arrays are stored as a list of objects where the path name is the array name plus
* the object's index (e.g. [Window2.Cookies] is the Cookies object of the second entry
* of the Window array).
*
* The .INI format used here is for performance and convenience reasons somewhat restricted:
* * files must be stored in a Unicode compatible encoding (such as UTF-8)
* * section headers and key names are case-sensitive
* * dots in section names have special meaning (separating the names in the object hierarchy)
* * numbers can have some special meaning as well (see below)
* * keys could occur in the "root" section (i.e. before the first section header)
*
* Despite these restrictions, these files should be quite interoperable with other .INI
* parsers since we're quite conservative in what we emit and pretty tolerant in what we
* accept (except for the encoding and the case sensitivity issues - which could be
* remedied by encoding all values containing non US-ASCII characters and by requesting
* all keys to be lower-cased (and enforcing this when restoring).
*
* Implementation details you have to be aware of:
* * empty (string) values aren't stored at all
* * keys beginning with an underscore are ignored
* * Arrays are stored with index-base 1 (NOT: 0!)
* * Array lengths are stored implicitely
* * true and false are (re)stored as boolean values
* * positive integers are returned as Number (all other numbers as String)
* * string values can contain all possible characters
* (they are automatically URI encoded/decoded should that be necessary)
*/
var IniObjectSerializer = {
encode: function(aObj, aPrefix) {
aPrefix = aPrefix ? aPrefix + "." : "";
var ini = [], iniChildren = [];
for (var key in aObj) {
if (!key || /[;\[\]=]/.test(key)) {
debug("Ignoring invalid key name: '" + key + "'!");
continue;
}
else if (key.charAt(0) == "_") { // ignore this key
continue;
}
var value = aObj[key];
if (typeof value == "boolean" || typeof value == "number") {
ini.push(key + "=" + value);
}
else if (typeof value == "string" && value) {
ini.push(key + "=" + (/^\s|[%\t\r\n;]|\s$/.test(value) ? encodeURI(value).replace(/;/g, "%3B") : value));
}
else if (value instanceof Array) {
for (var i = 0; i < value.length; i++) {
if (value[i] instanceof Object) {
iniChildren.push("[" + aPrefix + key + (i + 1) + "]");
iniChildren.push(this.encode(value[i], aPrefix + key + (i + 1)));
}
}
}
else if (typeof value == "object" && value) {
iniChildren.push("[" + aPrefix + key + "]");
iniChildren.push(this.encode(value, aPrefix + key));
}
}
return ini.concat(iniChildren).join("\n");
},
decode: function(aIniString) {
var rootObject = {};
var obj = rootObject;
var lines = aIniString.split("\n");
for (var i = 0; i < lines.length; i++) {
var line = lines[i].replace(/;.*/, "");
try {
if (line.charAt(0) == "[") {
obj = this._getObjForHeader(rootObject, line);
}
else if (/\S/.test(line)) { // ignore blank lines
this._setValueForLine(obj, line);
}
}
catch (ex) {
throw new Error("Error at line " + (i + 1) + ": " + ex.description);
}
}
return rootObject;
},
// Object which results after traversing the path indicated in the section header
_getObjForHeader: function(aObj, aLine) {
var names = aLine.split("]")[0].substr(1).split(".");
for (var i = 0; i < names.length; i++) {
var name = names[i];
if (!names[i]) {
throw new Error("Invalid header: [" + names.join(".") + "]!");
}
if (/(\d+)$/.test(names[i])) {
names[i] = names[i].slice(0, -RegExp.$1.length);
var ix = parseInt(RegExp.$1) - 1;
aObj = aObj[names[i]] = aObj[names[i]] || [];
aObj = aObj[ix] = aObj[ix] || {};
}
else {
aObj = aObj[names[i]] = aObj[names[i]] || {};
}
}
return aObj;
},
_setValueForLine: function(aObj, aLine) {
var ix = aLine.indexOf("=");
if (ix < 1) {
throw new Error("Invalid entry: " + aLine + "!");
}
var key = this._trim(aLine.substr(0, ix));
var value = this._trim(aLine.substr(ix + 1));
if (value == "true" || value == "false") {
value = (value == "true");
}
else if (/^\d+$/.test(value)) {
value = parseInt(value);
}
else if (value.indexOf("%") > -1) {
value = decodeURI(value.replace(/%3B/gi, ";"));
}
aObj[key] = value;
},
_trim: function(aString) {
return aString.replace(/^\s+|\s+$/g, "");
}
};
/* :::::::: Service Registration & Initialization ::::::::::::::: */
/* ........ nsIModule .............. */
const SessionStartupModule = {
getClassObject: function(aCompMgr, aCID, aIID) {
if (aCID.equals(CID)) {
return SessionStartupFactory;
}
Components.returnCode = Cr.NS_ERROR_NOT_REGISTERED;
return null;
},
registerSelf: function(aCompMgr, aFileSpec, aLocation, aType) {
aCompMgr.QueryInterface(Ci.nsIComponentRegistrar);
aCompMgr.registerFactoryLocation(CID, CLASS_NAME, CONTRACT_ID, aFileSpec, aLocation, aType);
var catMan = Cc["@mozilla.org/categorymanager;1"].
getService(Ci.nsICategoryManager);
catMan.addCategoryEntry("app-startup", CLASS_NAME, "service," + CONTRACT_ID, true, true);
},
unregisterSelf: function(aCompMgr, aLocation, aType) {
aCompMgr.QueryInterface(Ci.nsIComponentRegistrar);
aCompMgr.unregisterFactoryLocation(CID, aLocation);
var catMan = Cc["@mozilla.org/categorymanager;1"].
getService(Ci.nsICategoryManager);
catMan.deleteCategoryEntry( "app-startup", "service," + CONTRACT_ID, true);
},
canUnload: function(aCompMgr) {
return true;
}
}
/* ........ nsIFactory .............. */
const SessionStartupFactory = {
createInstance: function(aOuter, aIID) {
if (aOuter != null) {
Components.returnCode = Cr.NS_ERROR_NO_AGGREGATION;
return null;
}
return (new SessionStartup()).QueryInterface(aIID);
},
lockFactory: function(aLock) { },
QueryInterface: function(aIID) {
if (!aIID.equals(Ci.nsISupports) && !aIID.equals(Ci.nsIModule) &&
!aIID.equals(Ci.nsIFactory) && !aIID.equals(Ci.nsISessionStartup)) {
Components.returnCode = Cr.NS_ERROR_NO_INTERFACE;
return null;
}
return this;
}
};
function NSGetModule(aComMgr, aFileSpec) {
return SessionStartupModule;
}

Просмотреть файл

@ -43,26 +43,8 @@
* This service keeps track of a user's session, storing the various bits
* required to return the browser to it's current state. The relevant data is
* stored in memory, and is periodically saved to disk in an ini file in the
* profile directory. The service starts when the browser starts, and will restore
* the session from the information contained in the ini file under circumstances
* described below.
*
* Crash Detection
* The initial segment of the INI file is has a single field, "state", 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
* it's 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 boolean pref
* browser.sessionstore.resume_session is set to true.
* profile directory. The service is started at first window load, in delayedStartup, and will restore
* the session from the data received from the nsSessionStartup service.
*/
/* :::::::: Constants and Helpers ::::::::::::::: */
@ -149,8 +131,8 @@ const CAPABILITIES = [
function debug(aMsg) {
aMsg = ("SessionStore: " + aMsg).replace(/\S{80}/g, "$&\n");
Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService).
logStringMessage(aMsg);
Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService)
.logStringMessage(aMsg);
}
/* :::::::: The Service ::::::::::::::: */
@ -192,18 +174,24 @@ SessionStoreService.prototype = {
/**
* Initialize the component
*/
init: function sss_init() {
this._prefBranch =
Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefService).
getBranch("browser.");
init: function sss_init(aWindow) {
debug("store init");
if (!aWindow || aWindow == null)
return;
if (this._loadState == STATE_RUNNING)
return;
this._prefBranch = Cc["@mozilla.org/preferences-service;1"].
getService(Ci.nsIPrefService).getBranch("browser.");
this._prefBranch.QueryInterface(Ci.nsIPrefBranch2);
// if the service is disabled, do not init
if(!this._getPref("sessionstore.enabled", DEFAULT_ENABLED))
if (!this._getPref("sessionstore.enabled", DEFAULT_ENABLED))
return;
var observerService =
Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
var observerService = Cc["@mozilla.org/observer-service;1"].
getService(Ci.nsIObserverService);
OBSERVING.forEach(function(aTopic) {
observerService.addObserver(this, aTopic, true);
@ -218,16 +206,23 @@ SessionStoreService.prototype = {
this._prefBranch.addObserver("sessionstore.max_tabs_undo", this, true);
// get file references
this._sessionFile =
Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties)
.get("ProfD", Ci.nsILocalFile);
var dirService = Cc["@mozilla.org/file/directory_service;1"].
getService(Ci.nsIProperties);
this._sessionFile = dirService.get("ProfD", Ci.nsILocalFile);
this._sessionFileBackup = this._sessionFile.clone();
this._sessionFile.append("sessionstore.ini");
this._sessionFileBackup.append("sessionstore.bak");
// for the recover prompt:
// get string containing session state
var iniString = this._readFile(this._getSessionFile());
var iniString;
try {
var ss = Cc["@mozilla.org/browser/sessionstartup;1"].
getService(Ci.nsISessionStartup);
if (ss.doRestore())
iniString = ss.state;
}
catch(ex) { dump(ex + "\n"); } // no state to restore, which is ok
if (iniString) {
try {
// parse the session state into JS objects
@ -247,13 +242,18 @@ SessionStoreService.prototype = {
}
// if last session crashed, backup the session
// and try to restore the disk cache
if (this._lastSessionCrashed) {
try {
this._writeFile(this._getSessionFile(true), iniString);
}
catch (ex) { } // nothing else we can do here
}
// As this is called at delayedStartup, restoration must be initiated here
var windowLoad = function(self) {
self.onLoad(this);
}
aWindow.setTimeout(windowLoad, 0, this);
},
/**
@ -278,20 +278,13 @@ SessionStoreService.prototype = {
* Handle notifications
*/
observe: function sss_observe(aSubject, aTopic, aData) {
var observerService =
Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
var observerService = Cc["@mozilla.org/observer-service;1"].
getService(Ci.nsIObserverService);
// for event listeners
var _this = this;
switch (aTopic) {
case "app-startup":
observerService.addObserver(this, "final-ui-startup", true);
break;
case "final-ui-startup":
observerService.removeObserver(this, "final-ui-startup");
this.init();
break;
case "domwindowopened": // catch new windows
aSubject.addEventListener("load", function(aEvent) {
aEvent.currentTarget.removeEventListener("load", arguments.callee, false);
@ -392,23 +385,13 @@ SessionStoreService.prototype = {
// perform additional initialization when the first window is loading
if (this._loadState == STATE_STOPPED) {
// delete the initial state if the user doesn't want to restore it
// (since this might delay startup notably, don't set _loadState before
// this or we might get a corrupted session if Firefox crashes on
// startup)
if (this._initialState && !(this._lastSessionCrashed ? this._doRecoverSession() : this._doResumeSession())) {
delete this._initialState;
}
if (this._getPref("sessionstore.resume_session_once", DEFAULT_RESUME_SESSION_ONCE)) {
this._prefBranch.setBoolPref("sessionstore.resume_session_once", false);
}
this._loadState = STATE_RUNNING;
this._lastSaveTime = Date.now();
// don't save during the first five seconds
// (until most of the pages have been restored)
this.saveStateDelayed(aWindow, 10000);
// restore a crashed session resp. resume the last session if requested
if (this._initialState) {
// make sure that the restored tabs are first in the window
@ -871,7 +854,7 @@ SessionStoreService.prototype = {
aEntry.postData.QueryInterface(Ci.nsISeekableStream).
seek(Ci.nsISeekableStream.NS_SEEK_SET, 0);
var stream = Cc["@mozilla.org/scriptableinputstream;1"].
createInstance(Ci.nsIScriptableInputStream);
createInstance(Ci.nsIScriptableInputStream);
stream.init(aEntry.postData);
// Warning: LiveHTTPHeaders crashes around here!
var postdata = stream.read(stream.available());
@ -1015,7 +998,7 @@ SessionStoreService.prototype = {
*/
_updateCookies: function sss_updateCookies(aWindows) {
var cookiesEnum = Cc["@mozilla.org/cookiemanager;1"].
getService(Ci.nsICookieManager).enumerator;
getService(Ci.nsICookieManager).enumerator;
// collect the cookies per window
for (var i = 0; i < aWindows.length; i++) {
aWindows[i].Cookies = { count: 0 };
@ -1367,10 +1350,10 @@ SessionStoreService.prototype = {
*/
_deserializeHistoryEntry: function sss_deserializeHistoryEntry(aEntry, aIdMap) {
var shEntry = Cc["@mozilla.org/browser/session-history-entry;1"].
createInstance(Ci.nsISHEntry);
createInstance(Ci.nsISHEntry);
var ioService =
Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
var ioService = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
shEntry.setURI(ioService.newURI(aEntry.url, null, null));
shEntry.setTitle(aEntry.title || aEntry.url);
shEntry.setIsSubFrame(aEntry.subframe || false);
@ -1378,7 +1361,7 @@ SessionStoreService.prototype = {
if (aEntry.cacheKey) {
var cacheKey = Cc["@mozilla.org/supports-PRUint32;1"].
createInstance(Ci.nsISupportsPRUint32);
createInstance(Ci.nsISupportsPRUint32);
cacheKey.data = aEntry.cacheKey;
shEntry.cacheKey = cacheKey;
}
@ -1400,7 +1383,7 @@ SessionStoreService.prototype = {
if (aEntry.postdata) {
var stream = Cc["@mozilla.org/io/string-input-stream;1"].
createInstance(Ci.nsIStringInputStream);
createInstance(Ci.nsIStringInputStream);
stream.setData(aEntry.postdata, -1);
shEntry.postData = stream;
}
@ -1549,10 +1532,10 @@ SessionStoreService.prototype = {
* Array of cookie data
*/
restoreCookies: function sss_restoreCookies(aCookies) {
var cookieService =
Cc["@mozilla.org/cookieService;1"].getService(Ci.nsICookieService);
var ioService =
Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
var cookieService = Cc["@mozilla.org/cookieService;1"].
getService(Ci.nsICookieService);
var ioService = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
for (var i = 1; i <= aCookies.count; i++) {
try {
@ -1568,15 +1551,15 @@ SessionStoreService.prototype = {
* Window reference
*/
retryDownloads: function sss_retryDownloads(aWindow) {
var downloadManager =
Cc["@mozilla.org/download-manager;1"].getService(Ci.nsIDownloadManager);
var rdfService =
Cc["@mozilla.org/rdf/rdf-service;1"].getService(Ci.nsIRDFService);
var ioService =
Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
var downloadManager = Cc["@mozilla.org/download-manager;1"].
getService(Ci.nsIDownloadManager);
var rdfService = Cc["@mozilla.org/rdf/rdf-service;1"].
getService(Ci.nsIRDFService);
var ioService = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
var rdfContainer =
Cc["@mozilla.org/rdf/container;1"].createInstance(Ci.nsIRDFContainer);
var rdfContainer = Cc["@mozilla.org/rdf/container;1"].
createInstance(Ci.nsIRDFContainer);
var datasource = downloadManager.datasource;
try {
@ -1609,8 +1592,8 @@ SessionStoreService.prototype = {
node = datasource.GetTarget(rdfService.GetResource(download.Value), rdfService.GetResource("http://home.netscape.com/NC-rdf#File"), true);
node.QueryInterface(Ci.nsIRDFResource);
var linkChecker =
Cc["@mozilla.org/network/urichecker;1"].createInstance(Ci.nsIURIChecker);
var linkChecker = Cc["@mozilla.org/network/urichecker;1"].
createInstance(Ci.nsIURIChecker);
linkChecker.init(ioService.newURI(url, null, null));
linkChecker.loadFlags = Ci.nsIRequest.LOAD_BACKGROUND;
@ -1725,7 +1708,7 @@ SessionStoreService.prototype = {
*/
_getMostRecentBrowserWindow: function sss_getMostRecentBrowserWindow() {
var windowMediator = Cc["@mozilla.org/appshell/window-mediator;1"].
getService(Ci.nsIWindowMediator);
getService(Ci.nsIWindowMediator);
return windowMediator.getMostRecentWindow("navigator:browser");
},
@ -1739,8 +1722,8 @@ SessionStoreService.prototype = {
//zeniko: why isn't it possible to set the window's dimensions here (as feature)?
var window = Cc["@mozilla.org/embedcomp/window-watcher;1"].
getService(Ci.nsIWindowWatcher).
openWindow(null, this._getPref("chromeURL", null), "_blank", "chrome,dialog=no,all", null);
getService(Ci.nsIWindowWatcher).
openWindow(null, this._getPref("chromeURL", null), "_blank", "chrome,dialog=no,all", null);
window.__SS_state = aState;
var _this = this;
@ -1765,60 +1748,6 @@ SessionStoreService.prototype = {
|| this._getPref("startup.page", 1) == 2;
},
/**
* prompt user whether or not to restore the previous session,
* if the browser crashed
* @returns bool
*/
_doRecoverSession: function sss_doRecoverSession() {
// do not prompt or resume, post-crash
if (!this._getPref("sessionstore.resume_from_crash", DEFAULT_RESUME_FROM_CRASH))
return false;
// if the prompt fails, recover anyway
var recover = true;
// allow extensions to hook in a more elaborate restore prompt
//zeniko: drop this when we're using our own dialog instead of a standard prompt
var dialogURI = this._getPref("sessionstore.restore_prompt_uri");
try {
if (dialogURI) { // extension provided dialog
var params = Cc["@mozilla.org/embedcomp/dialogparam;1"].
createInstance(Ci.nsIDialogParamBlock);
// default to recovering
params.SetInt(0, 0);
Cc["@mozilla.org/embedcomp/window-watcher;1"].
getService(Ci.nsIWindowWatcher).
openWindow(null, dialogURI, "_blank", "chrome,modal,centerscreen,titlebar", params);
recover = params.GetInt(0) == 0;
}
else { // basic prompt with no options
// get app name from branding properties
var brandStringBundle = this._getStringBundle("chrome://branding/locale/brand.properties");
var brandShortName = brandStringBundle.GetStringFromName("brandShortName");
// create prompt strings
var ssStringBundle = this._getStringBundle("chrome://browser/locale/sessionstore.properties");
var restoreTitle = ssStringBundle.formatStringFromName("restoreTitle", [brandShortName], 1);
var restoreText = ssStringBundle.formatStringFromName("restoreText", [brandShortName], 1);
var buttonTitle = ssStringBundle.GetStringFromName("buttonTitle");
var promptService = Cc["@mozilla.org/embedcomp/prompt-service;1"].getService(Ci.nsIPromptService);
// set the buttons that will appear on the dialog
var flags = promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_1 +
promptService.BUTTON_TITLE_CANCEL * promptService.BUTTON_POS_0;
var buttonChoice = promptService.confirmEx(null, restoreTitle, restoreText,
flags, null, buttonTitle, null,
null, {});
recover = (buttonChoice == 1);
}
}
catch (ex) { dump(ex); } // if the prompt fails for some reason, recover anyway
return recover;
},
/**
* whether the user wants to load any other page at startup
* (except the homepage) - needed for determining whether to overwrite the current tabs
@ -1844,7 +1773,7 @@ SessionStoreService.prototype = {
break;
case 2:
homepage = Cc["@mozilla.org/browser/global-history;2"].
getService(Ci.nsIBrowserHistory).lastPageVisited;
getService(Ci.nsIBrowserHistory).lastPageVisited;
break;
}
@ -1918,9 +1847,9 @@ SessionStoreService.prototype = {
*/
_getStringBundle: function sss_getStringBundle(aURI) {
var bundleService = Cc["@mozilla.org/intl/stringbundle;1"].
getService(Ci.nsIStringBundleService);
getService(Ci.nsIStringBundleService);
var appLocale = Cc["@mozilla.org/intl/nslocaleservice;1"].
getService(Ci.nsILocaleService).getApplicationLocale();
getService(Ci.nsILocaleService).getApplicationLocale();
return bundleService.createBundle(aURI, appLocale);
},
@ -1931,7 +1860,7 @@ SessionStoreService.prototype = {
*/
_getURIFromString: function sss_getURIFromString(aString) {
var ioService = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
getService(Ci.nsIIOService);
return ioService.newURI(aString, null, null);
},
@ -1971,10 +1900,10 @@ SessionStoreService.prototype = {
_readFile: function sss_readFile(aFile) {
try {
var stream = Cc["@mozilla.org/network/file-input-stream;1"].
createInstance(Ci.nsIFileInputStream);
createInstance(Ci.nsIFileInputStream);
stream.init(aFile, 0x01, 0, 0);
var cvstream = Cc["@mozilla.org/intl/converter-input-stream;1"].
createInstance(Ci.nsIConverterInputStream);
createInstance(Ci.nsIConverterInputStream);
cvstream.init(stream, "UTF-8", 1024, Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
var content = "";
@ -2008,9 +1937,9 @@ SessionStoreService.prototype = {
/* ........ QueryInterface .............. */
QueryInterface: function(aIID) {
if (!aIID.equals(Ci.nsISupports) && !aIID.equals(Ci.nsIObserver)
&& !aIID.equals(Ci.nsISupportsWeakReference)
&& !aIID.equals(Ci.nsISessionStore)) {
if (!aIID.equals(Ci.nsISupports) && !aIID.equals(Ci.nsIObserver) &&
!aIID.equals(Ci.nsISupportsWeakReference) &&
!aIID.equals(Ci.nsISessionStore)) {
Components.returnCode = Cr.NS_ERROR_NO_INTERFACE;
return null;
}
@ -2031,12 +1960,12 @@ FileWriter.prototype = {
run: function FileWriter_run() {
// init stream
var stream = Cc["@mozilla.org/network/safe-file-output-stream;1"].
createInstance(Ci.nsIFileOutputStream);
createInstance(Ci.nsIFileOutputStream);
stream.init(this._file, 0x02 | 0x08 | 0x20, 0600, 0);
// convert to UTF-8
var converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
createInstance(Ci.nsIScriptableUnicodeConverter);
createInstance(Ci.nsIScriptableUnicodeConverter);
converter.charset = "UTF-8";
var convertedData = converter.ConvertFromUnicode(this._data);
convertedData += converter.Finish();
@ -2239,7 +2168,8 @@ const SessionStoreModule = {
aCompMgr.QueryInterface(Ci.nsIComponentRegistrar);
aCompMgr.registerFactoryLocation(CID, CLASS_NAME, CONTRACT_ID, aFileSpec, aLocation, aType);
var catMan = Cc["@mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager);
var catMan = Cc["@mozilla.org/categorymanager;1"].
getService(Ci.nsICategoryManager);
catMan.addCategoryEntry("app-startup", CLASS_NAME, "service," + CONTRACT_ID, true, true);
},
@ -2247,7 +2177,8 @@ const SessionStoreModule = {
aCompMgr.QueryInterface(Ci.nsIComponentRegistrar);
aCompMgr.unregisterFactoryLocation(CID, aLocation);
var catMan = Cc["@mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager);
var catMan = Cc["@mozilla.org/categorymanager;1"].
getService(Ci.nsICategoryManager);
catMan.deleteCategoryEntry( "app-startup", "service," + CONTRACT_ID, true);
},