Bug 1390249: Add PingCentre to browser/modules. draft
authorMarina Samuel <msamuel@mozilla.com>
Tue, 08 Aug 2017 14:41:15 -0400
changeset 653249 0f52a14c88651714efbf6b815888179194aba413
parent 653118 56188620cce00b19700fbb8efaafea65e6ca8c61
child 728295 22c86e093426a5a5f8df95c4f2a030b1bd0f1472
push id76280
push userbmo:msamuel@mozilla.com
push dateFri, 25 Aug 2017 19:17:39 +0000
bugs1390249
milestone57.0a1
Bug 1390249: Add PingCentre to browser/modules. MozReview-Commit-ID: GtwWh0iAqlu
browser/app/profile/firefox.js
browser/base/content/test/static/browser_all_files_referenced.js
browser/modules/PingCentre.jsm
browser/modules/moz.build
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1506,16 +1506,26 @@ pref("toolkit.telemetry.bhrPing.enabled"
 
 // Telemetry experiments settings.
 pref("experiments.enabled", true);
 pref("experiments.manifest.fetchIntervalSeconds", 86400);
 pref("experiments.manifest.uri", "https://telemetry-experiment.cdn.mozilla.net/manifest/v1/firefox/%VERSION%/%CHANNEL%");
 // Whether experiments are supported by the current application profile.
 pref("experiments.supported", true);
 
+// Ping Centre Telemetry settings.
+#ifdef NIGHTLY_BUILD
+pref("browser.ping-centre.telemetry", true);
+#else
+pref("browser.ping-centre.telemetry", false);
+#endif
+pref("browser.ping-centre.log", false);
+pref("browser.ping-centre.staging.endpoint", "https://onyx_tiles.stage.mozaws.net/v3/links/ping-centre");
+pref("browser.ping-centre.production.endpoint", "https://tiles.services.mozilla.com/v3/links/ping-centre");
+
 // Enable GMP support in the addon manager.
 pref("media.gmp-provider.enabled", true);
 
 #ifdef NIGHTLY_BUILD
 pref("privacy.trackingprotection.ui.enabled", true);
 #else
 pref("privacy.trackingprotection.ui.enabled", false);
 #endif
--- a/browser/base/content/test/static/browser_all_files_referenced.js
+++ b/browser/base/content/test/static/browser_all_files_referenced.js
@@ -82,16 +82,19 @@ var whitelist = [
 
   // Add-on API introduced in bug 1118285
   {file: "resource://app/modules/NewTabURL.jsm"},
 
   // browser/components/newtab bug 1355166
   {file: "resource://app/modules/NewTabSearchProvider.jsm"},
   {file: "resource://app/modules/NewTabWebChannel.jsm"},
 
+  // browser/modules/PingCentre.jsm will soon be used (bug 1393604)
+  {file: "resource://app/modules/PingCentre.jsm"},
+
   // layout/mathml/nsMathMLChar.cpp
   {file: "resource://gre/res/fonts/mathfontSTIXGeneral.properties"},
   {file: "resource://gre/res/fonts/mathfontUnicode.properties"},
 
   // toolkit/components/places/ColorAnalyzer_worker.js
   {file: "resource://gre/modules/ClusterLib.js"},
   {file: "resource://gre/modules/ColorConversion.js"},
 
new file mode 100644
--- /dev/null
+++ b/browser/modules/PingCentre.jsm
@@ -0,0 +1,137 @@
+/* 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/. */
+
+const {interfaces: Ci, utils: Cu} = Components;
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.importGlobalProperties(["fetch"]);
+
+XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
+  "resource://gre/modules/AppConstants.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "ClientID",
+  "resource://gre/modules/ClientID.jsm");
+
+const PREF_BRANCH = "browser.ping-centre.";
+
+const TELEMETRY_PREF = `${PREF_BRANCH}telemetry`;
+const LOGGING_PREF = `${PREF_BRANCH}log`;
+const STAGING_ENDPOINT_PREF = `${PREF_BRANCH}staging.endpoint`;
+const PRODUCTION_ENDPOINT_PREF = `${PREF_BRANCH}production.endpoint`;
+
+const FHR_UPLOAD_ENABLED_PREF = "datareporting.healthreport.uploadEnabled";
+
+/**
+ * Observe various notifications and send them to a telemetry endpoint.
+ *
+ * @param {Object} options
+ * @param {string} options.topic - a unique ID for users of PingCentre to distinguish
+ *                  their data on the server side.
+ * @param {string} options.overrideEndpointPref - optional pref for URL where the POST is sent.
+ */
+class PingCentre {
+  constructor(options) {
+    if (!options.topic) {
+      throw new Error("Must specify topic.");
+    }
+
+    this._topic = options.topic;
+    this._prefs = Services.prefs.getBranch("");
+
+    this._setPingEndpoint(options.topic, options.overrideEndpointPref);
+
+    this._enabled = this._prefs.getBoolPref(TELEMETRY_PREF);
+    this._onTelemetryPrefChange = this._onTelemetryPrefChange.bind(this);
+    this._prefs.addObserver(TELEMETRY_PREF, this._onTelemetryPrefChange);
+
+    this._fhrEnabled = this._prefs.getBoolPref(FHR_UPLOAD_ENABLED_PREF);
+    this._onFhrPrefChange = this._onFhrPrefChange.bind(this);
+    this._prefs.addObserver(FHR_UPLOAD_ENABLED_PREF, this._onFhrPrefChange);
+
+    this.logging = this._prefs.getBoolPref(LOGGING_PREF);
+    this._onLoggingPrefChange = this._onLoggingPrefChange.bind(this);
+    this._prefs.addObserver(LOGGING_PREF, this._onLoggingPrefChange);
+  }
+
+  /**
+   * Lazily get the Telemetry id promise
+   */
+  get telemetryClientId() {
+    Object.defineProperty(this, "telemetryClientId", {value: ClientID.getClientID()});
+    return this.telemetryClientId;
+  }
+
+  get enabled() {
+    return this._enabled && this._fhrEnabled;
+  }
+
+  _setPingEndpoint(topic, overrideEndpointPref) {
+    const overrideValue = overrideEndpointPref &&
+      this._prefs.getStringPref(overrideEndpointPref);
+    if (overrideValue) {
+      this._pingEndpoint = overrideValue;
+    } else if (AppConstants.MOZ_UPDATE_CHANNEL === "release") {
+      this._pingEndpoint = this._prefs.getStringPref(PRODUCTION_ENDPOINT_PREF);
+    } else {
+      this._pingEndpoint = this._prefs.getStringPref(STAGING_ENDPOINT_PREF);
+    }
+  }
+
+  _onLoggingPrefChange(aSubject, aTopic, prefKey) {
+    this.logging = this._prefs.getBoolPref(prefKey);
+  }
+
+  _onTelemetryPrefChange(aSubject, aTopic, prefKey) {
+    this._enabled = this._prefs.getBoolPref(prefKey);
+  }
+
+  _onFhrPrefChange(aSubject, aTopic, prefKey) {
+    this._fhrEnabled = this._prefs.getBoolPref(prefKey);
+  }
+
+  async sendPing(data) {
+    if (!this.enabled) {
+      return Promise.resolve();
+    }
+
+    let clientID = data.client_id || await this.telemetryClientId;
+    const payload = Object.assign({
+      topic: this._topic,
+      client_id: clientID
+    }, data);
+
+    if (this.logging) {
+      // performance related pings cause a lot of logging, so we mute them
+      if (data.action !== "activity_stream_performance") {
+        Services.console.logStringMessage(`TELEMETRY PING: ${JSON.stringify(payload)}\n`);
+      }
+    }
+
+    return fetch(this._pingEndpoint, {method: "POST", body: JSON.stringify(payload)}).then(response => {
+      if (!response.ok) {
+        Cu.reportError(`Ping failure with HTTP response code: ${response.status}`);
+      }
+    }).catch(e => {
+      Cu.reportError(`Ping failure with error: ${e}`);
+    });
+  }
+
+  uninit() {
+    try {
+      this._prefs.removeObserver(TELEMETRY_PREF, this._onTelemetryPrefChange);
+      this._prefs.removeObserver(LOGGING_PREF, this._onLoggingPrefChange);
+      this._prefs.removeObserver(FHR_UPLOAD_ENABLED_PREF, this._onFhrPrefChange);
+    } catch (e) {
+      Cu.reportError(e);
+    }
+  }
+}
+
+this.PingCentre = PingCentre;
+this.PingCentreConstants = {
+  PRODUCTION_ENDPOINT_PREF,
+  FHR_UPLOAD_ENABLED_PREF,
+  TELEMETRY_PREF,
+  LOGGING_PREF
+};
+this.EXPORTED_SYMBOLS = ["PingCentre", "PingCentreConstants"];
--- a/browser/modules/moz.build
+++ b/browser/modules/moz.build
@@ -140,16 +140,17 @@ EXTRA_JS_MODULES += [
     'ExtensionsUI.jsm',
     'Feeds.jsm',
     'FormSubmitObserver.jsm',
     'FormValidationHandler.jsm',
     'LaterRun.jsm',
     'offlineAppCache.jsm',
     'PageActions.jsm',
     'PermissionUI.jsm',
+    'PingCentre.jsm',
     'PluginContent.jsm',
     'ProcessHangMonitor.jsm',
     'ReaderParent.jsm',
     'RecentWindow.jsm',
     'RemotePrompt.jsm',
     'Sanitizer.jsm',
     'SitePermissions.jsm',
     'TransientPrefs.jsm',