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.

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

mercurial