mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
3ec162d8ae
The data reporting notification was over-complicated. It wasn't displayed for +24hr after first run and it had a weird, non-required policy around what constituted acceptance of the policy. The notification is now shown shortly after first startup. The logic around "notification accepted" has been greatly simplified by rolling it into "notification shown." Where we once were checking whether the notification has been "accepted," we now check whether it has been displayed. The overly complicated logic around the implicit acceptance of the policy has also been removed. The end result is the code for managing the state of the notification is greatly simplified.
299 lines
9.6 KiB
JavaScript
299 lines
9.6 KiB
JavaScript
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
"use strict";
|
|
|
|
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
|
|
|
Cu.import("resource://gre/modules/Preferences.jsm");
|
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
Cu.import("resource://services-common/utils.js");
|
|
|
|
|
|
const ROOT_BRANCH = "datareporting.";
|
|
const POLICY_BRANCH = ROOT_BRANCH + "policy.";
|
|
const SESSIONS_BRANCH = ROOT_BRANCH + "sessions.";
|
|
const HEALTHREPORT_BRANCH = ROOT_BRANCH + "healthreport.";
|
|
const HEALTHREPORT_LOGGING_BRANCH = HEALTHREPORT_BRANCH + "logging.";
|
|
const DEFAULT_LOAD_DELAY_MSEC = 10 * 1000;
|
|
const DEFAULT_LOAD_DELAY_FIRST_RUN_MSEC = 60 * 1000;
|
|
|
|
/**
|
|
* The Firefox Health Report XPCOM service.
|
|
*
|
|
* External consumers will be interested in the "reporter" property of this
|
|
* service. This property is a `HealthReporter` instance that powers the
|
|
* service. The property may be null if the Health Report service is not
|
|
* enabled.
|
|
*
|
|
* EXAMPLE USAGE
|
|
* =============
|
|
*
|
|
* let reporter = Cc["@mozilla.org/datareporting/service;1"]
|
|
* .getService(Ci.nsISupports)
|
|
* .wrappedJSObject
|
|
* .healthReporter;
|
|
*
|
|
* if (reporter.haveRemoteData) {
|
|
* // ...
|
|
* }
|
|
*
|
|
* IMPLEMENTATION NOTES
|
|
* ====================
|
|
*
|
|
* In order to not adversely impact application start time, the `HealthReporter`
|
|
* instance is not initialized until a few seconds after "final-ui-startup."
|
|
* The exact delay is configurable via preferences so it can be adjusted with
|
|
* a hotfix extension if the default value is ever problematic. Because of the
|
|
* overhead with the initial creation of the database, the first run is delayed
|
|
* even more than subsequent runs. This does mean that the first moments of
|
|
* browser activity may be lost by FHR.
|
|
*
|
|
* Shutdown of the `HealthReporter` instance is handled completely within the
|
|
* instance (it registers observers on initialization). See the notes on that
|
|
* type for more.
|
|
*/
|
|
this.DataReportingService = function () {
|
|
this.wrappedJSObject = this;
|
|
|
|
this._quitting = false;
|
|
|
|
this._os = Cc["@mozilla.org/observer-service;1"]
|
|
.getService(Ci.nsIObserverService);
|
|
}
|
|
|
|
DataReportingService.prototype = Object.freeze({
|
|
classID: Components.ID("{41f6ae36-a79f-4613-9ac3-915e70f83789}"),
|
|
|
|
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
|
|
Ci.nsISupportsWeakReference]),
|
|
|
|
//---------------------------------------------
|
|
// Start of policy listeners.
|
|
//---------------------------------------------
|
|
|
|
/**
|
|
* Called when policy requests data upload.
|
|
*/
|
|
onRequestDataUpload: function (request) {
|
|
if (!this.healthReporter) {
|
|
return;
|
|
}
|
|
|
|
this.healthReporter.requestDataUpload(request);
|
|
},
|
|
|
|
onNotifyDataPolicy: function (request) {
|
|
Observers.notify("datareporting:notify-data-policy:request", request);
|
|
},
|
|
|
|
onRequestRemoteDelete: function (request) {
|
|
if (!this.healthReporter) {
|
|
return;
|
|
}
|
|
|
|
this.healthReporter.deleteRemoteData(request);
|
|
},
|
|
|
|
//---------------------------------------------
|
|
// End of policy listeners.
|
|
//---------------------------------------------
|
|
|
|
observe: function observe(subject, topic, data) {
|
|
switch (topic) {
|
|
case "app-startup":
|
|
this._os.addObserver(this, "profile-after-change", true);
|
|
break;
|
|
|
|
case "profile-after-change":
|
|
this._os.removeObserver(this, "profile-after-change");
|
|
|
|
try {
|
|
this._prefs = new Preferences(HEALTHREPORT_BRANCH);
|
|
|
|
// We don't initialize the sessions recorder unless Health Report is
|
|
// around to provide pruning of data.
|
|
//
|
|
// FUTURE consider having the SessionsRecorder always enabled and/or
|
|
// living in its own XPCOM service.
|
|
if (this._prefs.get("service.enabled", true)) {
|
|
this.sessionRecorder = new SessionRecorder(SESSIONS_BRANCH);
|
|
this.sessionRecorder.onStartup();
|
|
}
|
|
|
|
// We can't interact with prefs until after the profile is present.
|
|
let policyPrefs = new Preferences(POLICY_BRANCH);
|
|
this.policy = new DataReportingPolicy(policyPrefs, this._prefs, this);
|
|
|
|
this._os.addObserver(this, "sessionstore-windows-restored", true);
|
|
} catch (ex) {
|
|
Cu.reportError("Exception when initializing data reporting service: " +
|
|
CommonUtils.exceptionStr(ex));
|
|
}
|
|
break;
|
|
|
|
case "sessionstore-windows-restored":
|
|
this._os.removeObserver(this, "sessionstore-windows-restored");
|
|
this._os.addObserver(this, "quit-application", false);
|
|
|
|
this.policy.startPolling();
|
|
|
|
// Don't initialize Firefox Health Reporter collection and submission
|
|
// service unless it is enabled.
|
|
if (!this._prefs.get("service.enabled", true)) {
|
|
return;
|
|
}
|
|
|
|
let haveFirstRun = this._prefs.get("service.firstRun", false);
|
|
let delayInterval;
|
|
|
|
if (haveFirstRun) {
|
|
delayInterval = this._prefs.get("service.loadDelayMsec") ||
|
|
DEFAULT_LOAD_DELAY_MSEC;
|
|
} else {
|
|
delayInterval = this._prefs.get("service.loadDelayFirstRunMsec") ||
|
|
DEFAULT_LOAD_DELAY_FIRST_RUN_MSEC;
|
|
}
|
|
|
|
// Delay service loading a little more so things have an opportunity
|
|
// to cool down first.
|
|
this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
|
this.timer.initWithCallback({
|
|
notify: function notify() {
|
|
delete this.timer;
|
|
|
|
// There could be a race between "quit-application" firing and
|
|
// this callback being invoked. We close that door.
|
|
if (this._quitting) {
|
|
return;
|
|
}
|
|
|
|
// Side effect: instantiates the reporter instance if not already
|
|
// accessed.
|
|
//
|
|
// The instance installs its own shutdown observers. So, we just
|
|
// fire and forget: it will clean itself up.
|
|
let reporter = this.healthReporter;
|
|
this.policy.ensureUserNotified();
|
|
}.bind(this),
|
|
}, delayInterval, this.timer.TYPE_ONE_SHOT);
|
|
|
|
break;
|
|
|
|
case "quit-application":
|
|
this._os.removeObserver(this, "quit-application");
|
|
this._quitting = true;
|
|
|
|
// Shutdown doesn't clear pending timers. So, we need to explicitly
|
|
// cancel our health reporter initialization timer or else it will
|
|
// attempt initialization after shutdown has commenced. This would
|
|
// likely lead to stalls or crashes.
|
|
if (this.timer) {
|
|
this.timer.cancel();
|
|
}
|
|
|
|
if (this.policy) {
|
|
this.policy.stopPolling();
|
|
}
|
|
break;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* The HealthReporter instance associated with this service.
|
|
*
|
|
* If the service is disabled, this will return null.
|
|
*
|
|
* The obtained instance may not be fully initialized.
|
|
*/
|
|
get healthReporter() {
|
|
if (!this._prefs.get("service.enabled", true)) {
|
|
return null;
|
|
}
|
|
|
|
if ("_healthReporter" in this) {
|
|
return this._healthReporter;
|
|
}
|
|
|
|
try {
|
|
this._loadHealthReporter();
|
|
} catch (ex) {
|
|
this._healthReporter = null;
|
|
Cu.reportError("Exception when obtaining health reporter: " +
|
|
CommonUtils.exceptionStr(ex));
|
|
}
|
|
|
|
return this._healthReporter;
|
|
},
|
|
|
|
_loadHealthReporter: function () {
|
|
// This should never happen. It was added to help trace down bug 924307.
|
|
if (!this.policy) {
|
|
throw new Error("this.policy not set.");
|
|
}
|
|
|
|
let ns = {};
|
|
// Lazy import so application startup isn't adversely affected.
|
|
|
|
Cu.import("resource://gre/modules/Task.jsm", ns);
|
|
Cu.import("resource://gre/modules/HealthReport.jsm", ns);
|
|
Cu.import("resource://gre/modules/Log.jsm", ns);
|
|
|
|
// How many times will we rewrite this code before rolling it up into a
|
|
// generic module? See also bug 451283.
|
|
const LOGGERS = [
|
|
"Services.DataReporting",
|
|
"Services.HealthReport",
|
|
"Services.Metrics",
|
|
"Services.BagheeraClient",
|
|
"Sqlite.Connection.healthreport",
|
|
];
|
|
|
|
let loggingPrefs = new Preferences(HEALTHREPORT_LOGGING_BRANCH);
|
|
if (loggingPrefs.get("consoleEnabled", true)) {
|
|
let level = loggingPrefs.get("consoleLevel", "Warn");
|
|
let appender = new ns.Log.ConsoleAppender();
|
|
appender.level = ns.Log.Level[level] || ns.Log.Level.Warn;
|
|
|
|
for (let name of LOGGERS) {
|
|
let logger = ns.Log.repository.getLogger(name);
|
|
logger.addAppender(appender);
|
|
}
|
|
}
|
|
|
|
if (loggingPrefs.get("dumpEnabled", false)) {
|
|
let level = loggingPrefs.get("dumpLevel", "Debug");
|
|
let appender = new ns.Log.DumpAppender();
|
|
appender.level = ns.Log.Level[level] || ns.Log.Level.Debug;
|
|
|
|
for (let name of LOGGERS) {
|
|
let logger = ns.Log.repository.getLogger(name);
|
|
logger.addAppender(appender);
|
|
}
|
|
}
|
|
|
|
this._healthReporter = new ns.HealthReporter(HEALTHREPORT_BRANCH,
|
|
this.policy,
|
|
this.sessionRecorder);
|
|
|
|
// Wait for initialization to finish so if a shutdown occurs before init
|
|
// has finished we don't adversely affect app startup on next run.
|
|
this._healthReporter.init().then(function onInit() {
|
|
this._prefs.set("service.firstRun", true);
|
|
}.bind(this));
|
|
},
|
|
});
|
|
|
|
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([DataReportingService]);
|
|
|
|
#define MERGED_COMPARTMENT
|
|
|
|
#include ../common/observers.js
|
|
;
|
|
#include policy.jsm
|
|
;
|
|
#include sessions.jsm
|
|
;
|
|
|