Bug 1456923 - can't switch to dev-tools add-on that is loaded temporarily r=pbro
authorMichael Ratcliffe <mratcliffe@mozilla.com>
Mon, 30 Apr 2018 15:57:39 +0100
changeset 472741 e6f674c99538d39323e0260693af26e4a9c969a5
parent 472740 b115bb8f62e31ceb53e9f5c30e0224da0a8c603b
child 472742 b860b811cbb04a993b1c12af0fe72477c79c9978
push id1728
push userjlund@mozilla.com
push dateMon, 18 Jun 2018 21:12:27 +0000
treeherdermozilla-release@c296fde26f5f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspbro
bugs1456923
milestone61.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 1456923 - can't switch to dev-tools add-on that is loaded temporarily r=pbro MozReview-Commit-ID: EOw2S34zqPa
browser/components/extensions/test/browser/browser_ext_devtools_panel.js
devtools/client/framework/toolbox.js
devtools/client/shared/telemetry.js
--- a/browser/components/extensions/test/browser/browser_ext_devtools_panel.js
+++ b/browser/components/extensions/test/browser/browser_ext_devtools_panel.js
@@ -237,17 +237,18 @@ add_task(async function test_devtools_pa
     // and accessible from the devtools_page when the panel.onShown
     // event has been received.
     window.TEST_PANEL_GLOBAL = "test_panel_global";
 
     browser.test.sendMessage("devtools_panel_inspectedWindow_tabId",
                              browser.devtools.inspectedWindow.tabId);
   }
 
