gecko/browser/modules/BrowserUITelemetry.jsm

221 lines
6.4 KiB
JavaScript
Raw Normal View History

// 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 = ["BrowserUITelemetry"];
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",
];
const OTHER_MOUSEUP_MONITORED_ITEMS = [
"PlacesChevron",
"PlacesToolbarItems",
];
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, aButton) {
const BUTTONS = ["left", "middle", "right"];
let buttonKey = BUTTONS[aButton];
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);
if (areaNode) {
(areaNode.customizationTarget || areaNode).addEventListener("mouseup", this);
}
}
for (let itemID of OTHER_MOUSEUP_MONITORED_ITEMS) {
let item = document.getElementById(itemID);
if (item) {
item.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);
if (areaNode) {
(areaNode.customizationTarget || areaNode).removeEventListener("mouseup", this);
}
}
for (let itemID of OTHER_MOUSEUP_MONITORED_ITEMS) {
let item = document.getElementById(itemID);
if (item) {
item.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 targetID = aEvent.currentTarget.id;
switch (targetID) {
case "PlacesToolbarItems":
this._PlacesToolbarItemsMouseUp(aEvent);
break;
case "PlacesChevron":
this._PlacesChevronMouseUp(aEvent);
break;
default:
this._checkForBuiltinItem(aEvent);
}
},
_PlacesChevronMouseUp: function(aEvent) {
let target = aEvent.originalTarget;
let result = target.id == "PlacesChevron" ? "chevron" : "overflowed-item";
this._countMouseUpEvent("click-bookmarks-bar", result, aEvent.button);
},
_PlacesToolbarItemsMouseUp: function(aEvent) {
let target = aEvent.originalTarget;
// If this isn't a bookmark-item, we don't care about it.
if (!target.classList.contains("bookmark-item")) {
return;
}
let result = target.hasAttribute("container") ? "container" : "item";
this._countMouseUpEvent("click-bookmarks-bar", result, aEvent.button);
},
_checkForBuiltinItem: 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.button);
}
},
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
});
// If there are no such windows, we're out of luck. :(
if (!win) {
return {};
}
let document = win.document;
let result = {};
// Determine if the Bookmarks bar is currently visible
let bookmarksBar = document.getElementById("PersonalToolbar");
result.bookmarksBarEnabled = bookmarksBar && !bookmarksBar.collapsed;
result.countableEvents = this._countableEvents;
return result;
},
};