toolkit/components/crashmonitor/CrashMonitor.jsm

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

michael@0 1 /* -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 /**
michael@0 7 * Crash Monitor
michael@0 8 *
michael@0 9 * Monitors execution of a program to detect possible crashes. After
michael@0 10 * program termination, the monitor can be queried during the next run
michael@0 11 * to determine whether the last run exited cleanly or not.
michael@0 12 *
michael@0 13 * The monitoring is done by registering and listening for special
michael@0 14 * notifications, or checkpoints, known to be sent by the monitored
michael@0 15 * program as different stages in the execution are reached. As they
michael@0 16 * are observed, these notifications are written asynchronously to a
michael@0 17 * checkpoint file.
michael@0 18 *
michael@0 19 * During next program startup the crash monitor reads the checkpoint
michael@0 20 * file from the last session. If notifications are missing, a crash
michael@0 21 * has likely happened. By inspecting the notifications present, it is
michael@0 22 * possible to determine what stages were reached in the program
michael@0 23 * before the crash.
michael@0 24 *
michael@0 25 * Note that since the file is written asynchronously it is possible
michael@0 26 * that a received notification is lost if the program crashes right
michael@0 27 * after a checkpoint, but before crash monitor has been able to write
michael@0 28 * it to disk. Thus, while the presence of a notification in the
michael@0 29 * checkpoint file tells us that the corresponding stage was reached
michael@0 30 * during the last run, the absence of a notification after a crash
michael@0 31 * does not necessarily tell us that the checkpoint wasn't reached.
michael@0 32 */
michael@0 33
michael@0 34 this.EXPORTED_SYMBOLS = [ "CrashMonitor" ];
michael@0 35
michael@0 36 const Cu = Components.utils;
michael@0 37 const Cr = Components.results;
michael@0 38
michael@0 39 Cu.import("resource://gre/modules/Services.jsm");
michael@0 40 Cu.import("resource://gre/modules/osfile.jsm");
michael@0 41 Cu.import("resource://gre/modules/Promise.jsm");
michael@0 42 Cu.import("resource://gre/modules/Task.jsm");
michael@0 43 Cu.import("resource://gre/modules/AsyncShutdown.jsm");
michael@0 44
michael@0 45 const NOTIFICATIONS = [
michael@0 46 "final-ui-startup",
michael@0 47 "sessionstore-windows-restored",
michael@0 48 "quit-application-granted",
michael@0 49 "quit-application",
michael@0 50 "profile-change-net-teardown",
michael@0 51 "profile-change-teardown",
michael@0 52 "profile-before-change",
michael@0 53 "sessionstore-final-state-write-complete"
michael@0 54 ];
michael@0 55
michael@0 56 let CrashMonitorInternal = {
michael@0 57
michael@0 58 /**
michael@0 59 * Notifications received during the current session.
michael@0 60 *
michael@0 61 * Object where a property with a value of |true| means that the
michael@0 62 * notification of the same name has been received at least once by
michael@0 63 * the CrashMonitor during this session. Notifications that have not
michael@0 64 * yet been received are not present as properties. |NOTIFICATIONS|
michael@0 65 * lists the notifications tracked by the CrashMonitor.
michael@0 66 */
michael@0 67 checkpoints: {},
michael@0 68
michael@0 69 /**
michael@0 70 * Notifications received during previous session.
michael@0 71 *
michael@0 72 * Available after |loadPreviousCheckpoints|. Promise which resolves
michael@0 73 * to an object containing a set of properties, where a property
michael@0 74 * with a value of |true| means that the notification with the same
michael@0 75 * name as the property name was received at least once last
michael@0 76 * session.
michael@0 77 */
michael@0 78 previousCheckpoints: null,
michael@0 79
michael@0 80 /* Deferred for AsyncShutdown blocker */
michael@0 81 profileBeforeChangeDeferred: Promise.defer(),
michael@0 82
michael@0 83 /**
michael@0 84 * Path to checkpoint file.
michael@0 85 *
michael@0 86 * Each time a new notification is received, this file is written to
michael@0 87 * disc to reflect the information in |checkpoints|. Although Firefox for
michael@0 88 * Desktop and Metro share the same profile, they need to keep record of
michael@0 89 * crashes separately.
michael@0 90 */
michael@0 91 path: (Services.metro && Services.metro.immersive) ?
michael@0 92 OS.Path.join(OS.Constants.Path.profileDir, "metro", "sessionCheckpoints.json"):
michael@0 93 OS.Path.join(OS.Constants.Path.profileDir, "sessionCheckpoints.json"),
michael@0 94
michael@0 95 /**
michael@0 96 * Load checkpoints from previous session asynchronously.
michael@0 97 *
michael@0 98 * @return {Promise} A promise that resolves/rejects once loading is complete
michael@0 99 */
michael@0 100 loadPreviousCheckpoints: function () {
michael@0 101 this.previousCheckpoints = Task.spawn(function*() {
michael@0 102 let data;
michael@0 103 try {
michael@0 104 data = yield OS.File.read(CrashMonitorInternal.path, { encoding: "utf-8" });
michael@0 105 } catch (ex if ex instanceof OS.File.Error) {
michael@0 106 if (!ex.becauseNoSuchFile) {
michael@0 107 Cu.reportError("Error while loading crash monitor data: " + ex.toString());
michael@0 108 }
michael@0 109
michael@0 110 return null;
michael@0 111 }
michael@0 112
michael@0 113 let notifications;
michael@0 114 try {
michael@0 115 notifications = JSON.parse(data);
michael@0 116 } catch (ex) {
michael@0 117 Cu.reportError("Error while parsing crash monitor data: " + ex);
michael@0 118 return null;
michael@0 119 }
michael@0 120
michael@0 121 return Object.freeze(notifications);
michael@0 122 });
michael@0 123
michael@0 124 return this.previousCheckpoints;
michael@0 125 }
michael@0 126 };
michael@0 127
michael@0 128 this.CrashMonitor = {
michael@0 129
michael@0 130 /**
michael@0 131 * Notifications received during previous session.
michael@0 132 *
michael@0 133 * Return object containing the set of notifications received last
michael@0 134 * session as keys with values set to |true|.
michael@0 135 *
michael@0 136 * @return {Promise} A promise resolving to previous checkpoints
michael@0 137 */
michael@0 138 get previousCheckpoints() {
michael@0 139 if (!CrashMonitorInternal.initialized) {
michael@0 140 throw new Error("CrashMonitor must be initialized before getting previous checkpoints");
michael@0 141 }
michael@0 142
michael@0 143 return CrashMonitorInternal.previousCheckpoints
michael@0 144 },
michael@0 145
michael@0 146 /**
michael@0 147 * Initialize CrashMonitor.
michael@0 148 *
michael@0 149 * Should only be called from the CrashMonitor XPCOM component.
michael@0 150 *
michael@0 151 * @return {Promise}
michael@0 152 */
michael@0 153 init: function () {
michael@0 154 if (CrashMonitorInternal.initialized) {
michael@0 155 throw new Error("CrashMonitor.init() must only be called once!");
michael@0 156 }
michael@0 157
michael@0 158 let promise = CrashMonitorInternal.loadPreviousCheckpoints();
michael@0 159 // Add "profile-after-change" to checkpoint as this method is
michael@0 160 // called after receiving it
michael@0 161 CrashMonitorInternal.checkpoints["profile-after-change"] = true;
michael@0 162
michael@0 163 NOTIFICATIONS.forEach(function (aTopic) {
michael@0 164 Services.obs.addObserver(this, aTopic, false);
michael@0 165 }, this);
michael@0 166
michael@0 167 // Add shutdown blocker for profile-before-change
michael@0 168 AsyncShutdown.profileBeforeChange.addBlocker(
michael@0 169 "CrashMonitor: Writing notifications to file after receiving profile-before-change",
michael@0 170 CrashMonitorInternal.profileBeforeChangeDeferred.promise
michael@0 171 );
michael@0 172
michael@0 173 CrashMonitorInternal.initialized = true;
michael@0 174 if (Services.metro && Services.metro.immersive) {
michael@0 175 OS.File.makeDir(OS.Path.join(OS.Constants.Path.profileDir, "metro"));
michael@0 176 }
michael@0 177 return promise;
michael@0 178 },
michael@0 179
michael@0 180 /**
michael@0 181 * Handle registered notifications.
michael@0 182 *
michael@0 183 * Update checkpoint file for every new notification received.
michael@0 184 */
michael@0 185 observe: function (aSubject, aTopic, aData) {
michael@0 186 if (!(aTopic in CrashMonitorInternal.checkpoints)) {
michael@0 187 // If this is the first time this notification is received,
michael@0 188 // remember it and write it to file
michael@0 189 CrashMonitorInternal.checkpoints[aTopic] = true;
michael@0 190 Task.spawn(function() {
michael@0 191 try {
michael@0 192 let data = JSON.stringify(CrashMonitorInternal.checkpoints);
michael@0 193
michael@0 194 /* Write to the checkpoint file asynchronously, off the main
michael@0 195 * thread, for performance reasons. Note that this means
michael@0 196 * that there's not a 100% guarantee that the file will be
michael@0 197 * written by the time the notification completes. The
michael@0 198 * exception is profile-before-change which has a shutdown
michael@0 199 * blocker. */
michael@0 200 yield OS.File.writeAtomic(
michael@0 201 CrashMonitorInternal.path,
michael@0 202 data, {tmpPath: CrashMonitorInternal.path + ".tmp"});
michael@0 203
michael@0 204 } finally {
michael@0 205 // Resolve promise for blocker
michael@0 206 if (aTopic == "profile-before-change") {
michael@0 207 CrashMonitorInternal.profileBeforeChangeDeferred.resolve();
michael@0 208 }
michael@0 209 }
michael@0 210 });
michael@0 211 }
michael@0 212
michael@0 213 if (NOTIFICATIONS.every(elem => elem in CrashMonitorInternal.checkpoints)) {
michael@0 214 // All notifications received, unregister observers
michael@0 215 NOTIFICATIONS.forEach(function (aTopic) {
michael@0 216 Services.obs.removeObserver(this, aTopic);
michael@0 217 }, this);
michael@0 218 }
michael@0 219 }
michael@0 220 };
michael@0 221 Object.freeze(this.CrashMonitor);

mercurial