Bug 1456923 - can't switch to dev-tools add-on that is loaded temporarily r?pbro draft
authorMichael Ratcliffe <mratcliffe@mozilla.com>
Mon, 30 Apr 2018 15:57:39 +0100
changeset 789753 1d5c6d4c4e0a66170845c5cf945d89b428880a3e
parent 789727 63519bfd42ee379f597c0357af2e712ec3cd9f50
push id108336
push userbmo:mratcliffe@mozilla.com
push dateMon, 30 Apr 2018 15:00:25 +0000
reviewerspbro
bugs1456923
milestone61.0a1
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;