зеркало из https://github.com/mozilla/gecko-dev.git
Bug 991757 - add telemetry for the context menu, r=mconley,bgrins,f=bwinton
This commit is contained in:
Родитель
1e7dad43eb
Коммит
e126ede3c2
|
@ -3,6 +3,9 @@
|
|||
# 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/.
|
||||
|
||||
# NB: IF YOU ADD ITEMS TO THIS FILE, PLEASE UPDATE THE WHITELIST IN
|
||||
# BrowserUITelemetry.jsm. SEE BUG 991757 FOR DETAILS.
|
||||
|
||||
<menugroup id="context-navigation">
|
||||
<menuitem id="context-back"
|
||||
class="menuitem-iconic"
|
||||
|
|
|
@ -38,6 +38,9 @@ nsContextMenu.prototype = {
|
|||
|
||||
// Initialize (disable/remove) menu items.
|
||||
this.initItems();
|
||||
|
||||
// Register this opening of the menu with telemetry:
|
||||
this._checkTelemetryForMenu(aXulMenu);
|
||||
},
|
||||
|
||||
hiding: function CM_hiding() {
|
||||
|
@ -45,6 +48,11 @@ nsContextMenu.prototype = {
|
|||
InlineSpellCheckerUI.clearSuggestionsFromMenu();
|
||||
InlineSpellCheckerUI.clearDictionaryListFromMenu();
|
||||
InlineSpellCheckerUI.uninit();
|
||||
|
||||
// This handler self-deletes, only run it if it is still there:
|
||||
if (this._onPopupHiding) {
|
||||
this._onPopupHiding();
|
||||
}
|
||||
},
|
||||
|
||||
initItems: function CM_initItems() {
|
||||
|
@ -1703,5 +1711,76 @@ nsContextMenu.prototype = {
|
|||
selectedText]);
|
||||
menuItem.label = menuLabel;
|
||||
menuItem.accessKey = gNavigatorBundle.getString("contextMenuSearch.accesskey");
|
||||
}
|
||||
},
|
||||
|
||||
_getTelemetryClickInfo: function(aXulMenu) {
|
||||
this._onPopupHiding = () => {
|
||||
aXulMenu.ownerDocument.removeEventListener("command", activationHandler, true);
|
||||
aXulMenu.removeEventListener("popuphiding", this._onPopupHiding, true);
|
||||
delete this._onPopupHiding;
|
||||
|
||||
let eventKey = [
|
||||
this._telemetryPageContext,
|
||||
this._telemetryHadCustomItems ? "withcustom" : "withoutcustom"
|
||||
];
|
||||
let target = this._telemetryClickID || "close-without-interaction";
|
||||
BrowserUITelemetry.registerContextMenuInteraction(eventKey, target);
|
||||
};
|
||||
let activationHandler = (e) => {
|
||||
// Deal with command events being routed to command elements; figure out
|
||||
// what triggered the event (which will have the right e.target)
|
||||
if (e.sourceEvent) {
|
||||
e = e.sourceEvent;
|
||||
}
|
||||
// Target should be in the menu (this catches using shortcuts for items
|
||||
// not in the menu while the menu is up)
|
||||
if (!aXulMenu.contains(e.target)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if this is a page menu item:
|
||||
if (e.target.hasAttribute(PageMenu.GENERATEDITEMID_ATTR)) {
|
||||
this._telemetryClickID = "custom-page-item";
|
||||
} else {
|
||||
this._telemetryClickID = (e.target.id || "unknown").replace(/^context-/i, "");
|
||||
}
|
||||
};
|
||||
aXulMenu.ownerDocument.addEventListener("command", activationHandler, true);
|
||||
aXulMenu.addEventListener("popuphiding", this._onPopupHiding, true);
|
||||
},
|
||||
|
||||
_getTelemetryPageContextInfo: function() {
|
||||
if (this.isContentSelected) {
|
||||
return "selection";
|
||||
}
|
||||
if (this.onLink) {
|
||||
if (this.onImage || this.onCanvas) {
|
||||
return "image-link";
|
||||
}
|
||||
return "link";
|
||||
}
|
||||
if (this.onImage) {
|
||||
return "image"
|
||||
}
|
||||
if (this.onCanvas) {
|
||||
return "canvas";
|
||||
}
|
||||
if (this.onVideo || this.onAudio) {
|
||||
return "media";
|
||||
}
|
||||
if (this.onTextInput) {
|
||||
return "input";
|
||||
}
|
||||
if (this.onSocial) {
|
||||
return "social";
|
||||
}
|
||||
return "other";
|
||||
},
|
||||
|
||||
_checkTelemetryForMenu: function(aXulMenu) {
|
||||
this._telemetryClickID = null;
|
||||
this._telemetryPageContext = this._getTelemetryPageContextInfo();
|
||||
this._telemetryHadCustomItems = this.hasPageMenu;
|
||||
this._getTelemetryClickInfo(aXulMenu);
|
||||
},
|
||||
};
|
||||
|
|
|
@ -119,6 +119,9 @@ function* clickOnInspectMenuItem(node) {
|
|||
info("Triggering inspect action.");
|
||||
yield contextMenu.inspectNode();
|
||||
|
||||
// Clean up context menu:
|
||||
contextMenu.hiding();
|
||||
|
||||
info("Waiting for inspector to update.");
|
||||
yield getActiveInspector().once("inspector-updated");
|
||||
}
|
||||
|
|
|
@ -31,6 +31,10 @@ let test = asyncTest(function*() {
|
|||
let contentAreaContextMenu = document.getElementById("contentAreaContextMenu");
|
||||
let contextMenu = new nsContextMenu(contentAreaContextMenu);
|
||||
yield contextMenu.inspectNode();
|
||||
|
||||
// Clean up context menu:
|
||||
contextMenu.hiding();
|
||||
|
||||
yield onInspectorReady;
|
||||
|
||||
let target = TargetFactory.forTab(gBrowser.selectedTab);
|
||||
|
|
|
@ -170,6 +170,8 @@ this.BrowserUITelemetry = {
|
|||
init: function() {
|
||||
UITelemetry.addSimpleMeasureFunction("toolbars",
|
||||
this.getToolbarMeasures.bind(this));
|
||||
UITelemetry.addSimpleMeasureFunction("contextmenu",
|
||||
this.getContextMenuInfo.bind(this));
|
||||
// Ensure that UITour.jsm remains lazy-loaded, yet always registers its
|
||||
// simple measure function with UITelemetry.
|
||||
UITelemetry.addSimpleMeasureFunction("UITour",
|
||||
|
@ -220,11 +222,13 @@ this.BrowserUITelemetry = {
|
|||
*
|
||||
* @param aKeys the Array of keys to chain Objects together with.
|
||||
* @param aEndWith the value to assign to the last key.
|
||||
* @param aRoot the root object onto which we create/get the object chain
|
||||
* designated by aKeys.
|
||||
* @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;
|
||||
_ensureObjectChain: function(aKeys, aEndWith, aRoot) {
|
||||
let current = aRoot;
|
||||
let parent = null;
|
||||
aKeys.unshift(this._bucket);
|
||||
for (let [i, key] of Iterator(aKeys)) {
|
||||
|
@ -242,8 +246,8 @@ this.BrowserUITelemetry = {
|
|||
},
|
||||
|
||||
_countableEvents: {},
|
||||
_countEvent: function(aKeyArray) {
|
||||
let countObject = this._ensureObjectChain(aKeyArray, 0);
|
||||
_countEvent: function(aKeyArray, root=this._countableEvents) {
|
||||
let countObject = this._ensureObjectChain(aKeyArray, 0, root);
|
||||
let lastItemKey = aKeyArray[aKeyArray.length - 1];
|
||||
countObject[lastItemKey]++;
|
||||
},
|
||||
|
@ -587,6 +591,54 @@ this.BrowserUITelemetry = {
|
|||
}
|
||||
},
|
||||
|
||||
_contextMenuItemWhitelist: new Set([
|
||||
"close-without-interaction", // for closing the menu without clicking it.
|
||||
"custom-page-item", // The ID we use for page-provided items
|
||||
"unknown", // The bucket for stuff with no id.
|
||||
// Everything we know of so far (which will exclude add-on items):
|
||||
"navigation", "back", "forward", "reload", "stop", "bookmarkpage",
|
||||
"spell-no-suggestions", "spell-add-to-dictionary",
|
||||
"spell-undo-add-to-dictionary", "openlinkincurrent", "openlinkintab",
|
||||
"openlink", "openlinkprivate", "bookmarklink", "sharelink", "savelink",
|
||||
"marklinkMenu", "copyemail", "copylink", "media-play", "media-pause",
|
||||
"media-mute", "media-unmute", "media-playbackrate",
|
||||
"media-playbackrate-050x", "media-playbackrate-100x",
|
||||
"media-playbackrate-150x", "media-playbackrate-200x",
|
||||
"media-showcontrols", "media-hidecontrols", "video-showstats",
|
||||
"video-hidestats", "video-fullscreen", "leave-dom-fullscreen",
|
||||
"reloadimage", "viewimage", "viewvideo", "copyimage-contents", "copyimage",
|
||||
"copyvideourl", "copyaudiourl", "saveimage", "shareimage", "sendimage",
|
||||
"setDesktopBackground", "viewimageinfo", "viewimagedesc", "savevideo",
|
||||
"sharevideo", "saveaudio", "video-saveimage", "sendvideo", "sendaudio",
|
||||
"ctp-play", "ctp-hide", "sharepage", "savepage", "markpageMenu",
|
||||
"viewbgimage", "undo", "cut", "copy", "paste", "delete", "selectall",
|
||||
"keywordfield", "searchselect", "shareselect", "frame", "showonlythisframe",
|
||||
"openframeintab", "openframe", "reloadframe", "bookmarkframe", "saveframe",
|
||||
"printframe", "viewframesource", "viewframeinfo",
|
||||
"viewpartialsource-selection", "viewpartialsource-mathml",
|
||||
"viewsource", "viewinfo", "spell-check-enabled",
|
||||
"spell-add-dictionaries-main", "spell-dictionaries",
|
||||
"spell-dictionaries-menu", "spell-add-dictionaries",
|
||||
"bidi-text-direction-toggle", "bidi-page-direction-toggle", "inspect",
|
||||
]),
|
||||
|
||||
_contextMenuInteractions: {},
|
||||
|
||||
registerContextMenuInteraction: function(keys, itemID) {
|
||||
if (itemID) {
|
||||
if (!this._contextMenuItemWhitelist.has(itemID)) {
|
||||
itemID = "other-item";
|
||||
}
|
||||
keys.push(itemID);
|
||||
}
|
||||
|
||||
this._countEvent(keys, this._contextMenuInteractions);
|
||||
},
|
||||
|
||||
getContextMenuInfo: function() {
|
||||
return this._contextMenuInteractions;
|
||||
},
|
||||
|
||||
_bucket: BUCKET_DEFAULT,
|
||||
_bucketTimer: null,
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче