Bug 1441070 - Instrument toolbox panel navigation with event telemetry r=yulia

This is pretty simple... finding the best locations for the probes was the
struggle here.

In order to land these probes before the freeze the test for these events will
be created as part of bug 1456073.

MozReview-Commit-ID: 7hIfbD3wQ1I

--HG--
extra : rebase_source : 990e69e0816742fc306b9313bd113f2d71f00b72
This commit is contained in:
Michael Ratcliffe 2018-04-19 12:09:28 +01:00
parent f52e0b206b
commit b23784ee71
9 changed files with 101 additions and 15 deletions

View File

@ -27,7 +27,7 @@ function handleThreadState(toolbox, event, packet) {
packet.why.type === "breakpoint" ||
packet.why.type === "exception") {
toolbox.raise();
toolbox.selectTool("jsdebugger");
toolbox.selectTool("jsdebugger", packet.why.type);
}
} else if (event === "resumed") {
toolbox.unhighlightTool("jsdebugger");

View File

@ -61,10 +61,10 @@ class ToolboxTab extends Component {
"aria-pressed": currentToolId === id ? "true" : "false",
tabIndex: focusedButton === id ? "0" : "-1",
onFocus: () => focusButton(id),
onMouseDown: () => selectTool(id),
onMouseDown: () => selectTool(id, "tab_switch"),
onKeyDown: (evt) => {
if (evt.key === "Enter" || evt.key === " ") {
selectTool(id);
selectTool(id, "tab_switch");
}
},
},

View File

@ -204,7 +204,7 @@ class ToolboxTabs extends Component {
if (this.state.overflowedTabIds.includes(id)) {
menu.append(new MenuItem({
click: () => {
selectTool(id);
selectTool(id, "tab_switch");
},
id: "tools-chevron-menupopup-" + id,
label,

View File

@ -459,7 +459,7 @@ DevTools.prototype = {
}
if (toolId != null && toolbox.currentToolId != toolId) {
await toolbox.selectTool(toolId);
await toolbox.selectTool(toolId, "toolbox_show");
}
toolbox.raise();

View File

@ -160,6 +160,7 @@ function Toolbox(target, selectedTool, hostType, contentWindow, frameId) {
this._onNewSelectedNodeFront = this._onNewSelectedNodeFront.bind(this);
this.updatePickerButton = this.updatePickerButton.bind(this);
this.selectTool = this.selectTool.bind(this);
this._pingTelemetrySelectTool = this._pingTelemetrySelectTool.bind(this);
this.toggleSplitConsole = this.toggleSplitConsole.bind(this);
this._target.on("close", this.destroy);
@ -536,7 +537,7 @@ Toolbox.prototype = {
this.component.setCanRender();
}, {timeout: 16});
await this.selectTool(this._defaultToolId);
await this.selectTool(this._defaultToolId, "initial_panel");
// Wait until the original tool is selected so that the split
// console input will receive focus.
@ -844,9 +845,9 @@ Toolbox.prototype = {
// on the options panel.
if (this.currentToolId === "options" &&
gDevTools.getToolDefinition(this.lastUsedToolId)) {
this.selectTool(this.lastUsedToolId);
this.selectTool(this.lastUsedToolId, "toggle_settings_off");
} else {
this.selectTool("options");
this.selectTool("options", "toggle_settings_on");
}
};
this.shortcuts.on(L10N.getStr("toolbox.help.key"), selectOptions);
@ -1016,7 +1017,7 @@ Toolbox.prototype = {
// needed. See bug 371900
key.setAttribute("oncommand", "void(0);");
key.addEventListener("command", () => {
this.selectTool(toolId).then(() => this.fireCustomKey(toolId));
this.selectTool(toolId, "key_shortcut").then(() => this.fireCustomKey(toolId));
}, true);
doc.getElementById("toolbox-keyset").appendChild(key);
}
@ -1818,8 +1819,10 @@ Toolbox.prototype = {
*
* @param {string} id
* The id of the tool to switch to
* @param {string} reason
* Reason the tool was opened
*/
selectTool: function(id) {
selectTool: function(id, reason = "unknown") {
if (this.currentToolId == id) {
let panel = this._toolPanels.get(id);
if (panel) {
@ -1847,7 +1850,8 @@ Toolbox.prototype = {
if (this.currentToolId) {
this._telemetry.toolClosed(this.currentToolId);
}
this._telemetry.toolOpened(id);
this._pingTelemetrySelectTool(id, reason);
} else {
throw new Error("No tool found");
}
@ -1873,6 +1877,31 @@ Toolbox.prototype = {
});
},
_pingTelemetrySelectTool(id, reason) {
const width = Math.ceil(this.win.outerWidth / 50) * 50;
let panelName = id;
if (!/webconsole|inspector|jsdebugger|styleeditor|netmonitor|storage/.test(id)) {
panelName = "other";
}
this._telemetry.addEventProperties("devtools.main", "enter", panelName, null, {
"host": this._hostType,
"width": width,
"start_state": reason,
"panel_name": id,
"cold": !this.getPanel(id)
});
const pending = ["host", "width", "start_state", "panel_name", "cold"];
if (id === "webconsole") {
pending.push("message_count");
}
this._telemetry.preparePendingEvent(
"devtools.main", "enter", panelName, null, pending);
this._telemetry.toolOpened(id);
},
/**
* Focus a tool's panel by id
* @param {string} id
@ -1995,7 +2024,7 @@ Toolbox.prototype = {
let definition = index === -1 || index >= definitions.length - 1
? definitions[0]
: definitions[index + 1];
return this.selectTool(definition.id);
return this.selectTool(definition.id, "select_next_key");
},
/**
@ -2007,7 +2036,7 @@ Toolbox.prototype = {
let definition = index === -1 || index < 1
? definitions[definitions.length - 1]
: definitions[index - 1];
return this.selectTool(definition.id);
return this.selectTool(definition.id, "select_prev_key");
},
/**
@ -2470,7 +2499,7 @@ Toolbox.prototype = {
toolNameToSelect = previousTool.id;
}
if (toolNameToSelect) {
this.selectTool(toolNameToSelect);
this.selectTool(toolNameToSelect, "tool_unloaded");
}
}
@ -2581,7 +2610,7 @@ Toolbox.prototype = {
const nodeFound = await inspector.inspectNodeActor(objectActor.actor,
inspectFromAnnotation);
if (nodeFound) {
await this.selectTool("inspector");
await this.selectTool("inspector", "inspect_dom");
}
} else if (objectActor.type !== "null" &&
objectActor.type !== "undefined") {

View File

@ -556,6 +556,33 @@ class Telemetry {
}
}
/**
* Adds expected properties for either a current or future pending event.
* This means that if preparePendingEvent() is called before or after sending
* the event properties they will automatically added to the event.
*
* @param {String} category
* The telemetry event category (a group name for events and helps to
* avoid name conflicts) e.g. "devtools.main"
* @param {String} method
* The telemetry event method (describes the type of event that
* occurred e.g. "open")
* @param {String} object
* The telemetry event object name (the name of the object the event
* occurred on) e.g. "tools" or "setting"
* @param {String|null} value
* The telemetry event value (a user defined value, providing context
* for the event) e.g. "console"
* @param {String} pendingObject
* An object containing key, value pairs that should be added to the
* event as properties.
*/
addEventProperties(category, method, object, value, pendingObject) {
for (let [key, val] of Object.entries(pendingObject)) {
this.addEventProperty(category, method, object, value, key, val);
}
}
/**
* Send a telemetry event.
*

View File

@ -279,6 +279,15 @@ this._telemetry.addEventProperty(
"devtools.main", "open", "tools", null, "splitconsole", false);
this._telemetry.addEventProperty(
"devtools.main", "open", "tools", null, "width", 1024);
// You can also add properties in batches using e.g.:
this._telemetry.addEventProperties("devtools.main", "open", "tools", null, {
"first_panel": "inspector",
"host": "bottom",
"splitconsole": false,
"width": 1024
});
```
Notes:

View File

@ -30,6 +30,7 @@ loader.lazyRequireGetter(this, "CONSOLE_WORKER_IDS", "devtools/server/actors/web
loader.lazyRequireGetter(this, "WebConsoleUtils", "devtools/server/actors/webconsole/utils", true);
loader.lazyRequireGetter(this, "EnvironmentActor", "devtools/server/actors/environment", true);
loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");
loader.lazyRequireGetter(this, "Telemetry", "devtools/client/shared/telemetry");
// Overwrite implemented listeners for workers so that we don't attempt
// to load an unsupported module.
@ -75,6 +76,8 @@ function WebConsoleActor(connection, parentActor) {
this._listeners = new Set();
this._lastConsoleInputEvaluation = undefined;
this._telemetry = new Telemetry();
this.objectGrip = this.objectGrip.bind(this);
this._onWillNavigate = this._onWillNavigate.bind(this);
this._onChangedToplevelDocument = this._onChangedToplevelDocument.bind(this);
@ -862,6 +865,9 @@ WebConsoleActor.prototype =
}
}
this._telemetry.addEventProperty(
"devtools.main", "enter", "webconsole", null, "message_count", messages.length);
return {
from: this.actorID,
messages: messages,

View File

@ -188,3 +188,18 @@ devtools.main:
extra_keys:
host: "Toolbox host (positioning): bottom, side, window or other."
width: Toolbox width rounded up to the nearest 50px.
enter:
objects: ["webconsole", "inspector", "jsdebugger", "styleeditor", "netmonitor", "storage", "other"]
bug_numbers: [1441070]
notification_emails: ["dev-developer-tools@lists.mozilla.org", "hkirschner@mozilla.com"]
record_in_processes: ["main"]
description: User opens a tool in the devtools toolbox.
release_channel_collection: opt-out
expiry_version: never
extra_keys:
host: "Toolbox host (positioning): bottom, side, window or other."
width: Toolbox width rounded up to the nearest 50px.
message_count: The number of cached console messages.
start_state: debuggerStatement, breakpoint, exception, tab_switch, toolbox_show, initial_panel, toggle_settings_off, toggle_settings_on, key_shortcut, select_next_key, select_prev_key, tool_unloaded, inspect_dom, unknown etc.
panel_name: The name of the panel opened, webconsole, inspector, jsdebugger, styleeditor, netmonitor, storage or other
cold: Is this the first time the current panel has been opened in this toolbox?