Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / browser / components / sessionstore / src / nsSessionStartup.js
blob9507e234a2f38184ef082d60a8fb49bff24d4872
1 /*
2 # ***** BEGIN LICENSE BLOCK *****
3 # * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 # *
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/
9 # *
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
13 # * License.
14 # *
15 # * The Original Code is the nsSessionStore component.
16 # *
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.
21 # *
22 # * Contributor(s):
23 # * Dietrich Ayala <autonome@gmail.com>
24 # *
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.
36 # *
37 # * ***** END LICENSE BLOCK *****
40 /**
41 # * Session Storage and Restoration
42 # *
43 # * Overview
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.
47 # *
48 # * Crash Detection
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.
55 # *
56 # * Forced Restarts
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.
60 # *
61 # * Always Resume
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
89 _iniString: null,
90 _sessionType: Ci.nsISessionStartup.NO_SESSION,
92 /* ........ Global Event Handlers .............. */
94 /**
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())
113 return;
115 // get string containing session state
116 this._iniString = this._readStateFile(sessionFile);
117 if (!this._iniString)
118 return;
120 try {
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;
136 else
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);
155 switch (aTopic) {
156 case "app-startup":
157 observerService.addObserver(this, "final-ui-startup", true);
158 observerService.addObserver(this, "quit-application", true);
159 break;
160 case "final-ui-startup":
161 observerService.removeObserver(this, "final-ui-startup");
162 observerService.removeObserver(this, "quit-application");
163 this.init();
164 break;
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");
169 break;
170 case "domwindowopened":
171 var window = aSubject;
172 var self = this;
173 window.addEventListener("load", function() {
174 self._onWindowOpened(window);
175 window.removeEventListener("load", arguments.callee, false);
176 }, false);
177 break;
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");
184 break;
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")
195 return;
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
202 * New" page.
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
227 get state() {
228 return this._iniString;
232 * Determine whether there is a pending session restore.
233 * @returns bool
235 doRestore: function sss_doRestore() {
236 return this._sessionType != Ci.nsISessionStartup.NO_SESSION;
240 * Get the type of pending session store, if any.
242 get sessionType() {
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
269 * @param aFile
270 * nsIFile
271 * @returns string
273 _readFile: function sss_readFile(aFile) {
274 try {
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);
282 var content = "";
283 var data = {};
284 while (cvstream.readString(4096, data)) {
285 content += data.value;
287 cvstream.close();
289 return content.replace(/\r\n?/g, "\n");
291 catch (ex) { Components.utils.reportError(ex); }
293 return null;
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
305 _xpcom_categories: [
306 // make ourselves a startup observer
307 { category: "app-startup", service: true }
312 function NSGetModule(aCompMgr, aFileSpec)
313 XPCOMUtils.generateModule([SessionStartup]);