Bug 1219854 - Add more robust source name parsing for displaying allocation stack tree items; r=jsantell, a=blanket-sylvestre, l10n=blanket-sylvestre CLOSED TREE
authorNick Fitzgerald <fitzgen@gmail.com>
Fri, 30 Oct 2015 13:31:41 -0700
changeset 305345 a97e4fd8931f9a420d7d1248a02bbd837d7a6c65
parent 305344 a59f94241d1ff26aee38b296de6820e1e018b6f5
child 305346 7dc7660b754f59601bcd682c915814619a2c7a41
push id1001
push userraliiev@mozilla.com
push dateMon, 18 Jan 2016 19:06:03 +0000
treeherdermozilla-release@8b89261f3ac4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjsantell, blanket-sylvestre
bugs1219854
milestone44.0a2
Bug 1219854 - Add more robust source name parsing for displaying allocation stack tree items; r=jsantell, a=blanket-sylvestre, l10n=blanket-sylvestre CLOSED TREE
browser/locales/en-US/chrome/browser/devtools/memory.properties
devtools/client/memory/components/frame.js
devtools/client/memory/test/unit/test_utils.js
devtools/client/memory/utils.js
--- a/browser/locales/en-US/chrome/browser/devtools/memory.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/memory.properties
@@ -112,8 +112,13 @@ heapview.field.count=Count
 # LOCALIZATION NOTE (heapview.field.totalbytes): The name of the column in the heap view for total bytes.
 heapview.field.totalbytes=Total Bytes
 
 # LOCALIZATION NOTE (heapview.field.totalcount): The name of the column in the heap view for total count.
 heapview.field.totalcount=Total Count
 
 # LOCALIZATION NOTE (heapview.field.name): The name of the column in the heap view for name.
 heapview.field.name=Name
+
+# LOCALIZATION NOTE (unknownSource): When we do not know the source filename of
+# a frame in the allocation stack breakdown tree view, we use this string
+# instead.
+unknownSource=(unknown)
--- a/devtools/client/memory/components/frame.js
+++ b/devtools/client/memory/components/frame.js
@@ -1,42 +1,40 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * 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/. */
 
 const { DOM: dom, createClass, PropTypes } = require("devtools/client/shared/vendor/react");
-const { L10N } = require("../utils");
-const { URL } = require("sdk/url");
+const { L10N, parseSource } = require("../utils");
 
 const Frame = module.exports = createClass({
   displayName: "frame-view",
 
   propTypes: {
     frame: PropTypes.object.isRequired,
     toolbox: PropTypes.object.isRequired,
   },
 
   render() {
     let { toolbox, frame } = this.props;
+    const { short, long, host } = parseSource(frame);
 
-    let url = new URL(frame.source);
-    let spec = url.toString();
     let func = frame.functionDisplayName || "";
-    let tooltip = `${func} (${spec}:${frame.line}:${frame.column})`;
-    let viewTooltip = L10N.getFormatStr("viewsourceindebugger", `${spec}:${frame.line}:${frame.column}`);
-    let onClick = () => toolbox.viewSourceInDebugger(spec, frame.line);
+    let tooltip = `${func} (${long}:${frame.line}:${frame.column})`;
+    let viewTooltip = L10N.getFormatStr("viewsourceindebugger", `${long}:${frame.line}:${frame.column}`);
+    let onClick = () => toolbox.viewSourceInDebugger(long, frame.line);
 
     let fields = [
       dom.span({ className: "frame-link-function-display-name" }, func),
-      dom.a({ className: "frame-link-filename", onClick, title: viewTooltip }, url.fileName),
+      dom.a({ className: "frame-link-filename", onClick, title: viewTooltip }, short),
       dom.span({ className: "frame-link-colon" }, ":"),
       dom.span({ className: "frame-link-line" }, frame.line),
       dom.span({ className: "frame-link-colon" }, ":"),
       dom.span({ className: "frame-link-column" }, frame.column)
     ];
 
-    if (url.scheme === "http" || url.scheme === "https" || url.scheme === "ftp") {
-      fields.push(dom.span({ className: "frame-link-host" }, url.host));
+    if (host) {
+      fields.push(dom.span({ className: "frame-link-host" }, host));
     }
 
     return dom.span({ className: "frame-link", title: tooltip }, ...fields);
   }
 });
--- a/devtools/client/memory/test/unit/test_utils.js
+++ b/devtools/client/memory/test/unit/test_utils.js
@@ -44,8 +44,27 @@ add_task(function *() {
     "utils.breakdownNameToSpec() works for presets");
 
   let custom = { by: "internalType", then: { by: "count", bytes: true }};
   Preferences.set("devtools.memory.custom-breakdowns", JSON.stringify({ "My Breakdown": custom }));
 
   ok(utils.breakdownEquals(utils.getCustomBreakdowns()["My Breakdown"], custom),
     "utils.getCustomBreakdowns() returns custom breakdowns");
 });
+
+// Test `utils.parseSource`.
+add_task(function* () {
+  const url = "http://example.com/foo/bar/baz.js";
+  let results = utils.parseSource(url);
+  equal(results.short, "baz.js");
+  equal(results.long, url);
+  equal(results.host, "example.com");
+
+  results = utils.parseSource("self-hosted");
+  equal(results.short, "self-hosted");
+  equal(results.long, "self-hosted");
+  equal(results.host, undefined);
+
+  results = utils.parseSource("");
+  equal(typeof results.short, "string");
+  equal(typeof results.long, "string");
+  equal(results.host, undefined);
+});
--- a/devtools/client/memory/utils.js
+++ b/devtools/client/memory/utils.js
@@ -1,16 +1,19 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * 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/. */
 
 const { Cu } = require("chrome");
+
 Cu.import("resource://devtools/client/shared/widgets/ViewHelpers.jsm");
 const STRINGS_URI = "chrome://browser/locale/devtools/memory.properties"
 const L10N = exports.L10N = new ViewHelpers.L10N(STRINGS_URI);
+
+const { URL } = require("sdk/url");
 const { assert } = require("devtools/shared/DevToolsUtils");
 const { Preferences } = require("resource://gre/modules/Preferences.jsm");
 const CUSTOM_BREAKDOWN_PREF = "devtools.memory.custom-breakdowns";
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
 const { snapshotState: states, breakdowns } = require("./constants");
 
 /**
  * Takes a snapshot object and returns the
@@ -263,8 +266,51 @@ exports.getSnapshotTotals = function (sn
     }
   }
 
   return {
     bytes: bytes || 0,
     count: count || 0,
   };
 };
+
+/**
+ * Parse a source into a short and long name as well as a host name.
+ *
+ * @param {String} source
+ *        The source to parse.
+ *
+ * @returns {Object}
+ *          An object with the following properties:
+ *            - {String} short: A short name for the source.
+ *            - {String} long: The full, long name for the source.
+ *            - {String?} host: If available, the host name for the source.
+ */
+exports.parseSource = function (source) {
+  const sourceStr = source ? String(source) : "";
+
+  let short;
+  let long;
+  let host;
+
+  try {
+    const url = new URL(sourceStr);
+    short = url.fileName;
+    host = url.host;
+    long = url.toString();
+  } catch (e) {
+    // Malformed URI.
+    long = sourceStr;
+    short = sourceStr.slice(0, 100);
+  }
+
+  if (!short) {
+    // Last ditch effort.
+
+    if (!long) {
+      long = L10N.getStr("unknownSource");
+    }
+
+    short = long.slice(0, 100);
+  }
+
+  return { short, long, host };
+};