Bug 1463087 - Instrument inspection of JS execution in the Web Console with event telemetry r=yulia
authorMichael Ratcliffe <mratcliffe@mozilla.com>
Thu, 24 May 2018 16:22:47 +0100
changeset 474870 b2cbe24032838b09781adeee59427c561c60b367
parent 474869 375638c8a4332004266e23ef9c882d057d5d7d81
child 474871 eccc5f4ea53eef6622349846b92fac0d396372da
push id9374
push userjlund@mozilla.com
push dateMon, 18 Jun 2018 21:43:20 +0000
treeherdermozilla-beta@160e085dfb0b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersyulia
bugs1463087
milestone62.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 1463087 - Instrument inspection of JS execution in the Web Console with event telemetry r=yulia MozReview-Commit-ID: 2lri9Kzso7Q
devtools/client/webconsole/components/JSTerm.js
devtools/client/webconsole/test/mochitest/browser_jsterm_multiline.js
toolkit/components/telemetry/Events.yaml
--- a/devtools/client/webconsole/components/JSTerm.js
+++ b/devtools/client/webconsole/components/JSTerm.js
@@ -13,16 +13,17 @@ loader.lazyServiceGetter(this, "clipboar
 loader.lazyRequireGetter(this, "defer", "devtools/shared/defer");
 loader.lazyRequireGetter(this, "Debugger", "Debugger");
 loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");
 loader.lazyRequireGetter(this, "AutocompletePopup", "devtools/client/shared/autocomplete-popup");
 loader.lazyRequireGetter(this, "PropTypes", "devtools/client/shared/vendor/react-prop-types");
 loader.lazyRequireGetter(this, "gDevTools", "devtools/client/framework/devtools", true);
 loader.lazyRequireGetter(this, "KeyCodes", "devtools/client/shared/keycodes", true);
 loader.lazyRequireGetter(this, "Editor", "devtools/client/sourceeditor/editor");
+loader.lazyRequireGetter(this, "Telemetry", "devtools/client/shared/telemetry");
 
 const l10n = require("devtools/client/webconsole/webconsole-l10n");
 
 const HELP_URL = "https://developer.mozilla.org/docs/Tools/Web_Console/Helpers";
 const PREF_AUTO_MULTILINE = "devtools.webconsole.autoMultiline";
 
 function gSequenceId() {
   return gSequenceId.n++;
@@ -151,16 +152,18 @@ class JSTerm extends Component {
     this.completeNode = null;
 
     this.COMPLETE_FORWARD = 0;
     this.COMPLETE_BACKWARD = 1;
     this.COMPLETE_HINT_ONLY = 2;
     this.COMPLETE_PAGEUP = 3;
     this.COMPLETE_PAGEDOWN = 4;
 
+    this._telemetry = new Telemetry();
+
     EventEmitter.decorate(this);
     hud.jsterm = this;
   }
 
   componentDidMount() {
     let autocompleteOptions = {
       onSelect: this.onAutocompleteSelect.bind(this),
       onClick: this.acceptProposedCompletion.bind(this),
@@ -433,16 +436,21 @@ class JSTerm extends Component {
     let evalOptions = {
       bindObjectActor: options.bindObjectActor,
       frameActor: frameActor,
       selectedNodeActor: options.selectedNodeActor,
       selectedObjectActor: options.selectedObjectActor,
     };
 
     this.webConsoleClient.evaluateJSAsync(str, onResult, evalOptions);
+
+    this._telemetry.recordEvent("devtools.main", "execute_js", "webconsole", null, {
+      lines: str.split(/\n/).length
+    });
+
     return deferred.promise;
   }
 
   /**
    * Copy the object/variable by invoking the server
    * which invokes the `copy(variable)` command and makes it
    * available in the clipboard
    * @param evalString - string which has the evaluation string to be copied
--- a/devtools/client/webconsole/test/mochitest/browser_jsterm_multiline.js
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_multiline.js
@@ -6,16 +6,17 @@
 // Tests that the console waits for more input instead of evaluating
 // when valid, but incomplete, statements are present upon pressing enter
 // -or- when the user ends a line with shift + enter.
 
 "use strict";
 
 const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
                  "test/mochitest/test-console.html";
+const OPTOUT = Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTOUT;
 
 let SHOULD_ENTER_MULTILINE = [
   {input: "function foo() {" },
   {input: "var a = 1," },
   {input: "var a = 1;", shiftKey: true },
   {input: "function foo() { }", shiftKey: true },
   {input: "function" },
   {input: "(x) =>" },
@@ -36,17 +37,57 @@ let SHOULD_EXECUTE = [
   {input: "99 + 3" },
   {input: "1, 2, 3" },
   // errors
   {input: "function f(x) { let y = 1, }" },
   {input: "function f(x=,) {" },
   {input: "{2,}" },
 ];
 
+const SINGLE_LINE_DATA = {
+  timestamp: null,
+  category: "devtools.main",
+  method: "execute_js",
+  object: "webconsole",
+  value: null,
+  extra: {
+    lines: "1"
+  }
+};
+
+const DATA = [
+  SINGLE_LINE_DATA,
+  SINGLE_LINE_DATA,
+  SINGLE_LINE_DATA,
+  SINGLE_LINE_DATA,
+  SINGLE_LINE_DATA,
+  SINGLE_LINE_DATA,
+  SINGLE_LINE_DATA,
+  SINGLE_LINE_DATA,
+  SINGLE_LINE_DATA,
+  {
+    timestamp: null,
+    category: "devtools.main",
+    method: "execute_js",
+    object: "webconsole",
+    value: null,
+    extra: {
+      lines: "3"
+    }
+  }
+];
+
 add_task(async function() {
+  // Let's reset the counts.
+  Services.telemetry.clearEvents();
+
+  // Ensure no events have been logged
+  const snapshot = Services.telemetry.snapshotEvents(OPTOUT, true);
+  ok(!snapshot.parent, "No events have been logged for the main process");
+
   let hud = await openNewTabAndConsole(TEST_URI);
   let { inputNode } = hud.jsterm;
 
   for (let {input, shiftKey} of SHOULD_ENTER_MULTILINE) {
     hud.jsterm.setInputValue(input);
     EventUtils.synthesizeKey("VK_RETURN", { shiftKey });
 
     let inputValue = hud.jsterm.getInputValue();
@@ -63,9 +104,36 @@ add_task(async function() {
 
     await waitFor(() => !hud.jsterm.getInputValue());
 
     let inputValue = hud.jsterm.getInputValue();
     is(inputNode.selectionStart, 0, "selection starts/ends at 0");
     is(inputNode.selectionEnd, 0, "selection starts/ends at 0");
     is(inputValue, "", "Input value is cleared");
   }
+
+  await hud.jsterm.execute("document.\nlocation.\nhref");
+
+  checkEventTelemetry();
 });
+
+function checkEventTelemetry() {
+  const snapshot = Services.telemetry.snapshotEvents(OPTOUT, true);
+  const events = snapshot.parent.filter(event => event[1] === "devtools.main" &&
+                                                  event[2] === "execute_js" &&
+                                                  event[3] === "webconsole" &&
+                                                  event[4] === null
+  );
+
+  for (let i in DATA) {
+    const [ timestamp, category, method, object, value, extra ] = events[i];
+    const expected = DATA[i];
+
+    // ignore timestamp
+    ok(timestamp > 0, "timestamp is greater than 0");
+    is(category, expected.category, "category is correct");
+    is(method, expected.method, "method is correct");
+    is(object, expected.object, "object is correct");
+    is(value, expected.value, "value is correct");
+
+    is(extra.lines, expected.extra.lines, "lines is correct");
+  }
+}
--- a/toolkit/components/telemetry/Events.yaml
+++ b/toolkit/components/telemetry/Events.yaml
@@ -296,8 +296,18 @@ devtools.main:
     notification_emails: ["dev-developer-tools@lists.mozilla.org", "hkirschner@mozilla.com"]
     record_in_processes: ["main"]
     description: User has switched sidepanel tabs.
     release_channel_collection: opt-out
     expiry_version: never
     extra_keys:
       oldpanel: The panel the user is switching from
       newpanel: The panel the user is switching to
+  execute_js:
+    objects: ["webconsole"]
+    bug_numbers: [1463083]
+    notification_emails: ["dev-developer-tools@lists.mozilla.org", "hkirschner@mozilla.com"]
+    record_in_processes: ["main"]
+    description: User has executed some JS in the Web Console.
+    release_channel_collection: opt-out
+    expiry_version: never
+    extra_keys:
+      lines: The number of lines contained in the command.