Bug 944114 - [Australis] Add telemetry probe for usage of 'Metro Mode' and 'Fullscreen' Australis default buttons. r=Gijs.
authorMike Conley <mconley@mozilla.com>
Tue, 10 Dec 2013 14:49:24 -0500
changeset 159722 71524d9e89f0d1b718e5d8fee241a89c0db91eca
parent 159721 db0be93f358d6bb86a268287d4088f32ba9e9832
child 159723 f507aadddd2ad909b434840c1015291995ce01c4
child 159831 1d891ccd0755a91bf2dd119130d194b3457fa2bb
push id25811
push userkwierso@gmail.com
push dateTue, 10 Dec 2013 23:59:03 +0000
treeherdermozilla-central@f507aadddd2a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersGijs
bugs944114
milestone29.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 944114 - [Australis] Add telemetry probe for usage of 'Metro Mode' and 'Fullscreen' Australis default buttons. r=Gijs.
browser/modules/BrowserUITelemetry.jsm
--- a/browser/modules/BrowserUITelemetry.jsm
+++ b/browser/modules/BrowserUITelemetry.jsm
@@ -10,21 +10,137 @@ const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "UITelemetry",
   "resource://gre/modules/UITelemetry.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow",
   "resource:///modules/RecentWindow.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI",
+  "resource:///modules/CustomizableUI.jsm");
+
+const ALL_BUILTIN_ITEMS = [
+  "fullscreen-button",
+  "switch-to-metro-button",
+];
 
 this.BrowserUITelemetry = {
   init: function() {
     UITelemetry.addSimpleMeasureFunction("toolbars",
                                          this.getToolbarMeasures.bind(this));
+    Services.obs.addObserver(this, "browser-delayed-startup-finished", false);
+  },
+
+  observe: function(aSubject, aTopic, aData) {
+    if (aTopic == "browser-delayed-startup-finished") {
+      this._registerWindow(aSubject);
+    }
+  },
+
+  /**
+   * For the _countableEvents object, constructs a chain of
+   * Javascript Objects with the keys in aKeys, with the final
+   * key getting the value in aEndWith. If the final key already
+   * exists in the final object, its value is not set. In either
+   * case, a reference to the second last object in the chain is
+   * returned.
+   *
+   * Example - suppose I want to store:
+   * _countableEvents: {
+   *   a: {
+   *     b: {
+   *       c: 0
+   *     }
+   *   }
+   * }
+   *
+   * And then increment the "c" value by 1, you could call this
+   * function like this:
+   *
+   * let example = this._ensureObjectChain([a, b, c], 0);
+   * example["c"]++;
+   *
+   * Subsequent repetitions of these last two lines would
+   * simply result in the c value being incremented again
+   * and again.
+   *
+   * @param aKeys the Array of keys to chain Objects together with.
+   * @param aEndWith the value to assign to the last key.
+   * @returns a reference to the second last object in the chain -
+   *          so in our example, that'd be "b".
+   */
+  _ensureObjectChain: function(aKeys, aEndWith) {
+    let current = this._countableEvents;
+    let parent = null;
+    for (let [i, key] of Iterator(aKeys)) {
+      if (!(key in current)) {
+        if (i == aKeys.length - 1) {
+          current[key] = aEndWith;
+        } else {
+          current[key] = {};
+        }
+      }
+      parent = current;
+      current = current[key];
+    }
+    return parent;
+  },
+
+  _countableEvents: {},
+  _countMouseUpEvent: function(aCategory, aAction, aMouseUpEvent) {
+    const BUTTONS = ["left", "middle", "right"];
+    let buttonKey = BUTTONS[aMouseUpEvent.button];
+    if (buttonKey) {
+      let countObject =
+        this._ensureObjectChain([aCategory, aAction, buttonKey], 0);
+      countObject[buttonKey]++;
+    }
+  },
+
+  _registerWindow: function(aWindow) {
+    aWindow.addEventListener("unload", this);
+    let document = aWindow.document;
+
+    for (let areaID of CustomizableUI.areas) {
+      let areaNode = document.getElementById(areaID);
+      (areaNode.customizationTarget || areaNode).addEventListener("mouseup", this);
+    }
+  },
+
+  _unregisterWindow: function(aWindow) {
+    aWindow.removeEventListener("unload", this);
+    let document = aWindow.document;
+
+    for (let areaID of CustomizableUI.areas) {
+      let areaNode = document.getElementById(areaID);
+      (areaNode.customizationTarget || areaNode).removeEventListener("mouseup", this);
+    }
+  },
+
+  handleEvent: function(aEvent) {
+    switch(aEvent.type) {
+      case "unload":
+        this._unregisterWindow(aEvent.currentTarget);
+        break;
+      case "mouseup":
+        this._handleMouseUp(aEvent);
+        break;
+    }
+  },
+
+  _handleMouseUp: function(aEvent) {
+    let item = aEvent.originalTarget;
+    // Perhaps we're seeing one of the default toolbar items
+    // being clicked.
+    if (ALL_BUILTIN_ITEMS.indexOf(item.id) != -1) {
+      // Base case - we clicked directly on one of our built-in items,
+      // and we can go ahead and register that click.
+      this._countMouseUpEvent("click-builtin-item", item.id, aEvent);
+    }
   },
 
   getToolbarMeasures: function() {
     // Grab the most recent non-popup, non-private browser window for us to
     // analyze the toolbars in...
     let win = RecentWindow.getMostRecentBrowserWindow({
       private: false,
       allowPopups: false
@@ -33,11 +149,13 @@ this.BrowserUITelemetry = {
     // If there are no such windows, we're out of luck. :(
     if (!win) {
       return {};
     }
 
     let document = win.document;
     let result = {};
 
+    result.countableEvents = this._countableEvents;
+
     return result;
   },
 };