2 # ***** BEGIN LICENSE BLOCK *****
3 # * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 # * The contents of this file are subject to the Mozilla Public License Version
6 # * 1.1 (the "License"); you may not use this file except in compliance with
7 # * the License. You may obtain a copy of the License at
8 # * http://www.mozilla.org/MPL/
10 # * Software distributed under the License is distributed on an "AS IS" basis,
11 # * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 # * for the specific language governing rights and limitations under the
15 # * The Original Code is the nsSessionStore component.
17 # * The Initial Developer of the Original Code is
18 # * Simon Bünzli <zeniko@gmail.com>
19 # * Portions created by the Initial Developer are Copyright (C) 2006
20 # * the Initial Developer. All Rights Reserved.
23 # * Dietrich Ayala <autonome@gmail.com>
25 # * Alternatively, the contents of this file may be used under the terms of
26 # * either the GNU General Public License Version 2 or later (the "GPL"), or
27 # * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 # * in which case the provisions of the GPL or the LGPL are applicable instead
29 # * of those above. If you wish to allow use of your version of this file only
30 # * under the terms of either the GPL or the LGPL, and not to allow others to
31 # * use your version of this file under the terms of the MPL, indicate your
32 # * decision by deleting the provisions above and replace them with the notice
33 # * and other provisions required by the GPL or the LGPL. If you do not delete
34 # * the provisions above, a recipient may use your version of this file under
35 # * the terms of any one of the MPL, the GPL or the LGPL.
37 # * ***** END LICENSE BLOCK *****
41 # * Session Storage and Restoration
44 # * This service reads user's session file at startup, and makes a determination
45 # * as to whether the session should be restored. It will restore the session
46 # * under the circumstances described below.
49 # * The session file stores a session.state property, that
50 # * indicates whether the browser is currently running. When the browser shuts
51 # * down, the field is changed to "stopped". At startup, this field is read, and
52 # * if its value is "running", then it's assumed that the browser had previously
53 # * crashed, or at the very least that something bad happened, and that we should
54 # * restore the session.
57 # * In the event that a restart is required due to application update or extension
58 # * installation, set the browser.sessionstore.resume_session_once pref to true,
59 # * and the session will be restored the next time the browser starts.
62 # * This service will always resume the session if the integer pref
63 # * browser.startup.page is set to 3.
66 /* :::::::: Constants and Helpers ::::::::::::::: */
68 const Cc
= Components
.classes
;
69 const Ci
= Components
.interfaces
;
70 const Cr
= Components
.results
;
71 Components
.utils
.import("resource://gre/modules/XPCOMUtils.jsm");
73 const STATE_RUNNING_STR
= "running";
75 function debug(aMsg
) {
76 aMsg
= ("SessionStartup: " + aMsg
).replace(/\S{80}/g, "$&\n");
77 Cc
["@mozilla.org/consoleservice;1"].getService(Ci
.nsIConsoleService
)
78 .logStringMessage(aMsg
);
81 /* :::::::: The Service ::::::::::::::: */
83 function SessionStartup() {
86 SessionStartup
.prototype = {
88 // the state to restore at startup
90 _sessionType
: Ci
.nsISessionStartup
.NO_SESSION
,
92 /* ........ Global Event Handlers .............. */
95 * Initialize the component
97 init
: function sss_init() {
98 let prefBranch
= Cc
["@mozilla.org/preferences-service;1"].
99 getService(Ci
.nsIPrefService
).getBranch("browser.");
101 // get file references
102 var dirService
= Cc
["@mozilla.org/file/directory_service;1"].
103 getService(Ci
.nsIProperties
);
104 let sessionFile
= dirService
.get("ProfD", Ci
.nsILocalFile
);
105 sessionFile
.append("sessionstore.js");
107 let doResumeSession
= prefBranch
.getBoolPref("sessionstore.resume_session_once") ||
108 prefBranch
.getIntPref("startup.page") == 3;
110 // only read the session file if config allows possibility of restoring
111 var resumeFromCrash
= prefBranch
.getBoolPref("sessionstore.resume_from_crash");
112 if (!resumeFromCrash
&& !doResumeSession
|| !sessionFile
.exists())
115 // get string containing session state
116 this._iniString
= this._readStateFile(sessionFile
);
117 if (!this._iniString
)
121 // parse the session state into JS objects
122 var s
= new Components
.utils
.Sandbox("about:blank");
123 var initialState
= Components
.utils
.evalInSandbox(this._iniString
, s
);
125 catch (ex
) { debug("The session file is invalid: " + ex
); }
127 let lastSessionCrashed
=
128 initialState
&& initialState
.session
&& initialState
.session
.state
&&
129 initialState
.session
.state
== STATE_RUNNING_STR
;
131 // set the startup type
132 if (lastSessionCrashed
&& resumeFromCrash
)
133 this._sessionType
= Ci
.nsISessionStartup
.RECOVER_SESSION
;
134 else if (!lastSessionCrashed
&& doResumeSession
)
135 this._sessionType
= Ci
.nsISessionStartup
.RESUME_SESSION
;
137 this._iniString
= null; // reset the state string
139 if (this._sessionType
!= Ci
.nsISessionStartup
.NO_SESSION
) {
140 // wait for the first browser window to open
141 var observerService
= Cc
["@mozilla.org/observer-service;1"].
142 getService(Ci
.nsIObserverService
);
143 observerService
.addObserver(this, "domwindowopened", true);
144 observerService
.addObserver(this, "browser:purge-session-history", true);
149 * Handle notifications
151 observe
: function sss_observe(aSubject
, aTopic
, aData
) {
152 var observerService
= Cc
["@mozilla.org/observer-service;1"].
153 getService(Ci
.nsIObserverService
);
157 observerService
.addObserver(this, "final-ui-startup", true);
158 observerService
.addObserver(this, "quit-application", true);
160 case "final-ui-startup":
161 observerService
.removeObserver(this, "final-ui-startup");
162 observerService
.removeObserver(this, "quit-application");
165 case "quit-application":
166 // no reason for initializing at this point (cf. bug 409115)
167 observerService
.removeObserver(this, "final-ui-startup");
168 observerService
.removeObserver(this, "quit-application");
170 case "domwindowopened":
171 var window
= aSubject
;
173 window
.addEventListener("load", function() {
174 self
._onWindowOpened(window
);
175 window
.removeEventListener("load", arguments
.callee
, false);
178 case "browser:purge-session-history":
179 // reset all state on sanitization
180 this._iniString
= null;
181 this._sessionType
= Ci
.nsISessionStartup
.NO_SESSION
;
182 // no need in repeating this, since startup state won't change
183 observerService
.removeObserver(this, "browser:purge-session-history");
189 * Removes the default arguments from the first browser window
190 * (and removes the "domwindowopened" observer afterwards).
192 _onWindowOpened
: function sss_onWindowOpened(aWindow
) {
193 var wType
= aWindow
.document
.documentElement
.getAttribute("windowtype");
194 if (wType
!= "navigator:browser")
198 * Note: this relies on the fact that nsBrowserContentHandler will return
199 * a different value the first time its getter is called after an update,
200 * due to its needHomePageOverride() logic. We don't want to remove the
201 * default arguments in the update case, since they include the "What's
204 * Since we're garanteed to be at least the second caller of defaultArgs
205 * (nsBrowserContentHandler calls it to determine which arguments to pass
206 * at startup), we know that if the window's arguments don't match the
207 * current defaultArguments, we're either in the update case, or we're
208 * launching a non-default browser window, so we shouldn't remove the
209 * window's arguments.
211 var defaultArgs
= Cc
["@mozilla.org/browser/clh;1"].
212 getService(Ci
.nsIBrowserHandler
).defaultArgs
;
213 if (aWindow
.arguments
&& aWindow
.arguments
[0] &&
214 aWindow
.arguments
[0] == defaultArgs
)
215 aWindow
.arguments
[0] = null;
217 var observerService
= Cc
["@mozilla.org/observer-service;1"].
218 getService(Ci
.nsIObserverService
);
219 observerService
.removeObserver(this, "domwindowopened");
222 /* ........ Public API ................*/
225 * Get the session state as a string
228 return this._iniString
;
232 * Determine whether there is a pending session restore.
235 doRestore
: function sss_doRestore() {
236 return this._sessionType
!= Ci
.nsISessionStartup
.NO_SESSION
;
240 * Get the type of pending session store, if any.
243 return this._sessionType
;
246 /* ........ Storage API .............. */
249 * Reads a session state file into a string and lets
250 * observers modify the state before it's being used
252 * @param aFile is any nsIFile
253 * @returns a session state string
255 _readStateFile
: function sss_readStateFile(aFile
) {
256 var stateString
= Cc
["@mozilla.org/supports-string;1"].
257 createInstance(Ci
.nsISupportsString
);
258 stateString
.data
= this._readFile(aFile
) || "";
260 var observerService
= Cc
["@mozilla.org/observer-service;1"].
261 getService(Ci
.nsIObserverService
);
262 observerService
.notifyObservers(stateString
, "sessionstore-state-read", "");
264 return stateString
.data
;
268 * reads a file into a string
273 _readFile
: function sss_readFile(aFile
) {
275 var stream
= Cc
["@mozilla.org/network/file-input-stream;1"].
276 createInstance(Ci
.nsIFileInputStream
);
277 stream
.init(aFile
, 0x01, 0, 0);
278 var cvstream
= Cc
["@mozilla.org/intl/converter-input-stream;1"].
279 createInstance(Ci
.nsIConverterInputStream
);
280 cvstream
.init(stream
, "UTF-8", 1024, Ci
.nsIConverterInputStream
.DEFAULT_REPLACEMENT_CHARACTER
);
284 while (cvstream
.readString(4096, data
)) {
285 content
+= data
.value
;
289 return content
.replace(/\r\n?/g, "\n");
291 catch (ex
) { Components
.utils
.reportError(ex
); }
296 /* ........ QueryInterface .............. */
297 QueryInterface
: XPCOMUtils
.generateQI([Ci
.nsIObserver
,
298 Ci
.nsISupportsWeakReference
,
299 Ci
.nsISessionStartup
]),
300 classDescription
: "Browser Session Startup Service",
301 classID
: Components
.ID("{ec7a6c20-e081-11da-8ad9-0800200c9a66}"),
302 contractID
: "@mozilla.org/browser/sessionstartup;1",
304 // get this contractID registered for certain categories via XPCOMUtils
306 // make ourselves a startup observer
307 { category
: "app-startup", service
: true }
312 function NSGetModule(aCompMgr
, aFileSpec
)
313 XPCOMUtils
.generateModule([SessionStartup
]);