-  const EXTENSION_ID = "@create-devtools-panel.test";
+  const longPrefix = (new Array(80)).fill("x").join("");
+  const EXTENSION_ID = `${longPrefix}@create-devtools-panel.test`;
 
   let extension = ExtensionTestUtils.loadExtension({
     useAddonManager: "temporary",
     manifest: {
       devtools_page: "devtools_page.html",
       applications: {
         gecko: {id: EXTENSION_ID},
       },
--- a/devtools/client/framework/toolbox.js
+++ b/devtools/client/framework/toolbox.js
@@ -1883,28 +1883,28 @@ Toolbox.prototype = {
     const panelName = this.getTelemetryPanelName(id);
     const prevPanelName = this.getTelemetryPanelName(this.currentToolId);
     const cold = !this.getPanel(id);
 
     this._telemetry.addEventProperties("devtools.main", "enter", panelName, null, {
       "host": this._hostType,
       "width": width,
       "start_state": reason,
-      "panel_name": id,
+      "panel_name": panelName,
       "cold": cold
     });
 
     // On first load this.currentToolId === undefined so we need to skip sending
     // a devtools.main.exit telemetry event.
     if (this.currentToolId) {
       this._telemetry.recordEvent("devtools.main", "exit", prevPanelName, null, {
         "host": this._hostType,
         "width": width,
         "panel_name": prevPanelName,
-        "next_panel": id,
+        "next_panel": panelName,
         "reason": reason
       });
     }
 
     const pending = ["host", "width", "start_state", "panel_name", "cold"];
     if (id === "webconsole") {
       pending.push("message_count");
 
@@ -2845,17 +2845,17 @@ Toolbox.prototype = {
     this._telemetry.toolClosed("toolbox");
     this._telemetry.recordEvent("devtools.main", "close", "tools", null, {
       host: host,
       width: width
     });
     this._telemetry.recordEvent("devtools.main", "exit", prevPanelName, null, {
       "host": host,
       "width": width,
-      "panel_name": this.currentToolId,
+      "panel_name": this.getTelemetryPanelName(this.currentToolId),
       "next_panel": "none",
       "reason": "toolbox_close"
     });
     this._telemetry.destroy();
 
     // Finish all outstanding tasks (which means finish destroying panels and
     // then destroying the host, successfully or not) before destroying the
     // target.
--- a/devtools/client/shared/telemetry.js
+++ b/devtools/client/shared/telemetry.js
@@ -6,16 +6,17 @@
  * This is the telemetry module to report metrics for tools.
  *
  * Comprehensive documentation is in docs/frontend/telemetry.md
  */
 
 "use strict";
 
 const Services = require("Services");
+const { getNthPathExcluding } = require("devtools/shared/platform/stack");
 const TOOLS_OPENED_PREF = "devtools.telemetry.tools.opened.version";
 
 // Object to be shared among all instances.
 const PENDING_EVENTS = new Map();
 const PENDING_EVENT_PROPERTIES = new Map();
 
 class Telemetry {
   constructor() {
@@ -290,17 +291,18 @@ class Telemetry {
       return;
     }
 
     try {
       let histogram = Services.telemetry.getHistogramById(histogramId);
       histogram.add(value);
     } catch (e) {
       dump(`Warning: An attempt was made to write to the ${histogramId} ` +
-           `histogram, which is not defined in Histograms.json\n`);
+           `histogram, which is not defined in Histograms.json\n` +
+           `CALLER: ${this.getCaller()}`);
     }
   }
 
   /**
    * Log a value to a scalar.
    *
    * @param  {String} scalarId
    *         Scalar in which the data is to be stored.
@@ -311,24 +313,26 @@ class Telemetry {
     if (!scalarId) {
       return;
     }
 
     try {
       if (isNaN(value) && typeof value !== "boolean") {
         dump(`Warning: An attempt was made to write a non-numeric and ` +
              `non-boolean value ${value} to the ${scalarId} scalar. Only ` +
-             `numeric and boolean values are allowed.\n`);
+             `numeric and boolean values are allowed.\n` +
+             `CALLER: ${this.getCaller()}`);
 
         return;
       }
       Services.telemetry.scalarSet(scalarId, value);
     } catch (e) {
       dump(`Warning: An attempt was made to write to the ${scalarId} ` +
-           `scalar, which is not defined in Scalars.yaml\n`);
+           `scalar, which is not defined in Scalars.yaml\n` +
+           `CALLER: ${this.getCaller()}`);
     }
   }
 
   /**
    * Log a value to a count scalar.
    *
    * @param  {String} scalarId
    *         Scalar in which the data is to be stored.
@@ -339,24 +343,26 @@ class Telemetry {
     if (!scalarId) {
       return;
     }
 
     try {
       if (isNaN(value)) {
         dump(`Warning: An attempt was made to write a non-numeric value ` +
              `${value} to the ${scalarId} scalar. Only numeric values are ` +
-             `allowed.\n`);
+             `allowed.\n` +
+             `CALLER: ${this.getCaller()}`);
 
         return;
       }
       Services.telemetry.scalarAdd(scalarId, value);
     } catch (e) {
       dump(`Warning: An attempt was made to write to the ${scalarId} ` +
-           `scalar, which is not defined in Scalars.yaml\n`);
+           `scalar, which is not defined in Scalars.yaml\n` +
+           `CALLER: ${this.getCaller()}`);
     }
   }
 
   /**
    * Log a value to a keyed count scalar.
    *
    * @param  {String} scalarId
    *         Scalar in which the data is to be stored.
@@ -369,24 +375,26 @@ class Telemetry {
     if (!scalarId) {
       return;
     }
 
     try {
       if (isNaN(value)) {
         dump(`Warning: An attempt was made to write a non-numeric value ` +
              `${value} to the ${scalarId} scalar. Only numeric values are ` +
-             `allowed.\n`);
+             `allowed.\n` +
+             `CALLER: ${this.getCaller()}`);
 
         return;
       }
       Services.telemetry.keyedScalarAdd(scalarId, key, value);
     } catch (e) {
       dump(`Warning: An attempt was made to write to the ${scalarId} ` +
-           `scalar, which is not defined in Scalars.yaml\n`);
+           `scalar, which is not defined in Scalars.yaml\n` +
+           `CALLER: ${this.getCaller()}`);
     }
   }
 
   /**
    * Log a value to a keyed histogram.
    *
    * @param  {String} histogramId
    *         Histogram in which the data is to be stored.
@@ -402,17 +410,18 @@ class Telemetry {
 
         if (typeof value === "undefined") {
           histogram.add(key);
         } else {
           histogram.add(key, value);
         }
       } catch (e) {
         dump(`Warning: An attempt was made to write to the ${histogramId} ` +
-             `histogram, which is not defined in Histograms.json\n`);
+             `histogram, which is not defined in Histograms.json\n` +
+             `CALLER: ${this.getCaller()}`);
       }
     }
   }
 
   /**
    * Log info about usage once per browser version. This allows us to discover
    * how many individual users are using our tools for each browser version.
    *
@@ -478,17 +487,18 @@ class Telemetry {
    *          "width"
    *        ]
    */
   preparePendingEvent(category, method, object, value, expected = []) {
     const sig = `${category},${method},${object},${value}`;
 
     if (expected.length === 0) {
       throw new Error(`preparePendingEvent() was called without any expected ` +
-                      `properties.`);
+                      `properties.\n` +
+                      `CALLER: ${this.getCaller()}`);
     }
 
     PENDING_EVENTS.set(sig, {
       extra: {},
       expected: new Set(expected)
     });
 
     const props = PENDING_EVENT_PROPERTIES.get(sig);
@@ -547,17 +557,18 @@ class Telemetry {
 
       if (expected.size === Object.keys(extra).length) {
         this._sendPendingEvent(category, method, object, value);
       }
     } else {
       // The property was not expected, warn and bail.
       throw new Error(`An attempt was made to add the unexpected property ` +
                       `"${pendingPropName}" to a telemetry event with the ` +
-                      `signature "${sig}"\n`);
+                      `signature "${sig}"\n` +
+                      `CALLER: ${this.getCaller()}`);
     }
   }
 
   /**
    * 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.
    *
@@ -613,17 +624,18 @@ class Telemetry {
       extra[name] = val;
 
       if (val.length > 80) {
         const sig = `${category},${method},${object},${value}`;
 
         throw new Error(`The property "${name}" was added to a telemetry ` +
                         `event with the signature ${sig} but it's value ` +
                         `"${val}" is longer than the maximum allowed length ` +
-                        `of 80 characters\n`);
+                        `of 80 characters\n` +
+                        `CALLER: ${this.getCaller()}`);
       }
     }
     Services.telemetry.recordEvent(category, method, object, value, extra);
   }
 
   /**
    * A private method that is not to be used externally. This method is used to
    * prepare a pending telemetry event for sending and then send it via
@@ -646,16 +658,25 @@ class Telemetry {
     const sig = `${category},${method},${object},${value}`;
     const { extra } = PENDING_EVENTS.get(sig);
 
     PENDING_EVENTS.delete(sig);
     PENDING_EVENT_PROPERTIES.delete(sig);
     this.recordEvent(category, method, object, value, extra);
   }
 
+  /**
+   * Displays the first caller and calling line outside of this file in the
+   * event of an error. This is the line that made the call that produced the
+   * error.
+   */
+  getCaller() {
+    return getNthPathExcluding(0, "/telemetry.js");
+  }
+
   destroy() {
     for (let histogramId of this._timers.keys()) {
       this.stopTimer(histogramId);
     }
   }
 }
 
 module.exports = Telemetry;