Bug 1101790 - FHRProvider for UITour, records treatment tag data. r=bsmedberg a=gavin
authorFelipe Gomes <felipc@gmail.com>
Thu, 20 Nov 2014 18:21:29 -0200
changeset 236307 166866d8cfcfb1b37cc079239992bb0021873439
parent 236306 659ccf9aac7a59e18d076a164ded1744e150f8f0
child 236308 0bcee2fc8e0b6883a360db16255481e7639ee93a
push id208
push userryanvm@gmail.com
push dateMon, 23 Feb 2015 15:44:39 +0000
treeherdermozilla-b2g37_v2_2@09dafeb43234 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbsmedberg, gavin
bugs1101790
milestone34.0
Bug 1101790 - FHRProvider for UITour, records treatment tag data. r=bsmedberg a=gavin
browser/modules/UITour.jsm
browser/modules/modules.manifest
browser/modules/moz.build
browser/modules/test/head.js
services/healthreport/docs/dataformat.rst
--- a/browser/modules/UITour.jsm
+++ b/browser/modules/UITour.jsm
@@ -1,15 +1,15 @@
 // 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";
 
-this.EXPORTED_SYMBOLS = ["UITour"];
+this.EXPORTED_SYMBOLS = ["UITour", "UITourMetricsProvider"];
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Promise.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 
@@ -18,17 +18,18 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "PermissionsUtils",
   "resource://gre/modules/PermissionsUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI",
   "resource:///modules/CustomizableUI.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "UITelemetry",
   "resource://gre/modules/UITelemetry.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "BrowserUITelemetry",
   "resource:///modules/BrowserUITelemetry.jsm");
-
+XPCOMUtils.defineLazyModuleGetter(this, "Metrics",
+  "resource://gre/modules/Metrics.jsm");
 
 const UITOUR_PERMISSION   = "uitour";
 const PREF_PERM_BRANCH    = "browser.uitour.";
 const PREF_SEENPAGEIDS    = "browser.uitour.seenPageIDs";
 const MAX_BUTTONS         = 4;
 
 const BUCKET_NAME         = "UITour";
 const BUCKET_TIMESTEPS    = [
@@ -1452,8 +1453,110 @@ this.UITour = {
       }.bind(this)).catch(() => {
         reject("Search engine not available");
       });
     });
   }
 };
 
 this.UITour.init();
