From 57f274b2a8601dde5185705cc556f0306f33c3db Mon Sep 17 00:00:00 2001 From: Gregory Szorc Date: Fri, 11 Jan 2013 13:45:22 -0800 Subject: [PATCH] Bug 828829 - Refactor Health Report policy out of services/healthreport; r=rnewman --HG-- rename : services/healthreport/HealthReportService.js => services/datareporting/DataReportingService.js rename : services/healthreport/modules-testing/mocks.jsm => services/datareporting/modules-testing/mocks.jsm rename : services/healthreport/policy.jsm => services/datareporting/policy.jsm rename : services/healthreport/tests/xpcshell/test_policy.js => services/datareporting/tests/xpcshell/test_policy.js extra : rebase_source : cf766bc99ff843f2d31f82b1c4be71313fbc65a8 --- browser/installer/package-manifest.in | 6 +- browser/installer/removed-files.in | 1 + configure.in | 9 ++ modules/libpref/src/Makefile.in | 4 + services/Makefile.in | 11 +- services/datareporting/DataReporting.manifest | 16 +++ .../DataReportingService.js} | 119 +++++++++++++----- services/datareporting/Makefile.in | 26 ++++ services/datareporting/datareporting-prefs.js | 12 ++ .../modules-testing/mocks.jsm | 3 +- .../policy.jsm | 102 ++++++++------- services/datareporting/tests/Makefile.in | 16 +++ .../tests/xpcshell/test_policy.js | 107 +++++++++------- .../datareporting/tests/xpcshell/xpcshell.ini | 5 + .../HealthReportComponents.manifest | 12 +- services/healthreport/Makefile.in | 3 - services/healthreport/healthreport-prefs.js | 39 +++--- services/healthreport/healthreporter.jsm | 96 ++++---------- .../healthreport/modules-testing/utils.jsm | 4 +- .../tests/xpcshell/test_healthreporter.js | 40 ++++-- .../tests/xpcshell/test_load_modules.js | 10 -- .../healthreport/tests/xpcshell/xpcshell.ini | 1 - services/makefiles.sh | 2 + testing/xpcshell/xpcshell.ini | 1 + 24 files changed, 391 insertions(+), 254 deletions(-) create mode 100644 services/datareporting/DataReporting.manifest rename services/{healthreport/HealthReportService.js => datareporting/DataReportingService.js} (54%) create mode 100644 services/datareporting/Makefile.in create mode 100644 services/datareporting/datareporting-prefs.js rename services/{healthreport => datareporting}/modules-testing/mocks.jsm (92%) rename services/{healthreport => datareporting}/policy.jsm (92%) create mode 100644 services/datareporting/tests/Makefile.in rename services/{healthreport => datareporting}/tests/xpcshell/test_policy.js (84%) create mode 100644 services/datareporting/tests/xpcshell/xpcshell.ini diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in index 42b6e390273e2..a863e7a8704c5 100644 --- a/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in @@ -469,9 +469,9 @@ @BINPATH@/components/AitcComponents.manifest @BINPATH@/components/Aitc.js #endif -#ifdef MOZ_SERVICES_HEALTHREPORT -@BINPATH@/components/HealthReportComponents.manifest -@BINPATH@/components/HealthReportService.js +#ifdef MOZ_DATA_REPORTING +@BINPATH@/components/DataReporting.manifest +@BINPATH@/components/DataReportingService.js #endif #ifdef MOZ_SERVICES_SYNC @BINPATH@/components/SyncComponents.manifest diff --git a/browser/installer/removed-files.in b/browser/installer/removed-files.in index 2afd384723f20..02ff38438cc8a 100644 --- a/browser/installer/removed-files.in +++ b/browser/installer/removed-files.in @@ -899,6 +899,7 @@ xpicleanup@BIN_SUFFIX@ components/GPSDGeolocationProvider.js components/interfaces.manifest components/jsconsole-clhandler.js + components/MetricsCollectionService.js components/NetworkGeolocationProvider.js components/NotificationsComponents.manifest components/nsBadCertHandler.js diff --git a/configure.in b/configure.in index 0417559c25efe..07cffe07d15ed 100644 --- a/configure.in +++ b/configure.in @@ -8719,6 +8719,15 @@ if test "$MOZ_TELEMETRY_REPORTING"; then fi fi +dnl If we have any service that uploads data (and requires data submission +dnl policy alert), set MOZ_DATA_REPORTING. +dnl We need SUBST for build system and DEFINE for xul preprocessor. +if test -n "$MOZ_TELEMETRY_REPORTING" || test -n "$MOZ_SERVICES_HEALTHREPORT" || test -n "MOZ_CRASHREPORTER"; then + MOZ_DATA_REPORTING=1 + AC_DEFINE(MOZ_DATA_REPORTING) + AC_SUBST(MOZ_DATA_REPORTING) +fi + dnl win32 options AC_SUBST(MOZ_MAPINFO) AC_SUBST(MOZ_BROWSE_INFO) diff --git a/modules/libpref/src/Makefile.in b/modules/libpref/src/Makefile.in index d9a0d4ad40e37..280deb110923d 100644 --- a/modules/libpref/src/Makefile.in +++ b/modules/libpref/src/Makefile.in @@ -43,6 +43,10 @@ GARBAGE += greprefs.js # TODO bug 813259 external files should be defined near their location in the source tree. grepref_files = $(topsrcdir)/netwerk/base/public/security-prefs.js $(srcdir)/init/all.js +ifdef MOZ_DATA_REPORTING +grepref_files += $(topsrcdir)/services/datareporting/datareporting-prefs.js +endif + ifdef MOZ_SERVICES_HEALTHREPORT grepref_files += $(topsrcdir)/services/healthreport/healthreport-prefs.js endif diff --git a/services/Makefile.in b/services/Makefile.in index 97a82d9072753..4c474ff0550be 100644 --- a/services/Makefile.in +++ b/services/Makefile.in @@ -9,9 +9,10 @@ VPATH = @srcdir@ include $(DEPTH)/config/autoconf.mk -PARALLEL_DIRS += common - -PARALLEL_DIRS += crypto +PARALLEL_DIRS += \ + common \ + crypto \ + $(NULL) ifdef MOZ_SERVICES_AITC PARALLEL_DIRS += aitc @@ -21,6 +22,10 @@ ifdef MOZ_SERVICES_HEALTHREPORT PARALLEL_DIRS += healthreport endif +ifdef MOZ_DATA_REPORTING +PARALLEL_DIRS += datareporting +endif + ifdef MOZ_SERVICES_METRICS PARALLEL_DIRS += metrics endif diff --git a/services/datareporting/DataReporting.manifest b/services/datareporting/DataReporting.manifest new file mode 100644 index 0000000000000..4942c04361397 --- /dev/null +++ b/services/datareporting/DataReporting.manifest @@ -0,0 +1,16 @@ +# b2g: {3c2e2abc-06d4-11e1-ac3b-374f68613e61} +# browser: {ec8030f7-c20a-464f-9b0e-13a3a9e97384} +# mobile/android: {aa3c5121-dab2-40e2-81ca-7ea25febc110} +# mobile/xul: {a23983c0-fd0e-11dc-95ff-0800200c9a66} +# suite (comm): {92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a} +# metro browser: {99bceaaa-e3c6-48c1-b981-ef9b46b67d60} + +# The Data Reporting Service drives collection and submission of metrics +# and other useful data to Mozilla. It drives the display of the data +# submission notification info bar and thus is required by Firefox Health +# Report and Telemetry. + +component {41f6ae36-a79f-4613-9ac3-915e70f83789} DataReportingService.js +contract @mozilla.org/datareporting/service;1 {41f6ae36-a79f-4613-9ac3-915e70f83789} +category app-startup DataReportingService service,@mozilla.org/datareporting/service;1 application={3c2e2abc-06d4-11e1-ac3b-374f68613e61} application={ec8030f7-c20a-464f-9b0e-13a3a9e97384} application={aa3c5121-dab2-40e2-81ca-7ea25febc110} application={a23983c0-fd0e-11dc-95ff-0800200c9a66} + diff --git a/services/healthreport/HealthReportService.js b/services/datareporting/DataReportingService.js similarity index 54% rename from services/healthreport/HealthReportService.js rename to services/datareporting/DataReportingService.js index 8d29c9664f8f7..a3fb72a6465a0 100644 --- a/services/healthreport/HealthReportService.js +++ b/services/datareporting/DataReportingService.js @@ -6,11 +6,16 @@ const {classes: Cc, interfaces: Ci, utils: Cu} = Components; +Cu.import("resource://gre/modules/services/datareporting/policy.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://services-common/observers.js"); Cu.import("resource://services-common/preferences.js"); -const BRANCH = "healthreport."; +const ROOT_BRANCH = "datareporting."; +const POLICY_BRANCH = ROOT_BRANCH + "policy."; +const HEALTHREPORT_BRANCH = ROOT_BRANCH + "healthreport."; +const HEALTHREPORT_LOGGING_BRANCH = HEALTHREPORT_BRANCH + "logging."; const DEFAULT_LOAD_DELAY_MSEC = 10 * 1000; /** @@ -27,7 +32,7 @@ const DEFAULT_LOAD_DELAY_MSEC = 10 * 1000; * let reporter = Cc["@mozilla.org/healthreport/service;1"] * .getService(Ci.nsISupports) * .wrappedJSObject - * .reporter; + * .healthReporter; * * if (reporter.haveRemoteData) { * // ... @@ -45,36 +50,77 @@ const DEFAULT_LOAD_DELAY_MSEC = 10 * 1000; * instance (it registers observers on initialization). See the notes on that * type for more. */ -this.HealthReportService = function HealthReportService() { +this.DataReportingService = function () { this.wrappedJSObject = this; - this._prefs = new Preferences(BRANCH); - - this._reporter = null; + this._os = Cc["@mozilla.org/observer-service;1"] + .getService(Ci.nsIObserverService); } -HealthReportService.prototype = { - classID: Components.ID("{e354c59b-b252-4040-b6dd-b71864e3e35c}"), +DataReportingService.prototype = Object.freeze({ + classID: Components.ID("{41f6ae36-a79f-4613-9ac3-915e70f83789}"), QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsISupportsWeakReference]), - observe: function observe(subject, topic, data) { - // If the background service is disabled, don't do anything. - if (!this._prefs.get("service.enabled", true)) { + //--------------------------------------------- + // Start of policy listeners. + //--------------------------------------------- + + /** + * Called when policy requests data upload. + */ + onRequestDataUpload: function (request) { + if (!this.healthReporter) { return; } - let os = Cc["@mozilla.org/observer-service;1"] - .getService(Ci.nsIObserverService); + 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": - os.addObserver(this, "sessionstore-windows-restored", true); + this._os.addObserver(this, "profile-after-change", true); + break; + + case "profile-after-change": + this._os.removeObserver(this, "profile-after-change"); + this._os.addObserver(this, "sessionstore-windows-restored", true); + + // We can't interact with prefs until after the profile is present. + let policyPrefs = new Preferences(POLICY_BRANCH); + this._prefs = new Preferences(HEALTHREPORT_BRANCH); + this.policy = new DataReportingPolicy(policyPrefs, this._prefs, this); break; case "sessionstore-windows-restored": - os.removeObserver(this, "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 delayInterval = this._prefs.get("service.loadDelayMsec") || DEFAULT_LOAD_DELAY_MSEC; @@ -86,12 +132,17 @@ HealthReportService.prototype = { notify: function notify() { // Side effect: instantiates the reporter instance if not already // accessed. - let reporter = this.reporter; + let reporter = this.healthReporter; delete this.timer; }.bind(this), }, delayInterval, this.timer.TYPE_ONE_SHOT); break; + + case "quit-application": + this._os.removeObserver(this, "quit-application"); + this.policy.stopPolling(); + break; } }, @@ -102,17 +153,29 @@ HealthReportService.prototype = { * * The obtained instance may not be fully initialized. */ - get reporter() { + get healthReporter() { if (!this._prefs.get("service.enabled", true)) { return null; } - if (this._reporter) { - return this._reporter; + if ("_healthReporter" in this) { + return this._healthReporter; + } + + try { + this._loadHealthReporter(); + } catch (ex) { + dump("Error loading health reporter: " + ex); + this._healthReporter = null; } + return this._healthReporter; + }, + + _loadHealthReporter: function () { let ns = {}; // Lazy import so application startup isn't adversely affected. + Cu.import("resource://gre/modules/Task.jsm", ns); Cu.import("resource://gre/modules/services/healthreport/healthreporter.jsm", ns); Cu.import("resource://services-common/log4moz.js", ns); @@ -120,15 +183,16 @@ HealthReportService.prototype = { // 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 prefs = new Preferences(BRANCH + "logging."); - if (prefs.get("consoleEnabled", true)) { - let level = prefs.get("consoleLevel", "Warn"); + let loggingPrefs = new Preferences(HEALTHREPORT_LOGGING_BRANCH); + if (loggingPrefs.get("consoleEnabled", true)) { + let level = loggingPrefs.get("consoleLevel", "Warn"); let appender = new ns.Log4Moz.ConsoleAppender(); appender.level = ns.Log4Moz.Level[level] || ns.Log4Moz.Level.Warn; @@ -139,13 +203,10 @@ HealthReportService.prototype = { } // The reporter initializes in the background. - this._reporter = new ns.HealthReporter(BRANCH); - - return this._reporter; + this._healthReporter = new ns.HealthReporter(HEALTHREPORT_BRANCH, + this.policy); }, -}; - -Object.freeze(HealthReportService.prototype); +}); -this.NSGetFactory = XPCOMUtils.generateNSGetFactory([HealthReportService]); +this.NSGetFactory = XPCOMUtils.generateNSGetFactory([DataReportingService]); diff --git a/services/datareporting/Makefile.in b/services/datareporting/Makefile.in new file mode 100644 index 0000000000000..b5a74c276bf0b --- /dev/null +++ b/services/datareporting/Makefile.in @@ -0,0 +1,26 @@ +# 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/. + +DEPTH = @DEPTH@ +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +TEST_DIRS += tests + +MODULES_FILES := policy.jsm +MODULES_DEST = $(FINAL_TARGET)/modules/services/datareporting +INSTALL_TARGETS += MODULES + +TESTING_JS_MODULES := $(addprefix modules-testing/,mocks.jsm) +TESTING_JS_MODULE_DIR := services/datareporting + +EXTRA_COMPONENTS := \ + DataReporting.manifest \ + DataReportingService.js \ + $(NULL) + +include $(topsrcdir)/config/rules.mk diff --git a/services/datareporting/datareporting-prefs.js b/services/datareporting/datareporting-prefs.js new file mode 100644 index 0000000000000..11d6a564fbaba --- /dev/null +++ b/services/datareporting/datareporting-prefs.js @@ -0,0 +1,12 @@ +/* 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/. */ + +pref("datareporting.policy.dataSubmissionEnabled", true); +pref("datareporting.policy.dataSubmissionPolicyAccepted", false); +pref("datareporting.policy.dataSubmissionPolicyBypassAcceptance", false); +pref("datareporting.policy.dataSubmissionPolicyNotifiedTime", "0"); +pref("datareporting.policy.dataSubmissionPolicyResponseType", ""); +pref("datareporting.policy.dataSubmissionPolicyResponseTime", "0"); +pref("datareporting.policy.firstRunTime", "0"); + diff --git a/services/healthreport/modules-testing/mocks.jsm b/services/datareporting/modules-testing/mocks.jsm similarity index 92% rename from services/healthreport/modules-testing/mocks.jsm rename to services/datareporting/modules-testing/mocks.jsm index ea0b40a117470..246c49549da9f 100644 --- a/services/healthreport/modules-testing/mocks.jsm +++ b/services/datareporting/modules-testing/mocks.jsm @@ -12,7 +12,7 @@ Cu.import("resource://services-common/log4moz.js"); this.MockPolicyListener = function MockPolicyListener() { - this._log = Log4Moz.repository.getLogger("HealthReport.Testing.MockPolicyListener"); + this._log = Log4Moz.repository.getLogger("Services.DataReporting.Testing.MockPolicyListener"); this._log.level = Log4Moz.Level["Debug"]; this.requestDataUploadCount = 0; @@ -44,3 +44,4 @@ MockPolicyListener.prototype = { this.lastNotifyRequest = request; }, }; + diff --git a/services/healthreport/policy.jsm b/services/datareporting/policy.jsm similarity index 92% rename from services/healthreport/policy.jsm rename to services/datareporting/policy.jsm index d26470f799e8d..d673453c697e4 100644 --- a/services/healthreport/policy.jsm +++ b/services/datareporting/policy.jsm @@ -2,11 +2,22 @@ * 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/. */ +/** + * This file is in transition. It was originally conceived to fulfill the + * needs of only Firefox Health Report. It is slowly being morphed into + * fulfilling the needs of all data reporting facilities in Gecko applications. + * As a result, some things feel a bit weird. + * + * DataReportingPolicy is both a driver for data reporting notification + * (a true policy) and the driver for FHR data submission. The latter should + * eventually be split into its own type and module. + */ + "use strict"; this.EXPORTED_SYMBOLS = [ "DataSubmissionRequest", // For test use only. - "HealthReportPolicy", + "DataReportingPolicy", ]; const {classes: Cc, interfaces: Ci, utils: Cu} = Components; @@ -41,7 +52,7 @@ const OLDEST_ALLOWED_YEAR = 2012; * signaling explicit user acceptance or rejection of the policy. They do this * by calling `onUserAccept` or `onUserReject`, respectively. These functions * are essentially proxies to - * HealthReportPolicy.{recordUserAcceptance,recordUserRejection}. + * DataReportingPolicy.{recordUserAcceptance,recordUserRejection}. * * If the user never explicitly accepts or rejects the policy, it will be * implicitly accepted after a specified duration of time. The notice is @@ -53,7 +64,7 @@ const OLDEST_ALLOWED_YEAR = 2012; * the exception of the on* functions. * * @param policy - * (HealthReportPolicy) The policy instance this request came from. + * (DataReportingPolicy) The policy instance this request came from. * @param promise * (deferred) The promise that will be fulfilled when display occurs. */ @@ -235,15 +246,17 @@ Object.freeze(DataSubmissionRequest.prototype); * can have different mechanisms by which they notify the user of data * submission practices. * - * @param prefs + * @param policyPrefs * (Preferences) Handle on preferences branch on which state will be * queried and stored. + * @param healthReportPrefs + * (Preferences) Handle on preferences branch hold Health Report state. * @param listener * (object) Object with callbacks that will be invoked at certain key * events. */ -this.HealthReportPolicy = function HealthReportPolicy(prefs, listener) { - this._log = Log4Moz.repository.getLogger("Services.HealthReport.Policy"); +this.DataReportingPolicy = function (prefs, healthReportPrefs, listener) { + this._log = Log4Moz.repository.getLogger("Services.DataReporting.Policy"); this._log.level = Log4Moz.Level["Debug"]; for (let handler of this.REQUIRED_LISTENERS) { @@ -254,6 +267,7 @@ this.HealthReportPolicy = function HealthReportPolicy(prefs, listener) { } this._prefs = prefs; + this._healthReportPrefs = healthReportPrefs; this._listener = listener; // If we've never run before, record the current time. @@ -276,7 +290,7 @@ this.HealthReportPolicy = function HealthReportPolicy(prefs, listener) { this._inProgressSubmissionRequest = null; } -HealthReportPolicy.prototype = { +DataReportingPolicy.prototype = Object.freeze({ /** * How long after first run we should notify about data submission. */ @@ -444,22 +458,6 @@ HealthReportPolicy.prototype = { this._prefs.set("dataSubmissionEnabled", !!value); }, - /** - * Whether upload of data is allowed. - * - * This is a kill switch for upload. It is meant to reflect a system or - * deployment policy decision. User intent should be reflected in the - * "dataSubmissionPolicy" prefs. - */ - get dataUploadEnabled() { - // Default is true because we are opt-out. - return this._prefs.get("dataUploadEnabled", true); - }, - - set dataUploadEnabled(value) { - this._prefs.set("dataUploadEnabled", !!value); - }, - /** * Whether the user has accepted that data submission can occur. * @@ -474,13 +472,17 @@ HealthReportPolicy.prototype = { this._prefs.set("dataSubmissionPolicyAccepted", !!value); }, + set dataSubmissionPolicyAcceptedVersion(value) { + this._prefs.set("dataSubmissionPolicyAcceptedVersion", value); + }, + /** * The state of user notification of the data policy. * - * This must be HealthReportPolicy.STATE_NOTIFY_COMPLETE before data + * This must be DataReportingPolicy.STATE_NOTIFY_COMPLETE before data * submission can occur. * - * @return HealthReportPolicy.STATE_NOTIFY_* constant. + * @return DataReportingPolicy.STATE_NOTIFY_* constant. */ get notifyState() { if (this.dataSubmissionPolicyResponseDate.getTime()) { @@ -505,13 +507,14 @@ HealthReportPolicy.prototype = { * on scheduling or run-time behavior. */ get lastDataSubmissionRequestedDate() { - return CommonUtils.getDatePref(this._prefs, + return CommonUtils.getDatePref(this._healthReportPrefs, "lastDataSubmissionRequestedTime", 0, this._log, OLDEST_ALLOWED_YEAR); }, set lastDataSubmissionRequestedDate(value) { - CommonUtils.setDatePref(this._prefs, "lastDataSubmissionRequestedTime", + CommonUtils.setDatePref(this._healthReportPrefs, + "lastDataSubmissionRequestedTime", value, OLDEST_ALLOWED_YEAR); }, @@ -522,13 +525,14 @@ HealthReportPolicy.prototype = { * actual scheduling. */ get lastDataSubmissionSuccessfulDate() { - return CommonUtils.getDatePref(this._prefs, + return CommonUtils.getDatePref(this._healthReportPrefs, "lastDataSubmissionSuccessfulTime", 0, this._log, OLDEST_ALLOWED_YEAR); }, set lastDataSubmissionSuccessfulDate(value) { - CommonUtils.setDatePref(this._prefs, "lastDataSubmissionSuccessfulTime", + CommonUtils.setDatePref(this._healthReportPrefs, + "lastDataSubmissionSuccessfulTime", value, OLDEST_ALLOWED_YEAR); }, @@ -539,13 +543,15 @@ HealthReportPolicy.prototype = { * scheduling. */ get lastDataSubmissionFailureDate() { - return CommonUtils.getDatePref(this._prefs, "lastDataSubmissionFailureTime", + return CommonUtils.getDatePref(this._healthReportPrefs, + "lastDataSubmissionFailureTime", 0, this._log, OLDEST_ALLOWED_YEAR); }, set lastDataSubmissionFailureDate(value) { - CommonUtils.setDatePref(this._prefs, "lastDataSubmissionFailureTime", value, - OLDEST_ALLOWED_YEAR); + CommonUtils.setDatePref(this._healthReportPrefs, + "lastDataSubmissionFailureTime", + value, OLDEST_ALLOWED_YEAR); }, /** @@ -555,12 +561,14 @@ HealthReportPolicy.prototype = { * mutate this value. */ get nextDataSubmissionDate() { - return CommonUtils.getDatePref(this._prefs, "nextDataSubmissionTime", 0, + return CommonUtils.getDatePref(this._healthReportPrefs, + "nextDataSubmissionTime", 0, this._log, OLDEST_ALLOWED_YEAR); }, set nextDataSubmissionDate(value) { - CommonUtils.setDatePref(this._prefs, "nextDataSubmissionTime", value, + CommonUtils.setDatePref(this._healthReportPrefs, + "nextDataSubmissionTime", value, OLDEST_ALLOWED_YEAR); }, @@ -570,7 +578,7 @@ HealthReportPolicy.prototype = { * This is used to drive backoff and scheduling. */ get currentDaySubmissionFailureCount() { - let v = this._prefs.get("currentDaySubmissionFailureCount", 0); + let v = this._healthReportPrefs.get("currentDaySubmissionFailureCount", 0); if (!Number.isInteger(v)) { v = 0; @@ -584,7 +592,7 @@ HealthReportPolicy.prototype = { throw new Error("Value must be integer: " + value); } - this._prefs.set("currentDaySubmissionFailureCount", value); + this._healthReportPrefs.set("currentDaySubmissionFailureCount", value); }, /** @@ -595,11 +603,22 @@ HealthReportPolicy.prototype = { * the remote deletion is fulfilled. */ get pendingDeleteRemoteData() { - return !!this._prefs.get("pendingDeleteRemoteData", false); + return !!this._healthReportPrefs.get("pendingDeleteRemoteData", false); }, set pendingDeleteRemoteData(value) { - this._prefs.set("pendingDeleteRemoteData", !!value); + this._healthReportPrefs.set("pendingDeleteRemoteData", !!value); + }, + + /** + * Whether upload of Firefox Health Report data is enabled. + */ + get healthReportUploadEnabled() { + return !!this._healthReportPrefs.get("uploadEnabled", true); + }, + + set healthReportUploadEnabled(value) { + this._healthReportPrefs.set("uploadEnabled", !!value); }, /** @@ -620,6 +639,7 @@ HealthReportPolicy.prototype = { this.dataSubmissionPolicyResponseDate = this.now(); this.dataSubmissionPolicyResponseType = "accepted-" + reason; this.dataSubmissionPolicyAccepted = true; + this.dataSubmissionPolicyAcceptedVersion = 1; }, /** @@ -753,7 +773,7 @@ HealthReportPolicy.prototype = { return this._dispatchSubmissionRequest("onRequestRemoteDelete", true); } - if (!this.dataUploadEnabled) { + if (!this.healthReportUploadEnabled) { this._log.debug("Data upload is disabled. Doing nothing."); return; } @@ -1003,7 +1023,5 @@ HealthReportPolicy.prototype = { _futureDate: function _futureDate(offset) { return new Date(this.now().getTime() + offset); }, -}; - -Object.freeze(HealthReportPolicy.prototype); +}); diff --git a/services/datareporting/tests/Makefile.in b/services/datareporting/tests/Makefile.in new file mode 100644 index 0000000000000..2332dc0cc9c22 --- /dev/null +++ b/services/datareporting/tests/Makefile.in @@ -0,0 +1,16 @@ +# 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/. + +DEPTH = @DEPTH@ +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ +relativesrcdir = @relativesrcdir@ + +include $(DEPTH)/config/autoconf.mk + +XPCSHELL_TESTS = xpcshell + +include $(topsrcdir)/config/rules.mk + diff --git a/services/healthreport/tests/xpcshell/test_policy.js b/services/datareporting/tests/xpcshell/test_policy.js similarity index 84% rename from services/healthreport/tests/xpcshell/test_policy.js rename to services/datareporting/tests/xpcshell/test_policy.js index c0c6a56dc3519..9b8c95e466742 100644 --- a/services/healthreport/tests/xpcshell/test_policy.js +++ b/services/datareporting/tests/xpcshell/test_policy.js @@ -6,15 +6,19 @@ const {utils: Cu} = Components; Cu.import("resource://services-common/preferences.js"); -Cu.import("resource://gre/modules/services/healthreport/policy.jsm"); -Cu.import("resource://testing-common/services/healthreport/mocks.jsm"); +Cu.import("resource://gre/modules/services/datareporting/policy.jsm"); +Cu.import("resource://testing-common/services/datareporting/mocks.jsm"); function getPolicy(name) { - let prefs = new Preferences(name); + let branch = "testing.datareporting." + name; + let policyPrefs = new Preferences(branch + ".policy."); + let healthReportPrefs = new Preferences(branch + ".healthreport."); + let listener = new MockPolicyListener(); + let policy = new DataReportingPolicy(policyPrefs, healthReportPrefs, listener); - return [new HealthReportPolicy(prefs, listener), prefs, listener]; + return [policy, policyPrefs, healthReportPrefs, listener]; } function defineNow(policy, now) { @@ -32,14 +36,15 @@ function run_test() { } add_test(function test_constructor() { - let prefs = new Preferences("foo.bar"); + let policyPrefs = new Preferences("foo.bar.policy."); + let hrPrefs = new Preferences("foo.bar.healthreport."); let listener = { onRequestDataUpload: function() {}, onRequestRemoteDelete: function() {}, onNotifyDataPolicy: function() {}, }; - let policy = new HealthReportPolicy(prefs, listener); + let policy = new DataReportingPolicy(policyPrefs, hrPrefs, listener); do_check_true(Date.now() - policy.firstRunDate.getTime() < 1000); let tomorrow = Date.now() + 24 * 60 * 60 * 1000; @@ -51,68 +56,75 @@ add_test(function test_constructor() { }); add_test(function test_prefs() { - let [policy, prefs, listener] = getPolicy("prefs"); + let [policy, policyPrefs, hrPrefs, listener] = getPolicy("prefs"); let now = new Date(); let nowT = now.getTime(); policy.firstRunDate = now; - do_check_eq(prefs.get("firstRunTime"), nowT); + do_check_eq(policyPrefs.get("firstRunTime"), nowT); do_check_eq(policy.firstRunDate.getTime(), nowT); policy.dataSubmissionPolicyNotifiedDate= now; - do_check_eq(prefs.get("dataSubmissionPolicyNotifiedTime"), nowT); + do_check_eq(policyPrefs.get("dataSubmissionPolicyNotifiedTime"), nowT); do_check_eq(policy.dataSubmissionPolicyNotifiedDate.getTime(), nowT); policy.dataSubmissionPolicyResponseDate = now; - do_check_eq(prefs.get("dataSubmissionPolicyResponseTime"), nowT); + do_check_eq(policyPrefs.get("dataSubmissionPolicyResponseTime"), nowT); do_check_eq(policy.dataSubmissionPolicyResponseDate.getTime(), nowT); policy.dataSubmissionPolicyResponseType = "type-1"; - do_check_eq(prefs.get("dataSubmissionPolicyResponseType"), "type-1"); + do_check_eq(policyPrefs.get("dataSubmissionPolicyResponseType"), "type-1"); do_check_eq(policy.dataSubmissionPolicyResponseType, "type-1"); policy.dataSubmissionEnabled = false; - do_check_false(prefs.get("dataSubmissionEnabled", true)); + do_check_false(policyPrefs.get("dataSubmissionEnabled", true)); do_check_false(policy.dataSubmissionEnabled); policy.dataSubmissionPolicyAccepted = false; - do_check_false(prefs.get("dataSubmissionPolicyAccepted", true)); + do_check_false(policyPrefs.get("dataSubmissionPolicyAccepted", true)); do_check_false(policy.dataSubmissionPolicyAccepted); + policy.dataSubmissionPolicyAcceptedVersion = 2; + do_check_eq(policyPrefs.get("dataSubmissionPolicyAcceptedVersion"), 2); + do_check_false(policy.dataSubmissionPolicyBypassAcceptance); - prefs.set("dataSubmissionPolicyBypassAcceptance", true); + policyPrefs.set("dataSubmissionPolicyBypassAcceptance", true); do_check_true(policy.dataSubmissionPolicyBypassAcceptance); policy.lastDataSubmissionRequestedDate = now; - do_check_eq(prefs.get("lastDataSubmissionRequestedTime"), nowT); + do_check_eq(hrPrefs.get("lastDataSubmissionRequestedTime"), nowT); do_check_eq(policy.lastDataSubmissionRequestedDate.getTime(), nowT); policy.lastDataSubmissionSuccessfulDate = now; - do_check_eq(prefs.get("lastDataSubmissionSuccessfulTime"), nowT); + do_check_eq(hrPrefs.get("lastDataSubmissionSuccessfulTime"), nowT); do_check_eq(policy.lastDataSubmissionSuccessfulDate.getTime(), nowT); policy.lastDataSubmissionFailureDate = now; - do_check_eq(prefs.get("lastDataSubmissionFailureTime"), nowT); + do_check_eq(hrPrefs.get("lastDataSubmissionFailureTime"), nowT); do_check_eq(policy.lastDataSubmissionFailureDate.getTime(), nowT); policy.nextDataSubmissionDate = now; - do_check_eq(prefs.get("nextDataSubmissionTime"), nowT); + do_check_eq(hrPrefs.get("nextDataSubmissionTime"), nowT); do_check_eq(policy.nextDataSubmissionDate.getTime(), nowT); policy.currentDaySubmissionFailureCount = 2; - do_check_eq(prefs.get("currentDaySubmissionFailureCount", 0), 2); + do_check_eq(hrPrefs.get("currentDaySubmissionFailureCount", 0), 2); do_check_eq(policy.currentDaySubmissionFailureCount, 2); policy.pendingDeleteRemoteData = true; - do_check_true(prefs.get("pendingDeleteRemoteData")); + do_check_true(hrPrefs.get("pendingDeleteRemoteData")); do_check_true(policy.pendingDeleteRemoteData); + policy.healthReportUploadEnabled = false; + do_check_false(hrPrefs.get("uploadEnabled")); + do_check_false(policy.healthReportUploadEnabled); + run_next_test(); }); add_test(function test_notify_state_prefs() { - let [policy, prefs, listener] = getPolicy("notify_state_prefs"); + let [policy, policyPrefs, hrPrefs, listener] = getPolicy("notify_state_prefs"); do_check_eq(policy.notifyState, policy.STATE_NOTIFY_UNNOTIFIED); @@ -127,7 +139,7 @@ add_test(function test_notify_state_prefs() { }); add_test(function test_initial_submission_notification() { - let [policy, prefs, listener] = getPolicy("initial_submission_notification"); + let [policy, policyPrefs, hrPrefs, listener] = getPolicy("initial_submission_notification"); do_check_eq(listener.notifyUserCount, 0); @@ -159,9 +171,9 @@ add_test(function test_initial_submission_notification() { }); add_test(function test_bypass_acceptance() { - let [policy, prefs, listener] = getPolicy("bypass_acceptance"); + let [policy, policyPrefs, hrPrefs, listener] = getPolicy("bypass_acceptance"); - prefs.set("dataSubmissionPolicyBypassAcceptance", true); + policyPrefs.set("dataSubmissionPolicyBypassAcceptance", true); do_check_false(policy.dataSubmissionPolicyAccepted); do_check_true(policy.dataSubmissionPolicyBypassAcceptance); defineNow(policy, new Date(policy.nextDataSubmissionDate.getTime())); @@ -172,7 +184,7 @@ add_test(function test_bypass_acceptance() { }); add_test(function test_notification_implicit_acceptance() { - let [policy, prefs, listener] = getPolicy("notification_implicit_acceptance"); + let [policy, policyPrefs, hrPrefs, listener] = getPolicy("notification_implicit_acceptance"); let now = new Date(policy.nextDataSubmissionDate.getTime() - policy.SUBMISSION_NOTIFY_INTERVAL_MSEC + 1); @@ -202,7 +214,7 @@ add_test(function test_notification_implicit_acceptance() { add_test(function test_notification_rejected() { // User notification failed. We should not record it as being presented. - let [policy, prefs, listener] = getPolicy("notification_failed"); + let [policy, policyPrefs, hrPrefs, listener] = getPolicy("notification_failed"); let now = new Date(policy.nextDataSubmissionDate.getTime() - policy.SUBMISSION_NOTIFY_INTERVAL_MSEC + 1); @@ -218,7 +230,7 @@ add_test(function test_notification_rejected() { }); add_test(function test_notification_accepted() { - let [policy, prefs, listener] = getPolicy("notification_accepted"); + let [policy, policyPrefs, hrPrefs, listener] = getPolicy("notification_accepted"); let now = new Date(policy.nextDataSubmissionDate.getTime() - policy.SUBMISSION_NOTIFY_INTERVAL_MSEC + 1); @@ -238,7 +250,7 @@ add_test(function test_notification_accepted() { }); add_test(function test_notification_rejected() { - let [policy, prefs, listener] = getPolicy("notification_rejected"); + let [policy, policyPrefs, hrPrefs, listener] = getPolicy("notification_rejected"); let now = new Date(policy.nextDataSubmissionDate.getTime() - policy.SUBMISSION_NOTIFY_INTERVAL_MSEC + 1); @@ -261,11 +273,12 @@ add_test(function test_notification_rejected() { }); add_test(function test_submission_kill_switch() { - let [policy, prefs, listener] = getPolicy("submission_kill_switch"); + let [policy, policyPrefs, hrPrefs, listener] = getPolicy("submission_kill_switch"); policy.firstRunDate = new Date(Date.now() - 3 * 24 * 60 * 60 * 1000); policy.nextDataSubmissionDate = new Date(Date.now() - 24 * 60 * 60 * 1000); policy.recordUserAcceptance("accept-old-ack"); + do_check_eq(policyPrefs.get("dataSubmissionPolicyAcceptedVersion"), 1); policy.checkStateAndTrigger(); do_check_eq(listener.requestDataUploadCount, 1); @@ -279,16 +292,16 @@ add_test(function test_submission_kill_switch() { }); add_test(function test_upload_kill_switch() { - let [policy, prefs, listener] = getPolicy("upload_kill_switch"); + let [policy, policyPrefs, hrPrefs, listener] = getPolicy("upload_kill_switch"); defineNow(policy, policy._futureDate(-24 * 60 * 60 * 1000)); policy.recordUserAcceptance(); defineNow(policy, policy.nextDataSubmissionDate); - policy.dataUploadEnabled = false; + policy.healthReportUploadEnabled = false; policy.checkStateAndTrigger(); do_check_eq(listener.requestDataUploadCount, 0); - policy.dataUploadEnabled = true; + policy.healthReportUploadEnabled = true; policy.checkStateAndTrigger(); do_check_eq(listener.requestDataUploadCount, 1); @@ -296,7 +309,7 @@ add_test(function test_upload_kill_switch() { }); add_test(function test_data_submission_no_data() { - let [policy, prefs, listener] = getPolicy("data_submission_no_data"); + let [policy, policyPrefs, hrPrefs, listener] = getPolicy("data_submission_no_data"); policy.dataSubmissionPolicyResponseDate = new Date(Date.now() - 24 * 60 * 60 * 1000); policy.dataSubmissionPolicyAccepted = true; @@ -316,7 +329,7 @@ add_test(function test_data_submission_no_data() { }); add_test(function test_data_submission_submit_failure_hard() { - let [policy, prefs, listener] = getPolicy("data_submission_submit_failure_hard"); + let [policy, policyPrefs, hrPrefs, listener] = getPolicy("data_submission_submit_failure_hard"); policy.dataSubmissionPolicyResponseDate = new Date(Date.now() - 24 * 60 * 60 * 1000); policy.dataSubmissionPolicyAccepted = true; @@ -341,7 +354,7 @@ add_test(function test_data_submission_submit_failure_hard() { }); add_test(function test_data_submission_submit_try_again() { - let [policy, prefs, listener] = getPolicy("data_submission_failure_soft"); + let [policy, policyPrefs, hrPrefs, listener] = getPolicy("data_submission_failure_soft"); policy.recordUserAcceptance(); let nextDataSubmissionDate = policy.nextDataSubmissionDate; @@ -356,7 +369,7 @@ add_test(function test_data_submission_submit_try_again() { }); add_test(function test_submission_daily_scheduling() { - let [policy, prefs, listener] = getPolicy("submission_daily_scheduling"); + let [policy, policyPrefs, hrPrefs, listener] = getPolicy("submission_daily_scheduling"); policy.dataSubmissionPolicyResponseDate = new Date(Date.now() - 24 * 60 * 60 * 1000); policy.dataSubmissionPolicyAccepted = true; @@ -396,7 +409,7 @@ add_test(function test_submission_daily_scheduling() { }); add_test(function test_submission_far_future_scheduling() { - let [policy, prefs, listener] = getPolicy("submission_far_future_scheduling"); + let [policy, policyPrefs, hrPrefs, listener] = getPolicy("submission_far_future_scheduling"); let now = new Date(Date.now() - 24 * 60 * 60 * 1000); defineNow(policy, now); @@ -420,7 +433,7 @@ add_test(function test_submission_far_future_scheduling() { }); add_test(function test_submission_backoff() { - let [policy, prefs, listener] = getPolicy("submission_backoff"); + let [policy, policyPrefs, hrPrefs, listener] = getPolicy("submission_backoff"); do_check_eq(policy.FAILURE_BACKOFF_INTERVALS.length, 2); @@ -483,7 +496,7 @@ add_test(function test_submission_backoff() { // Ensure that only one submission request can be active at a time. add_test(function test_submission_expiring() { - let [policy, prefs, listener] = getPolicy("submission_expiring"); + let [policy, policyPrefs, hrPrefs, listener] = getPolicy("submission_expiring"); policy.dataSubmissionPolicyResponseDate = new Date(Date.now() - 24 * 60 * 60 * 1000); policy.dataSubmissionPolicyAccepted = true; @@ -506,7 +519,7 @@ add_test(function test_submission_expiring() { }); add_test(function test_delete_remote_data() { - let [policy, prefs, listener] = getPolicy("delete_remote_data"); + let [policy, policyPrefs, hrPrefs, listener] = getPolicy("delete_remote_data"); do_check_false(policy.pendingDeleteRemoteData); let nextSubmissionDate = policy.nextDataSubmissionDate; @@ -532,7 +545,7 @@ add_test(function test_delete_remote_data() { // Ensure that deletion requests take priority over regular data submission. add_test(function test_delete_remote_data_priority() { - let [policy, prefs, listener] = getPolicy("delete_remote_data_priority"); + let [policy, policyPrefs, hrPrefs, listener] = getPolicy("delete_remote_data_priority"); let now = new Date(); defineNow(policy, policy._futureDate(-24 * 60 * 60 * 1000)); @@ -553,7 +566,7 @@ add_test(function test_delete_remote_data_priority() { }); add_test(function test_delete_remote_data_backoff() { - let [policy, prefs, listener] = getPolicy("delete_remote_data_backoff"); + let [policy, policyPrefs, hrPrefs, listener] = getPolicy("delete_remote_data_backoff"); let now = new Date(); defineNow(policy, policy._futureDate(-24 * 60 * 60 * 1000)); @@ -586,7 +599,7 @@ add_test(function test_delete_remote_data_backoff() { // If we request delete while an upload is in progress, delete should be // scheduled immediately after upload. add_test(function test_delete_remote_data_in_progress_upload() { - let [policy, prefs, listener] = getPolicy("delete_remote_data_in_progress_upload"); + let [policy, policyPrefs, hrPrefs, listener] = getPolicy("delete_remote_data_in_progress_upload"); let now = new Date(); defineNow(policy, policy._futureDate(-24 * 60 * 60 * 1000)); @@ -616,7 +629,7 @@ add_test(function test_delete_remote_data_in_progress_upload() { }); add_test(function test_polling() { - let [policy, prefs, listener] = getPolicy("polling"); + let [policy, policyPrefs, hrPrefs, listener] = getPolicy("polling"); // Ensure checkStateAndTrigger is called at a regular interval. let now = new Date(); @@ -632,7 +645,7 @@ add_test(function test_polling() { do_check_true(now2.getTime() - now.getTime() >= 500); now = now2; - HealthReportPolicy.prototype.checkStateAndTrigger.call(policy); + DataReportingPolicy.prototype.checkStateAndTrigger.call(policy); if (count >= 2) { policy.stopPolling(); @@ -652,7 +665,7 @@ add_test(function test_polling() { // This is probably covered by other tests. But, it's best to have explicit // coverage from a higher-level. add_test(function test_polling_implicit_acceptance() { - let [policy, prefs, listener] = getPolicy("polling_implicit_acceptance"); + let [policy, policyPrefs, hrPrefs, listener] = getPolicy("polling_implicit_acceptance"); // Redefine intervals with shorter, test-friendly values. Object.defineProperty(policy, "POLL_INTERVAL_MSEC", { @@ -670,7 +683,7 @@ add_test(function test_polling_implicit_acceptance() { print("checkStateAndTrigger count: " + count); // Account for some slack. - HealthReportPolicy.prototype.checkStateAndTrigger.call(policy); + DataReportingPolicy.prototype.checkStateAndTrigger.call(policy); // What should happen on different invocations: // diff --git a/services/datareporting/tests/xpcshell/xpcshell.ini b/services/datareporting/tests/xpcshell/xpcshell.ini new file mode 100644 index 0000000000000..72b1edeab5ccc --- /dev/null +++ b/services/datareporting/tests/xpcshell/xpcshell.ini @@ -0,0 +1,5 @@ +[DEFAULT] +head = +tail = + +[test_policy.js] diff --git a/services/healthreport/HealthReportComponents.manifest b/services/healthreport/HealthReportComponents.manifest index b9ff2708ab04d..44b737e94d188 100644 --- a/services/healthreport/HealthReportComponents.manifest +++ b/services/healthreport/HealthReportComponents.manifest @@ -1,14 +1,4 @@ -# b2g: {3c2e2abc-06d4-11e1-ac3b-374f68613e61} -# browser: {ec8030f7-c20a-464f-9b0e-13a3a9e97384} -# mobile/android: {aa3c5121-dab2-40e2-81ca-7ea25febc110} -# mobile/xul: {a23983c0-fd0e-11dc-95ff-0800200c9a66} -# suite (comm): {92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a} -# metro browser: {99bceaaa-e3c6-48c1-b981-ef9b46b67d60} - -component {e354c59b-b252-4040-b6dd-b71864e3e35c} HealthReportService.js -contract @mozilla.org/healthreport/service;1 {e354c59b-b252-4040-b6dd-b71864e3e35c} -category app-startup HealthReportService service,@mozilla.org/healthreport/service;1 application={3c2e2abc-06d4-11e1-ac3b-374f68613e61} application={ec8030f7-c20a-464f-9b0e-13a3a9e97384} application={aa3c5121-dab2-40e2-81ca-7ea25febc110} application={a23983c0-fd0e-11dc-95ff-0800200c9a66} - +# Register Firefox Health Report providers. category healthreport-js-provider AddonsProvider resource://gre/modules/services/healthreport/providers.jsm category healthreport-js-provider AppInfoProvider resource://gre/modules/services/healthreport/providers.jsm category healthreport-js-provider CrashesProvider resource://gre/modules/services/healthreport/providers.jsm diff --git a/services/healthreport/Makefile.in b/services/healthreport/Makefile.in index 08ba81dbc9df3..9db31b75e9479 100644 --- a/services/healthreport/Makefile.in +++ b/services/healthreport/Makefile.in @@ -11,13 +11,11 @@ include $(DEPTH)/config/autoconf.mk modules := \ healthreporter.jsm \ - policy.jsm \ profile.jsm \ providers.jsm \ $(NULL) testing_modules := \ - mocks.jsm \ utils.jsm \ $(NULL) @@ -32,7 +30,6 @@ TESTING_JS_MODULE_DIR := services/healthreport EXTRA_COMPONENTS := \ HealthReportComponents.manifest \ - HealthReportService.js \ $(NULL) include $(topsrcdir)/config/rules.mk diff --git a/services/healthreport/healthreport-prefs.js b/services/healthreport/healthreport-prefs.js index 09d2d38b196f0..7cd6f562d7dac 100644 --- a/services/healthreport/healthreport-prefs.js +++ b/services/healthreport/healthreport-prefs.js @@ -2,23 +2,22 @@ * 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/. */ -pref("healthreport.documentServerURI", "https://data.mozilla.com/"); -pref("healthreport.documentServerNamespace", "metrics"); -pref("healthreport.logging.consoleEnabled", true); -pref("healthreport.logging.consoleLevel", "Warn"); -pref("healthreport.policy.currentDaySubmissionFailureCount", 0); -pref("healthreport.policy.dataSubmissionEnabled", true); -pref("healthreport.policy.dataSubmissionPolicyAccepted", false); -pref("healthreport.policy.dataSubmissionPolicyBypassAcceptance", false); -pref("healthreport.policy.dataSubmissionPolicyNotifiedTime", "0"); -pref("healthreport.policy.dataSubmissionPolicyResponseType", ""); -pref("healthreport.policy.dataSubmissionPolicyResponseTime", "0"); -pref("healthreport.policy.firstRunTime", "0"); -pref("healthreport.policy.lastDataSubmissionFailureTime", "0"); -pref("healthreport.policy.lastDataSubmissionRequestedTime", "0"); -pref("healthreport.policy.lastDataSubmissionSuccessfulTime", "0"); -pref("healthreport.policy.nextDataSubmissionTime", "0"); -pref("healthreport.service.enabled", true); -pref("healthreport.service.loadDelayMsec", 10000); -pref("healthreport.service.providerCategories", "healthreport-js-provider"); -pref("healthreport.infoURL", "http://www.mozilla.org/legal/privacy/firefox.html#health-report"); +pref("datareporting.healthreport.currentDaySubmissionFailureCount", 0); +pref("datareporting.healthreport.documentServerURI", "https://data.mozilla.com/"); +pref("datareporting.healthreport.documentServerNamespace", "metrics"); +pref("datareporting.healthreport.infoURL", "http://www.mozilla.org/legal/privacy/firefox.html#health-report"); +pref("datareporting.healthreport.logging.consoleEnabled", true); +pref("datareporting.healthreport.logging.consoleLevel", "Warn"); +pref("datareporting.healthreport.lastDataSubmissionFailureTime", "0"); +pref("datareporting.healthreport.lastDataSubmissionRequestedTime", "0"); +pref("datareporting.healthreport.lastDataSubmissionSuccessfulTime", "0"); +pref("datareporting.healthreport.nextDataSubmissionTime", "0"); +pref("datareporting.healthreport.pendingDeleteRemoteData", false); + +// Health Report is enabled by default on all channels. +pref("datareporting.healthreport.uploadEnabled", true); + +pref("datareporting.healthreport.service.enabled", true); +pref("datareporting.healthreport.service.loadDelayMsec", 10000); +pref("datareporting.healthreport.service.providerCategories", "healthreport-js-provider"); + diff --git a/services/healthreport/healthreporter.jsm b/services/healthreport/healthreporter.jsm index 72d97a2ae2c5d..75c7b9e239405 100644 --- a/services/healthreport/healthreporter.jsm +++ b/services/healthreport/healthreporter.jsm @@ -11,7 +11,6 @@ const {classes: Cc, interfaces: Ci, utils: Cu} = Components; Cu.import("resource://services-common/async.js"); Cu.import("resource://services-common/bagheeraclient.js"); Cu.import("resource://services-common/log4moz.js"); -Cu.import("resource://services-common/observers.js"); Cu.import("resource://services-common/preferences.js"); Cu.import("resource://services-common/utils.js"); Cu.import("resource://gre/modules/commonjs/promise/core.js"); @@ -20,7 +19,6 @@ Cu.import("resource://gre/modules/osfile.jsm"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/Task.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/services/healthreport/policy.jsm"); // Oldest year to allow in date preferences. This module was implemented in @@ -81,12 +79,19 @@ const DEFAULT_DATABASE_NAME = "healthreport.sqlite"; * @param branch * (string) The preferences branch to use for state storage. The value * must end with a period (.). + * + * @param policy + * (HealthReportPolicy) Policy driving execution of HealthReporter. */ -function HealthReporter(branch) { +function HealthReporter(branch, policy) { if (!branch.endsWith(".")) { throw new Error("Branch must end with a period (.): " + branch); } + if (!policy) { + throw new Error("Must provide policy to HealthReporter constructor."); + } + this._log = Log4Moz.repository.getLogger("Services.HealthReport.HealthReporter"); this._log.info("Initializing health reporter instance against " + branch); @@ -100,10 +105,9 @@ function HealthReporter(branch) { throw new Error("No server namespace defined. Did you forget a pref?"); } - this._dbName = this._prefs.get("dbName") || DEFAULT_DATABASE_NAME; + this._policy = policy; - let policyBranch = new Preferences(branch + "policy."); - this._policy = new HealthReportPolicy(policyBranch, this); + this._dbName = this._prefs.get("dbName") || DEFAULT_DATABASE_NAME; this._storage = null; this._storageInProgress = false; @@ -210,6 +214,14 @@ HealthReporter.prototype = Object.freeze({ this._prefs.set("lastSubmitID", value || ""); }, + /** + * Whether this instance will upload data to a server. + */ + get willUploadData() { + return this._policy.dataSubmissionPolicyAccepted && + this._policy.healthReportUploadEnabled; + }, + /** * Whether remote data is currently stored. * @@ -289,7 +301,6 @@ HealthReporter.prototype = Object.freeze({ return; } - this._policy.startPolling(); this._log.info("HealthReporter started."); this._initialized = true; Services.obs.addObserver(this, "idle-daily", false); @@ -327,9 +338,6 @@ HealthReporter.prototype = Object.freeze({ this._initialized = false; this._shutdownRequested = true; - // Safe to call multiple times. - this._policy.stopPolling(); - if (this._collectorInProgress) { this._log.warn("Collector is in progress of initializing. Waiting to finish."); return; @@ -552,45 +560,14 @@ HealthReporter.prototype = Object.freeze({ }, /** - * Record the user's rejection of the data submission policy. - * - * This should be what everything uses to disable data submission. + * Called to initiate a data upload. * - * @param reason - * (string) Why data submission is being disabled. + * The passed argument is a `DataSubmissionRequest` from policy.jsm. */ - recordPolicyRejection: function (reason) { - this._policy.recordUserRejection(reason); - }, - - /** - * Record the user's acceptance of the data submission policy. - * - * This should be what everything uses to enable data submission. - * - * @param reason - * (string) Why data submission is being enabled. - */ - recordPolicyAcceptance: function (reason) { - this._policy.recordUserAcceptance(reason); - }, - - /** - * Whether the data submission policy has been accepted. - * - * If this is true, health data will be submitted unless one of the kill - * switches is active. - */ - get dataSubmissionPolicyAccepted() { - return this._policy.dataSubmissionPolicyAccepted; - }, - - /** - * Whether this health reporter will upload data to a server. - */ - get willUploadData() { - return this._policy.dataSubmissionPolicyAccepted && - this._policy.dataUploadEnabled; + requestDataUpload: function (request) { + this.collectMeasurements() + .then(this._uploadData.bind(this, request), + this._onSubmitDataRequestFailure.bind(this)); }, /** @@ -768,7 +745,7 @@ HealthReporter.prototype = Object.freeze({ }.bind(this)); }, - _deleteRemoteData: function (request) { + deleteRemoteData: function (request) { if (!this.lastSubmitID) { this._log.info("Received request to delete remote data but no data stored."); request.onNoDataAvailable(); @@ -859,28 +836,5 @@ HealthReporter.prototype = Object.freeze({ return new Date(); }, - //----------------------------- - // HealthReportPolicy listeners - //----------------------------- - - onRequestDataUpload: function (request) { - this.collectMeasurements() - .then(this._uploadData.bind(this, request), - this._onSubmitDataRequestFailure.bind(this)); - }, - - onNotifyDataPolicy: function (request) { - // This isn't very loosely coupled. We may want to have this call - // registered listeners instead. - Observers.notify("healthreport:notify-data-policy:request", request); - }, - - onRequestRemoteDelete: function (request) { - this._deleteRemoteData(request); - }, - - //------------------------------------ - // End of HealthReportPolicy listeners - //------------------------------------ }); diff --git a/services/healthreport/modules-testing/utils.jsm b/services/healthreport/modules-testing/utils.jsm index 593737c431475..db9b3dbba4a90 100644 --- a/services/healthreport/modules-testing/utils.jsm +++ b/services/healthreport/modules-testing/utils.jsm @@ -216,8 +216,8 @@ this.createFakeCrash = function (submitted=false, date=new Date()) { * * The purpose of this type is to aid testing of startup and shutdown. */ -this.InspectedHealthReporter = function (branch) { - HealthReporter.call(this, branch); +this.InspectedHealthReporter = function (branch, policy) { + HealthReporter.call(this, branch, policy); this.onStorageCreated = null; this.onCollectorInitialized = null; diff --git a/services/healthreport/tests/xpcshell/test_healthreporter.js b/services/healthreport/tests/xpcshell/test_healthreporter.js index 7eff9559adbdf..fdabf11324305 100644 --- a/services/healthreport/tests/xpcshell/test_healthreporter.js +++ b/services/healthreport/tests/xpcshell/test_healthreporter.js @@ -9,7 +9,7 @@ Cu.import("resource://services-common/observers.js"); Cu.import("resource://services-common/preferences.js"); Cu.import("resource://gre/modules/commonjs/promise/core.js"); Cu.import("resource://gre/modules/services/healthreport/healthreporter.jsm"); -Cu.import("resource://gre/modules/services/healthreport/policy.jsm"); +Cu.import("resource://gre/modules/services/datareporting/policy.jsm"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/Task.jsm"); Cu.import("resource://testing-common/services-common/bagheeraserver.js"); @@ -36,12 +36,29 @@ function defineNow(policy, now) { function getJustReporter(name, uri=SERVER_URI, inspected=false) { let branch = "healthreport.testing. " + name + "."; - let prefs = new Preferences(branch); + let prefs = new Preferences(branch + "healthreport."); prefs.set("documentServerURI", uri); prefs.set("dbName", name); + let reporter; + + let policyPrefs = new Preferences(branch + "policy."); + let policy = new DataReportingPolicy(policyPrefs, prefs, { + onRequestDataUpload: function (request) { + reporter.requestDataUpload(request); + }, + + onNotifyDataPolicy: function (request) { }, + + onRequestRemoteDelete: function (request) { + reporter.deleteRemoteData(request); + }, + }); + let type = inspected ? InspectedHealthReporter : HealthReporter; - return new type(branch); + reporter = new type(branch + "healthreport.", policy); + + return reporter; } function getReporter(name, uri, inspected) { @@ -251,7 +268,7 @@ add_task(function test_data_submission_transport_failure() { let deferred = Promise.defer(); let request = new DataSubmissionRequest(deferred, new Date(Date.now + 30000)); - reporter.onRequestDataUpload(request); + reporter.requestDataUpload(request); yield deferred.promise; do_check_eq(request.state, request.SUBMISSION_FAILURE_SOFT); @@ -268,7 +285,7 @@ add_task(function test_data_submission_success() { let deferred = Promise.defer(); let request = new DataSubmissionRequest(deferred, new Date()); - reporter.onRequestDataUpload(request); + reporter.requestDataUpload(request); yield deferred.promise; do_check_eq(request.state, request.SUBMISSION_SUCCESS); do_check_true(reporter.lastPingDate.getTime() > 0); @@ -336,22 +353,23 @@ add_task(function test_request_remote_data_deletion() { add_task(function test_policy_accept_reject() { let [reporter, server] = yield getReporterAndServer("policy_accept_reject"); - do_check_false(reporter.dataSubmissionPolicyAccepted); + let policy = reporter._policy; + + do_check_false(policy.dataSubmissionPolicyAccepted); do_check_false(reporter.willUploadData); - reporter.recordPolicyAcceptance(); - do_check_true(reporter.dataSubmissionPolicyAccepted); + policy.recordUserAcceptance(); + do_check_true(policy.dataSubmissionPolicyAccepted); do_check_true(reporter.willUploadData); - reporter.recordPolicyRejection(); - do_check_false(reporter.dataSubmissionPolicyAccepted); + policy.recordUserRejection(); + do_check_false(policy.dataSubmissionPolicyAccepted); do_check_false(reporter.willUploadData); reporter._shutdown(); yield shutdownServer(server); }); - add_task(function test_upload_save_payload() { let [reporter, server] = yield getReporterAndServer("upload_save_payload"); diff --git a/services/healthreport/tests/xpcshell/test_load_modules.js b/services/healthreport/tests/xpcshell/test_load_modules.js index 5a85abab05c49..c52055d3a62be 100644 --- a/services/healthreport/tests/xpcshell/test_load_modules.js +++ b/services/healthreport/tests/xpcshell/test_load_modules.js @@ -5,24 +5,14 @@ const modules = [ "healthreporter.jsm", - "policy.jsm", "profile.jsm", "providers.jsm", ]; -const test_modules = [ - "mocks.jsm", -]; - function run_test() { for (let m of modules) { let resource = "resource://gre/modules/services/healthreport/" + m; Components.utils.import(resource, {}); } - - for (let m of test_modules) { - let resource = "resource://testing-common/services/healthreport/" + m; - Components.utils.import(resource, {}); - } } diff --git a/services/healthreport/tests/xpcshell/xpcshell.ini b/services/healthreport/tests/xpcshell/xpcshell.ini index 3461e38a88a96..5cc0918d6948c 100644 --- a/services/healthreport/tests/xpcshell/xpcshell.ini +++ b/services/healthreport/tests/xpcshell/xpcshell.ini @@ -4,7 +4,6 @@ tail = [test_load_modules.js] [test_profile.js] -[test_policy.js] [test_healthreporter.js] [test_provider_addons.js] [test_provider_appinfo.js] diff --git a/services/makefiles.sh b/services/makefiles.sh index 20fb0c65a87c5..5c61572f87aa6 100644 --- a/services/makefiles.sh +++ b/services/makefiles.sh @@ -9,6 +9,7 @@ add_makefiles " services/crypto/Makefile services/crypto/component/Makefile services/healthreport/Makefile + services/datareporting/Makefile services/metrics/Makefile services/sync/Makefile services/sync/locales/Makefile @@ -20,6 +21,7 @@ if [ "$ENABLE_TESTS" ]; then services/common/tests/Makefile services/crypto/tests/Makefile services/healthreport/tests/Makefile + services/datareporting/tests/Makefile services/metrics/tests/Makefile services/sync/tests/Makefile " diff --git a/testing/xpcshell/xpcshell.ini b/testing/xpcshell/xpcshell.ini index f6d5853192ee1..1c76527c10a06 100644 --- a/testing/xpcshell/xpcshell.ini +++ b/testing/xpcshell/xpcshell.ini @@ -88,6 +88,7 @@ skip-if = os == "android" [include:services/common/tests/unit/xpcshell.ini] [include:services/crypto/tests/unit/xpcshell.ini] [include:services/crypto/components/tests/unit/xpcshell.ini] +[include:services/datareporting/tests/xpcshell/xpcshell.ini] [include:services/healthreport/tests/xpcshell/xpcshell.ini] [include:services/metrics/tests/xpcshell/xpcshell.ini] [include:services/sync/tests/unit/xpcshell.ini]