services/datareporting/DataReportingService.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 "use strict";
michael@0 6
michael@0 7 const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
michael@0 8
michael@0 9 Cu.import("resource://gre/modules/Preferences.jsm");
michael@0 10 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
michael@0 11 Cu.import("resource://services-common/utils.js");
michael@0 12
michael@0 13
michael@0 14 const ROOT_BRANCH = "datareporting.";
michael@0 15 const POLICY_BRANCH = ROOT_BRANCH + "policy.";
michael@0 16 const SESSIONS_BRANCH = ROOT_BRANCH + "sessions.";
michael@0 17 const HEALTHREPORT_BRANCH = ROOT_BRANCH + "healthreport.";
michael@0 18 const HEALTHREPORT_LOGGING_BRANCH = HEALTHREPORT_BRANCH + "logging.";
michael@0 19 const DEFAULT_LOAD_DELAY_MSEC = 10 * 1000;
michael@0 20 const DEFAULT_LOAD_DELAY_FIRST_RUN_MSEC = 60 * 1000;
michael@0 21
michael@0 22 /**
michael@0 23 * The Firefox Health Report XPCOM service.
michael@0 24 *
michael@0 25 * External consumers will be interested in the "reporter" property of this
michael@0 26 * service. This property is a `HealthReporter` instance that powers the
michael@0 27 * service. The property may be null if the Health Report service is not
michael@0 28 * enabled.
michael@0 29 *
michael@0 30 * EXAMPLE USAGE
michael@0 31 * =============
michael@0 32 *
michael@0 33 * let reporter = Cc["@mozilla.org/datareporting/service;1"]
michael@0 34 * .getService(Ci.nsISupports)
michael@0 35 * .wrappedJSObject
michael@0 36 * .healthReporter;
michael@0 37 *
michael@0 38 * if (reporter.haveRemoteData) {
michael@0 39 * // ...
michael@0 40 * }
michael@0 41 *
michael@0 42 * IMPLEMENTATION NOTES
michael@0 43 * ====================
michael@0 44 *
michael@0 45 * In order to not adversely impact application start time, the `HealthReporter`
michael@0 46 * instance is not initialized until a few seconds after "final-ui-startup."
michael@0 47 * The exact delay is configurable via preferences so it can be adjusted with
michael@0 48 * a hotfix extension if the default value is ever problematic. Because of the
michael@0 49 * overhead with the initial creation of the database, the first run is delayed
michael@0 50 * even more than subsequent runs. This does mean that the first moments of
michael@0 51 * browser activity may be lost by FHR.
michael@0 52 *
michael@0 53 * Shutdown of the `HealthReporter` instance is handled completely within the
michael@0 54 * instance (it registers observers on initialization). See the notes on that
michael@0 55 * type for more.
michael@0 56 */
michael@0 57 this.DataReportingService = function () {
michael@0 58 this.wrappedJSObject = this;
michael@0 59
michael@0 60 this._quitting = false;
michael@0 61
michael@0 62 this._os = Cc["@mozilla.org/observer-service;1"]
michael@0 63 .getService(Ci.nsIObserverService);
michael@0 64 }
michael@0 65
michael@0 66 DataReportingService.prototype = Object.freeze({
michael@0 67 classID: Components.ID("{41f6ae36-a79f-4613-9ac3-915e70f83789}"),
michael@0 68
michael@0 69 QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
michael@0 70 Ci.nsISupportsWeakReference]),
michael@0 71
michael@0 72 //---------------------------------------------
michael@0 73 // Start of policy listeners.
michael@0 74 //---------------------------------------------
michael@0 75
michael@0 76 /**
michael@0 77 * Called when policy requests data upload.
michael@0 78 */
michael@0 79 onRequestDataUpload: function (request) {
michael@0 80 if (!this.healthReporter) {
michael@0 81 return;
michael@0 82 }
michael@0 83
michael@0 84 this.healthReporter.requestDataUpload(request);
michael@0 85 },
michael@0 86
michael@0 87 onNotifyDataPolicy: function (request) {
michael@0 88 Observers.notify("datareporting:notify-data-policy:request", request);
michael@0 89 },
michael@0 90
michael@0 91 onRequestRemoteDelete: function (request) {
michael@0 92 if (!this.healthReporter) {
michael@0 93 return;
michael@0 94 }
michael@0 95
michael@0 96 this.healthReporter.deleteRemoteData(request);
michael@0 97 },
michael@0 98
michael@0 99 //---------------------------------------------
michael@0 100 // End of policy listeners.
michael@0 101 //---------------------------------------------
michael@0 102
michael@0 103 observe: function observe(subject, topic, data) {
michael@0 104 switch (topic) {
michael@0 105 case "app-startup":
michael@0 106 this._os.addObserver(this, "profile-after-change", true);
michael@0 107 break;
michael@0 108
michael@0 109 case "profile-after-change":
michael@0 110 this._os.removeObserver(this, "profile-after-change");
michael@0 111
michael@0 112 try {
michael@0 113 this._prefs = new Preferences(HEALTHREPORT_BRANCH);
michael@0 114
michael@0 115 // We don't initialize the sessions recorder unless Health Report is
michael@0 116 // around to provide pruning of data.
michael@0 117 //
michael@0 118 // FUTURE consider having the SessionsRecorder always enabled and/or
michael@0 119 // living in its own XPCOM service.
michael@0 120 if (this._prefs.get("service.enabled", true)) {
michael@0 121 this.sessionRecorder = new SessionRecorder(SESSIONS_BRANCH);
michael@0 122 this.sessionRecorder.onStartup();
michael@0 123 }
michael@0 124
michael@0 125 // We can't interact with prefs until after the profile is present.
michael@0 126 let policyPrefs = new Preferences(POLICY_BRANCH);
michael@0 127 this.policy = new DataReportingPolicy(policyPrefs, this._prefs, this);
michael@0 128
michael@0 129 this._os.addObserver(this, "sessionstore-windows-restored", true);
michael@0 130 } catch (ex) {
michael@0 131 Cu.reportError("Exception when initializing data reporting service: " +
michael@0 132 CommonUtils.exceptionStr(ex));
michael@0 133 }
michael@0 134 break;
michael@0 135
michael@0 136 case "sessionstore-windows-restored":
michael@0 137 this._os.removeObserver(this, "sessionstore-windows-restored");
michael@0 138 this._os.addObserver(this, "quit-application", false);
michael@0 139
michael@0 140 this.policy.startPolling();
michael@0 141
michael@0 142 // Don't initialize Firefox Health Reporter collection and submission
michael@0 143 // service unless it is enabled.
michael@0 144 if (!this._prefs.get("service.enabled", true)) {
michael@0 145 return;
michael@0 146 }
michael@0 147
michael@0 148 let haveFirstRun = this._prefs.get("service.firstRun", false);
michael@0 149 let delayInterval;
michael@0 150
michael@0 151 if (haveFirstRun) {
michael@0 152 delayInterval = this._prefs.get("service.loadDelayMsec") ||
michael@0 153 DEFAULT_LOAD_DELAY_MSEC;
michael@0 154 } else {
michael@0 155 delayInterval = this._prefs.get("service.loadDelayFirstRunMsec") ||
michael@0 156 DEFAULT_LOAD_DELAY_FIRST_RUN_MSEC;
michael@0 157 }
michael@0 158
michael@0 159 // Delay service loading a little more so things have an opportunity
michael@0 160 // to cool down first.
michael@0 161 this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
michael@0 162 this.timer.initWithCallback({
michael@0 163 notify: function notify() {
michael@0 164 delete this.timer;
michael@0 165
michael@0 166 // There could be a race between "quit-application" firing and
michael@0 167 // this callback being invoked. We close that door.
michael@0 168 if (this._quitting) {
michael@0 169 return;
michael@0 170 }
michael@0 171
michael@0 172 // Side effect: instantiates the reporter instance if not already
michael@0 173 // accessed.
michael@0 174 //
michael@0 175 // The instance installs its own shutdown observers. So, we just
michael@0 176 // fire and forget: it will clean itself up.
michael@0 177 let reporter = this.healthReporter;
michael@0 178 }.bind(this),
michael@0 179 }, delayInterval, this.timer.TYPE_ONE_SHOT);
michael@0 180
michael@0 181 break;
michael@0 182
michael@0 183 case "quit-application":
michael@0 184 this._os.removeObserver(this, "quit-application");
michael@0 185 this._quitting = true;
michael@0 186
michael@0 187 // Shutdown doesn't clear pending timers. So, we need to explicitly
michael@0 188 // cancel our health reporter initialization timer or else it will
michael@0 189 // attempt initialization after shutdown has commenced. This would
michael@0 190 // likely lead to stalls or crashes.
michael@0 191 if (this.timer) {
michael@0 192 this.timer.cancel();
michael@0 193 }
michael@0 194
michael@0 195 if (this.policy) {
michael@0 196 this.policy.stopPolling();
michael@0 197 }
michael@0 198 break;
michael@0 199 }
michael@0 200 },
michael@0 201
michael@0 202 /**
michael@0 203 * The HealthReporter instance associated with this service.
michael@0 204 *
michael@0 205 * If the service is disabled, this will return null.
michael@0 206 *
michael@0 207 * The obtained instance may not be fully initialized.
michael@0 208 */
michael@0 209 get healthReporter() {
michael@0 210 if (!this._prefs.get("service.enabled", true)) {
michael@0 211 return null;
michael@0 212 }
michael@0 213
michael@0 214 if ("_healthReporter" in this) {
michael@0 215 return this._healthReporter;
michael@0 216 }
michael@0 217
michael@0 218 try {
michael@0 219 this._loadHealthReporter();
michael@0 220 } catch (ex) {
michael@0 221 this._healthReporter = null;
michael@0 222 Cu.reportError("Exception when obtaining health reporter: " +
michael@0 223 CommonUtils.exceptionStr(ex));
michael@0 224 }
michael@0 225
michael@0 226 return this._healthReporter;
michael@0 227 },
michael@0 228
michael@0 229 _loadHealthReporter: function () {
michael@0 230 // This should never happen. It was added to help trace down bug 924307.
michael@0 231 if (!this.policy) {
michael@0 232 throw new Error("this.policy not set.");
michael@0 233 }
michael@0 234
michael@0 235 let ns = {};
michael@0 236 // Lazy import so application startup isn't adversely affected.
michael@0 237
michael@0 238 Cu.import("resource://gre/modules/Task.jsm", ns);
michael@0 239 Cu.import("resource://gre/modules/HealthReport.jsm", ns);
michael@0 240 Cu.import("resource://gre/modules/Log.jsm", ns);
michael@0 241
michael@0 242 // How many times will we rewrite this code before rolling it up into a
michael@0 243 // generic module? See also bug 451283.
michael@0 244 const LOGGERS = [
michael@0 245 "Services.DataReporting",
michael@0 246 "Services.HealthReport",
michael@0 247 "Services.Metrics",
michael@0 248 "Services.BagheeraClient",
michael@0 249 "Sqlite.Connection.healthreport",
michael@0 250 ];
michael@0 251
michael@0 252 let loggingPrefs = new Preferences(HEALTHREPORT_LOGGING_BRANCH);
michael@0 253 if (loggingPrefs.get("consoleEnabled", true)) {
michael@0 254 let level = loggingPrefs.get("consoleLevel", "Warn");
michael@0 255 let appender = new ns.Log.ConsoleAppender();
michael@0 256 appender.level = ns.Log.Level[level] || ns.Log.Level.Warn;
michael@0 257
michael@0 258 for (let name of LOGGERS) {
michael@0 259 let logger = ns.Log.repository.getLogger(name);
michael@0 260 logger.addAppender(appender);
michael@0 261 }
michael@0 262 }
michael@0 263
michael@0 264 if (loggingPrefs.get("dumpEnabled", false)) {
michael@0 265 let level = loggingPrefs.get("dumpLevel", "Debug");
michael@0 266 let appender = new ns.Log.DumpAppender();
michael@0 267 appender.level = ns.Log.Level[level] || ns.Log.Level.Debug;
michael@0 268
michael@0 269 for (let name of LOGGERS) {
michael@0 270 let logger = ns.Log.repository.getLogger(name);
michael@0 271 logger.addAppender(appender);
michael@0 272 }
michael@0 273 }
michael@0 274
michael@0 275 this._healthReporter = new ns.HealthReporter(HEALTHREPORT_BRANCH,
michael@0 276 this.policy,
michael@0 277 this.sessionRecorder);
michael@0 278
michael@0 279 // Wait for initialization to finish so if a shutdown occurs before init
michael@0 280 // has finished we don't adversely affect app startup on next run.
michael@0 281 this._healthReporter.init().then(function onInit() {
michael@0 282 this._prefs.set("service.firstRun", true);
michael@0 283 }.bind(this));
michael@0 284 },
michael@0 285 });
michael@0 286
michael@0 287 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([DataReportingService]);
michael@0 288
michael@0 289 #define MERGED_COMPARTMENT
michael@0 290
michael@0 291 #include ../common/observers.js
michael@0 292 ;
michael@0 293 #include policy.jsm
michael@0 294 ;
michael@0 295 #include sessions.jsm
michael@0 296 ;
michael@0 297

mercurial