+
+/**
+ * UITour Health Report
+ */
+const DAILY_DISCRETE_TEXT_FIELD = Metrics.Storage.FIELD_DAILY_DISCRETE_TEXT;
+
+/**
+ * Public API to be called by the UITour code
+ */
+const UITourHealthReport = {
+  recordTreatmentTag: function(tag, value) {
+#ifdef MOZ_SERVICES_HEALTHREPORT
+    Task.spawn(function*() {
+      let reporter = Cc["@mozilla.org/datareporting/service;1"]
+                       .getService()
+                       .wrappedJSObject
+                       .healthReporter;
+
+      // This can happen if the FHR component of the data reporting service is
+      // disabled. This is controlled by a pref that most will never use.
+      if (!reporter) {
+        return;
+      }
+
+      yield reporter.onInit();
+
+      // Get the UITourMetricsProvider instance from the Health Reporter
+      reporter.getProvider("org.mozilla.uitour").recordTreatmentTag(tag, value);
+    });
+#endif
+  }
+};
+
+this.UITourMetricsProvider = function() {
+  Metrics.Provider.call(this);
+}
+
+UITourMetricsProvider.prototype = Object.freeze({
+  __proto__: Metrics.Provider.prototype,
+
+  name: "org.mozilla.uitour",
+
+  measurementTypes: [
+    UITourTreatmentMeasurement1,
+  ],
+
+  recordTreatmentTag: function(tag, value) {
+    let m = this.getMeasurement(UITourTreatmentMeasurement1.prototype.name,
+                                UITourTreatmentMeasurement1.prototype.version);
+    let field = tag;
+
+    if (this.storage.hasFieldFromMeasurement(m.id, field,
+                                             DAILY_DISCRETE_TEXT_FIELD)) {
+      let fieldID = this.storage.fieldIDFromMeasurement(m.id, field);
+      return this.enqueueStorageOperation(function recordKnownField() {
+        return this.storage.addDailyDiscreteTextFromFieldID(fieldID, value);
+      }.bind(this));
+    }
+
+    // Otherwise, we first need to create the field.
+    return this.enqueueStorageOperation(function recordField() {
+      // This function has to return a promise.
+      return Task.spawn(function () {
+        let fieldID = yield this.storage.registerField(m.id, field,
+                                                       DAILY_DISCRETE_TEXT_FIELD);
+        yield this.storage.addDailyDiscreteTextFromFieldID(fieldID, value);
+      }.bind(this));
+    }.bind(this));
+  },
+});
+
+function UITourTreatmentMeasurement1() {
+  Metrics.Measurement.call(this);
+
+  this._serializers = {};
+  this._serializers[this.SERIALIZE_JSON] = {
+    //singular: We don't need a singular serializer because we have none of this data
+    daily: this._serializeJSONDaily.bind(this)
+  };
+
+}
+
+UITourTreatmentMeasurement1.prototype = Object.freeze({
+  __proto__: Metrics.Measurement.prototype,
+
+  name: "treatment",
+  version: 1,
+
+  // our fields are dynamic
+  fields: { },
+
+  // We need a custom serializer because the default one doesn't accept unknown fields
+  _serializeJSONDaily: function(data) {
+    let result = {_v: this.version };
+
+    for (let [field, data] of data) {
+      result[field] = data;
+    }
+
+    return result;
+  }
+});
new file mode 100644
--- /dev/null
+++ b/browser/modules/modules.manifest
@@ -0,0 +1,3 @@
+#ifdef MOZ_SERVICES_HEALTHREPORT
+category healthreport-js-provider-default UITourMetricsProvider resource:///modules/UITour.jsm
+#endif
--- a/browser/modules/moz.build
+++ b/browser/modules/moz.build
@@ -44,10 +44,14 @@ if CONFIG['NIGHTLY_BUILD']:
 
 EXTRA_PP_JS_MODULES += [
     'AboutHome.jsm',
     'RecentWindow.jsm',
     'UITour.jsm',
     'webrtcUI.jsm',
 ]
 
+EXTRA_PP_COMPONENTS += [
+    'modules.manifest',
+]
+
 if CONFIG['MOZILLA_OFFICIAL']:
     DEFINES['MOZILLA_OFFICIAL'] = 1
--- a/browser/modules/test/head.js
+++ b/browser/modules/test/head.js
@@ -137,16 +137,17 @@ function UITourTest() {
   Services.prefs.setBoolPref("browser.uitour.enabled", true);
   let testUri = Services.io.newURI("http://example.com", null, null);
   Services.perms.add(testUri, "uitour", Services.perms.ALLOW_ACTION);
 
   waitForExplicitFinish();
 
   registerCleanupFunction(function() {
     delete window.UITour;
+    delete window.UITourMetricsProvider;
     delete window.gContentWindow;
     delete window.gContentAPI;
     if (gTestTab)
       gBrowser.removeTab(gTestTab);
     delete window.gTestTab;
     Services.prefs.clearUserPref("browser.uitour.enabled", true);
     Services.perms.remove("example.com", "uitour");
   });
--- a/services/healthreport/docs/dataformat.rst
+++ b/services/healthreport/docs/dataformat.rst
@@ -1805,8 +1805,37 @@ Example
 ::
 
     "org.mozilla.experiments.info": {
       "_v": 2,
       "lastActive": "some.experiment.id",
       "lastActiveBranch": "control"
     }
 
+org.mozilla.uitour.treatment
+----------------------------
+
+Daily measurement reporting information about treatment tagging done
+by the UITour module.
+
+Version 1
+^^^^^^^^^
+
+Daily text values in the following properties:
+
+<tag>:
+    Array of discrete strings corresponding to calls for setTreatmentTag(tag, value).
+
+Example
+^^^^^^^
+
+::
+
+    "org.mozilla.uitour.treatment": {
+      "_v": 1,
+      "treatment": [
+        "optin",
+        "optin-DNT"
+      ],
+      "another-tag": [
+        "foobar-value"
+      ]
+    }