Bug 1453946 - Update Debugger Frontend v38. r=jdescottes
authorJason Laster <jason.laster.11@gmail.com>
Sun, 15 Apr 2018 00:03:03 +0200
changeset 413443 244ecba50f11b3cf54288242c80262b5342ba665
parent 413442 c80bf1d63a2afbf5c6a8921f17fcf00bc513d6dd
child 413444 f291b07fa1f869fd3f9af9f5fe940d47b842edcf
push id33850
push userapavel@mozilla.com
push dateMon, 16 Apr 2018 09:53:48 +0000
treeherdermozilla-central@6276ec7ebbf3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjdescottes
bugs1453946
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 1453946 - Update Debugger Frontend v38. r=jdescottes MozReview-Commit-ID: 9Q0fu93y15i
devtools/client/debugger/new/README.mozilla
devtools/client/debugger/new/debugger.css
devtools/client/debugger/new/debugger.js
devtools/client/debugger/new/test/mochitest/browser_dbg-asm.js
devtools/client/debugger/new/test/mochitest/browser_dbg-sources.js
devtools/client/themes/images/debugger/typescript.svg
--- a/devtools/client/debugger/new/README.mozilla
+++ b/devtools/client/debugger/new/README.mozilla
@@ -1,13 +1,13 @@
 This is the debugger.html project output.
 See https://github.com/devtools-html/debugger.html
 
-Version 37.0
+Version 38.0
 
-Comparison: https://github.com/devtools-html/debugger.html/compare/release-36...release-37
+Comparison: https://github.com/devtools-html/debugger.html/compare/release-37...release-38
 
 Packages:
 - babel-plugin-transform-es2015-modules-commonjs @6.26.0
 - babel-preset-react @6.24.1
 - react @16.2.0
 - react-dom @16.2.0
 - webpack @3.11.0
--- a/devtools/client/debugger/new/debugger.css
+++ b/devtools/client/debugger/new/debugger.css
@@ -1359,18 +1359,19 @@ html[dir="rtl"] .tree-node img.arrow {
 
 .sources-list .managed-tree .tree .node img.blackBox {
   mask: url("chrome://devtools/skin/images/debugger/blackBox.svg") no-repeat;
   mask-size: 100%;
   background-color: var(--theme-highlight-blue);
   width: 13px;
   height: 13px;
   display: inline-block;
-  margin-inline-end: 5px;
-  margin-bottom: -2px;
+  margin-inline-end: 6px;
+  margin-inline-start: 1px;
+  margin-top: 2px;
 }
 
 .sources-list .managed-tree .tree .node.focused img {
   background-color: white;
 }
 
 .theme-dark .sources-list .managed-tree .tree .node:not(.focused) img.blackBox {
   background-color: var(--theme-comment);
@@ -1943,17 +1944,16 @@ html .toggle-button.end.vertical svg {
   --null-color: var(--theme-comment);
   --object-color: var(--theme-highlight-blue);
   --caption-color: var(--theme-highlight-blue);
   --location-color: var(--theme-comment);
   --source-link-color: var(--theme-highlight-blue);
   --node-color: var(--theme-highlight-purple);
   --reference-color: var(--theme-highlight-blue);
   --comment-node-color: var(--theme-comment);
-  --stack-function-color: var(--theme-highlight-red);
 }
 
 .theme-firebug {
   --number-color: #000088;
   --string-color: #FF0000;
   --null-color: #787878;
   --object-color: DarkGreen;
   --caption-color: #444444;
@@ -2005,37 +2005,60 @@ html .toggle-button.end.vertical svg {
   cursor: pointer;
 }
 
 .objectBox-string a:hover {
   text-decoration: underline;
 }
 
 .objectBox-function,
-.objectBox-stackTrace,
 .objectBox-profile {
   color: var(--object-color);
 }
 
+.objectBox-stackTrace {
+  color: var(--error-color);
+}
+
 .objectBox-stackTrace-grid {
   display: inline-grid;
   grid-template-columns: auto auto;
   margin-top: 3px;
 }
 
 .objectBox-stackTrace-fn::before {
   content: "\3BB"; /* The "lambda" symbol */
-  color: var(--theme-body-color);
   display: inline-block;
   margin: 0 0.3em;
 }
 
 .objectBox-stackTrace-fn {
-  color: var(--stack-function-color);
+  color: var(--console-output-color);
   padding-inline-start: 17px;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  margin-inline-end: 5px;
+}
+
+.objectBox-stackTrace-location {
+  color: var(--frame-link-source, currentColor);
+  direction: rtl;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  text-align: end;
+}
+
+.objectBox-stackTrace-location:hover {
+  text-decoration: underline;
+}
+
+.objectBox-stackTrace-location {
+  cursor: pointer;
 }
 
 .objectBox-Location,
 .location {
   color: var(--location-color);
 }
 
 .objectBox-null,
@@ -2954,16 +2977,20 @@ html[dir="rtl"] .breakpoints-list .break
 .CodeMirror.cm-s-mozilla-breakpoint .CodeMirror-code,
 .CodeMirror.cm-s-mozilla-breakpoint .CodeMirror-scroll {
   cursor: default;
 }
 /* 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/>. */
 
+.watch-expressions-pane .plus {
+  margin-top: -2px;
+}
+
 .expression-input-form {
   width: 100%;
 }
 
 .input-expression {
   width: 100%;
   margin: 0;
   border: 1px;
--- a/devtools/client/debugger/new/debugger.js
+++ b/devtools/client/debugger/new/debugger.js
@@ -2321,18 +2321,17 @@ var _path = __webpack_require__(1393);
 var _url = __webpack_require__(334);
 
 var _sourcesTree = __webpack_require__(1442);
 
 const sourceTypes = exports.sourceTypes = {
   coffee: "coffeescript",
   js: "javascript",
   jsx: "react",
-  ts: "typescript",
-  css: "css"
+  ts: "typescript"
 };
 
 /**
  * Trims the query part or reference identifier of a url string, if necessary.
  *
  * @memberof utils/source
  * @static
  */
@@ -7068,28 +7067,19 @@ class ManagedTree extends _react.Compone
   }
 
   highlightItem(highlightItems) {
     const { expanded } = this.state;
     // This file is visible, so we highlight it.
     if (expanded.has(this.props.getPath(highlightItems[0]))) {
       this.focusItem(highlightItems[0]);
     } else {
-      // Look at folders starting from the top-level and expand all the items
-      // which lie in the path of the item to be highlighted
-      highlightItems.reverse();
-      let index = highlightItems.findIndex(item => !expanded.has(this.props.getPath(item)));
-
-      if (this.props.autoExpandOnHighlight) {
-        while (index < highlightItems.length - 1) {
-          this.setExpanded(highlightItems[index], true, false);
-          index++;
-        }
-      }
-
+      // Look at folders starting from the top-level until finds a
+      // closed folder and highlights this folder
+      const index = highlightItems.reverse().findIndex(item => !expanded.has(this.props.getPath(item)));
       this.focusItem(highlightItems[index]);
     }
   }
 
   render() {
     const { expanded, focusedItem } = this.state;
     return _react2.default.createElement(
       "div",
@@ -10213,17 +10203,17 @@ var _initialiseProps = function () {
       className: (0, _classnames2.default)("result-item", {
         selected: index === selected
       })
     };
 
     return _react2.default.createElement(
       "li",
       props,
-      _react2.default.createElement(
+      item.icon && _react2.default.createElement(
         "div",
         null,
         _react2.default.createElement("img", { className: item.icon })
       ),
       _react2.default.createElement(
         "div",
         { id: `${item.id}-title`, className: "title" },
         item.title
@@ -10594,17 +10584,18 @@ function supportsObject(object, noGrip =
 
   return getGripType(object, noGrip) == "string";
 }
 
 // Exports from this module
 
 module.exports = {
   rep: wrapRender(StringRep),
-  supportsObject
+  supportsObject,
+  isLongString
 };
 
 /***/ }),
 
 /***/ 1448:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
@@ -16989,17 +16980,16 @@ class SourcesTree extends _react.Compone
 
     if (isEmpty && !isCustomRoot) {
       return this.renderEmptyElement(L10N.getStr("sources.noSourcesAvailable"));
     }
 
     const treeProps = {
       autoExpandAll: false,
       autoExpandDepth: expanded ? 0 : 1,
-      autoExpandOnHighlight: true,
       expanded,
       getChildren: item => (0, _sourcesTree.nodeHasChildren)(item) ? item.contents : [],
       getParent: item => parentMap.get(item),
       getPath: this.getPath,
       getRoots: roots,
       highlightItems,
       itemHeight: 21,
       key: isEmpty ? "empty" : "full",
@@ -18649,17 +18639,20 @@ class Popup extends _react.Component {
 
   async componentWillMount() {
     const {
       value,
       expression,
       setPopupObjectProperties,
       popupObjectProperties
     } = this.props;
-    const root = createNode(null, expression, expression, { value });
+    const root = createNode({
+      name: expression,
+      contents: { value }
+    });
 
     if (!nodeIsPrimitive(root) && value && value.actor && !popupObjectProperties[value.actor]) {
       const onLoadItemProperties = loadItemProperties(root, _firefox.createObjectClient);
       if (onLoadItemProperties !== null) {
         const properties = await onLoadItemProperties;
         setPopupObjectProperties(value, properties);
       }
     }
@@ -18763,17 +18756,17 @@ class Popup extends _react.Component {
       _react2.default.createElement(_Svg2.default, { name: "immutable", className: "immutable-logo" }),
       _react2.default.createElement(
         "h3",
         null,
         immutableHeader
       )
     );
 
-    const roots = [createNode(null, "entries", "entries", { value: immutable.entries })];
+    const roots = [createNode({ name: "entries", contents: { value: immutable.entries } })];
 
     return _react2.default.createElement(
       "div",
       { className: "preview-popup" },
       header,
       this.renderObjectInspector(roots)
     );
   }
@@ -20470,20 +20463,22 @@ module.exports = {
 const PropTypes = __webpack_require__(20);
 // Utils
 const {
   getGripType,
   isGrip,
   wrapRender
 } = __webpack_require__(1353);
 const { cleanFunctionName } = __webpack_require__(1573);
+const { isLongString } = __webpack_require__(1447);
 const { MODE } = __webpack_require__(1357);
 
 const dom = __webpack_require__(1758);
 const { span } = dom;
+const IGNORED_SOURCE_URLS = ["debugger eval code"];
 
 /**
  * Renders Error objects.
  */
 ErrorRep.propTypes = {
   object: PropTypes.object.isRequired,
   // @TODO Change this to Object.values once it's supported in Node's version of V8
   mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key]))
@@ -20513,17 +20508,17 @@ function ErrorRep(props) {
 
   if (props.mode === MODE.TINY) {
     content.push(name);
   } else {
     content.push(`${name}: "${preview.message}"`);
   }
 
   if (preview.stack && props.mode !== MODE.TINY) {
-    content.push("\n", getStacktraceElements(preview));
+    content.push("\n", getStacktraceElements(props, preview));
   }
 
   return span({
     "data-link-actor-id": object.actor,
     className: "objectBox-stackTrace"
   }, content);
 }
 
@@ -20538,52 +20533,86 @@ function ErrorRep(props) {
  *
  * Into a column layout:
  *
  * semicolon  (<anonymous>:8:10)
  * jkl        (<anonymous>:5:10)
  * asdf       (<anonymous>:2:10)
  *            (<anonymous>:11:1)
  */
-function getStacktraceElements(preview) {
+function getStacktraceElements(props, preview) {
   const stack = [];
-  preview.stack.split("\n").forEach((line, index) => {
-    if (!line) {
+  if (!preview.stack) {
+    return stack;
+  }
+
+  const isStacktraceALongString = isLongString(preview.stack);
+  const stackString = isStacktraceALongString ? preview.stack.initial : preview.stack;
+
+  stackString.split("\n").forEach((frame, index) => {
+    if (!frame) {
       // Skip any blank lines
       return;
     }
 
     let functionName;
     let location;
 
     // Given the input: "functionName@scriptLocation:2:100"
     // Result:
     // ["functionName@scriptLocation:2:100", "functionName", "scriptLocation:2:100"]
-    const result = line.match(/^(.*)@(.*)$/);
+    const result = frame.match(/^(.*)@(.*)$/);
     if (result && result.length === 3) {
       functionName = result[1];
 
       // If the resource was loaded by base-loader.js, the location looks like:
       // resource://devtools/shared/base-loader.js -> resource://path/to/file.js .
       // What's needed is only the last part after " -> ".
       location = result[2].split(" -> ").pop();
     }
 
     if (!functionName) {
       functionName = "<anonymous>";
     }
 
+    let onLocationClick;
+    // Given the input: "scriptLocation:2:100"
+    // Result:
+    // ["scriptLocation:2:100", "scriptLocation", "2", "100"]
+    const locationParts = location.match(/^(.*):(\d+):(\d+)$/);
+    if (props.onViewSourceInDebugger && location && !IGNORED_SOURCE_URLS.includes(locationParts[1]) && locationParts) {
+      let [, url, line, column] = locationParts;
+      onLocationClick = e => {
+        // Don't trigger ObjectInspector expand/collapse.
+        e.stopPropagation();
+        props.onViewSourceInDebugger({
+          url,
+          line: Number(line),
+          column: Number(column)
+        });
+      };
+    }
+
     stack.push(span({
       key: "fn" + index,
       className: "objectBox-stackTrace-fn"
     }, cleanFunctionName(functionName)), span({
       key: "location" + index,
-      className: "objectBox-stackTrace-location"
-    }, ` (${location})`));
-  });
+      className: "objectBox-stackTrace-location",
+      onClick: onLocationClick,
+      title: onLocationClick ? "View source in debugger → " + location : undefined
+    }, location));
+  });
+
+  if (isStacktraceALongString) {
+    // Remove the last frame (i.e. 2 last elements in the array, the function name and the
+    // location) which is certainly incomplete.
+    // Can be removed when https://bugzilla.mozilla.org/show_bug.cgi?id=1448833 is fixed.
+    stack.splice(-2);
+  }
 
   return span({
     key: "stack",
     className: "objectBox-stackTrace-grid"
   }, stack);
 }
 
 // Registration
@@ -21027,16 +21056,21 @@ module.exports = {
 /* 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 { createElement, createFactory, PureComponent } = __webpack_require__(0);
 const { Provider } = __webpack_require__(3592);
 const ObjectInspector = createFactory(__webpack_require__(3615));
 const createStore = __webpack_require__(3618);
+const Utils = __webpack_require__(1938);
+const {
+  renderRep,
+  shouldRenderRootsInReps
+} = Utils;
 
 class OI extends PureComponent {
 
   constructor(props) {
     super(props);
     this.store = createStore(props);
   }
 
@@ -21044,17 +21078,23 @@ class OI extends PureComponent {
     return this.store;
   }
 
   render() {
     return createElement(Provider, { store: this.store }, ObjectInspector(this.props));
   }
 }
 
-module.exports = OI;
+module.exports = props => {
+  let { roots } = props;
+  if (shouldRenderRootsInReps(roots)) {
+    return renderRep(roots[0], props);
+  }
+  return new OI(props);
+};
 
 /***/ }),
 
 /***/ 1586:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
@@ -24242,17 +24282,16 @@ exports.default = Accordion;
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
-exports.debugBtn = debugBtn;
 
 var _propTypes = __webpack_require__(20);
 
 var _propTypes2 = _interopRequireDefault(_propTypes);
 
 var _react = __webpack_require__(0);
 
 var _react2 = _interopRequireDefault(_react);
@@ -24272,18 +24311,16 @@ var _selectors = __webpack_require__(359
 var _text = __webpack_require__(1387);
 
 var _actions = __webpack_require__(1354);
 
 var _actions2 = _interopRequireDefault(_actions);
 
 var _CommandBarButton = __webpack_require__(1764);
 
-var _CommandBarButton2 = _interopRequireDefault(_CommandBarButton);
-
 __webpack_require__(1295);
 
 var _devtoolsModules = __webpack_require__(1376);
 
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
 /* -*- indent-tabs-mode: nil; js-indent-level: 2; js-indent-level: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
@@ -24335,31 +24372,16 @@ function formatKey(action) {
   if (isMacOS) {
     const winKey = getKeyForOS("WINNT", `${action}Display`) || getKeyForOS("WINNT", action);
     // display both Windows type and Mac specific keys
     return (0, _text.formatKeyShortcut)([key, winKey].join(" "));
   }
   return (0, _text.formatKeyShortcut)(key);
 }
 
-function debugBtn(onClick, type, className, tooltip, disabled = false, ariaPressed = false) {
-  return _react2.default.createElement(
-    _CommandBarButton2.default,
-    {
-      className: (0, _classnames2.default)(type, className),
-      disabled: disabled,
-      key: type,
-      onClick: onClick,
-      pressed: ariaPressed,
-      title: tooltip
-    },
-    _react2.default.createElement("img", { className: type })
-  );
-}
-
 class CommandBar extends _react.Component {
   componentWillUnmount() {
     const shortcuts = this.context.shortcuts;
     COMMANDS.forEach(action => shortcuts.off(getKey(action)));
     if (isMacOS) {
       COMMANDS.forEach(action => shortcuts.off(getKeyForOS("WINNT", action)));
     }
   }
@@ -24391,44 +24413,44 @@ class CommandBar extends _react.Componen
     const { isPaused, canRewind } = this.props;
     const className = isPaused ? "active" : "disabled";
     const isDisabled = !isPaused;
 
     if (canRewind || !isPaused && _prefs.features.removeCommandBarOptions) {
       return;
     }
 
-    return [debugBtn(this.props.stepOver, "stepOver", className, L10N.getFormatStr("stepOverTooltip", formatKey("stepOver")), isDisabled), debugBtn(this.props.stepIn, "stepIn", className, L10N.getFormatStr("stepInTooltip", formatKey("stepIn")), isDisabled), debugBtn(this.props.stepOut, "stepOut", className, L10N.getFormatStr("stepOutTooltip", formatKey("stepOut")), isDisabled)];
+    return [(0, _CommandBarButton.debugBtn)(this.props.stepOver, "stepOver", className, L10N.getFormatStr("stepOverTooltip", formatKey("stepOver")), isDisabled), (0, _CommandBarButton.debugBtn)(this.props.stepIn, "stepIn", className, L10N.getFormatStr("stepInTooltip", formatKey("stepIn")), isDisabled), (0, _CommandBarButton.debugBtn)(this.props.stepOut, "stepOut", className, L10N.getFormatStr("stepOutTooltip", formatKey("stepOut")), isDisabled)];
   }
 
   resume() {
     this.props.resume();
     this.props.clearHistory();
   }
 
   renderPauseButton() {
     const { isPaused, breakOnNext, isWaitingOnBreak, canRewind } = this.props;
 
     if (canRewind) {
       return;
     }
 
     if (isPaused) {
-      return debugBtn(() => this.resume(), "resume", "active", L10N.getFormatStr("resumeButtonTooltip", formatKey("resume")));
+      return (0, _CommandBarButton.debugBtn)(() => this.resume(), "resume", "active", L10N.getFormatStr("resumeButtonTooltip", formatKey("resume")));
     }
 
     if (_prefs.features.removeCommandBarOptions && !this.props.canRewind) {
       return;
     }
 
     if (isWaitingOnBreak) {
-      return debugBtn(null, "pause", "disabled", L10N.getStr("pausePendingButtonTooltip"), true);
-    }
-
-    return debugBtn(breakOnNext, "pause", "active", L10N.getFormatStr("pauseButtonTooltip", formatKey("pause")));
+      return (0, _CommandBarButton.debugBtn)(null, "pause", "disabled", L10N.getStr("pausePendingButtonTooltip"), true);
+    }
+
+    return (0, _CommandBarButton.debugBtn)(breakOnNext, "pause", "active", L10N.getFormatStr("pauseButtonTooltip", formatKey("pause")));
   }
 
   /*
    * The pause on exception button has three states in this order:
    *  1. don't pause on exceptions      [false, false]
    *  2. pause on uncaught exceptions   [true, true]
    *  3. pause on all exceptions        [true, false]
   */
@@ -24440,62 +24462,62 @@ class CommandBar extends _react.Componen
       canRewind
     } = this.props;
 
     if (canRewind || _prefs.features.breakpointsDropdown) {
       return;
     }
 
     if (!shouldPauseOnExceptions && !shouldIgnoreCaughtExceptions) {
-      return debugBtn(() => pauseOnExceptions(true, true), "pause-exceptions", "enabled", L10N.getStr("ignoreExceptions"), false, false);
+      return (0, _CommandBarButton.debugBtn)(() => pauseOnExceptions(true, true), "pause-exceptions", "enabled", L10N.getStr("ignoreExceptions"), false, false);
     }
 
     if (shouldPauseOnExceptions && shouldIgnoreCaughtExceptions) {
-      return debugBtn(() => pauseOnExceptions(true, false), "pause-exceptions", "uncaught enabled", L10N.getStr("pauseOnUncaughtExceptions"), false, true);
-    }
-
-    return debugBtn(() => pauseOnExceptions(false, false), "pause-exceptions", "all enabled", L10N.getStr("pauseOnExceptions"), false, true);
+      return (0, _CommandBarButton.debugBtn)(() => pauseOnExceptions(true, false), "pause-exceptions", "uncaught enabled", L10N.getStr("pauseOnUncaughtExceptions"), false, true);
+    }
+
+    return (0, _CommandBarButton.debugBtn)(() => pauseOnExceptions(false, false), "pause-exceptions", "all enabled", L10N.getStr("pauseOnExceptions"), false, true);
   }
 
   renderTimeTravelButtons() {
     const { isPaused, canRewind } = this.props;
 
     if (!canRewind || !isPaused) {
       return null;
     }
 
     const isDisabled = !isPaused;
 
-    return [debugBtn(this.props.rewind, "rewind", "active", "Rewind Execution"), debugBtn(() => this.props.resume, "resume", "active", L10N.getFormatStr("resumeButtonTooltip", formatKey("resume"))), _react2.default.createElement("div", { className: "divider" }), debugBtn(this.props.reverseStepOver, "reverseStepOver", "active", "Reverse step over"), debugBtn(this.props.stepOver, "stepOver", "active", L10N.getFormatStr("stepOverTooltip", formatKey("stepOver")), isDisabled), _react2.default.createElement("div", { className: "divider" }), debugBtn(this.props.stepOut, "stepOut", "active", L10N.getFormatStr("stepOutTooltip", formatKey("stepOut")), isDisabled), debugBtn(this.props.stepIn, "stepIn", "active", L10N.getFormatStr("stepInTooltip", formatKey("stepIn")), isDisabled)];
+    return [(0, _CommandBarButton.debugBtn)(this.props.rewind, "rewind", "active", "Rewind Execution"), (0, _CommandBarButton.debugBtn)(() => this.props.resume, "resume", "active", L10N.getFormatStr("resumeButtonTooltip", formatKey("resume"))), _react2.default.createElement("div", { className: "divider" }), (0, _CommandBarButton.debugBtn)(this.props.reverseStepOver, "reverseStepOver", "active", "Reverse step over"), (0, _CommandBarButton.debugBtn)(this.props.stepOver, "stepOver", "active", L10N.getFormatStr("stepOverTooltip", formatKey("stepOver")), isDisabled), _react2.default.createElement("div", { className: "divider" }), (0, _CommandBarButton.debugBtn)(this.props.stepOut, "stepOut", "active", L10N.getFormatStr("stepOutTooltip", formatKey("stepOut")), isDisabled), (0, _CommandBarButton.debugBtn)(this.props.stepIn, "stepIn", "active", L10N.getFormatStr("stepInTooltip", formatKey("stepIn")), isDisabled)];
   }
 
   replayPreviousButton() {
     const { history, historyPosition, canRewind } = this.props;
     const historyLength = history.length;
 
     if (canRewind || !historyLength || historyLength <= 1 || !_prefs.features.replay) {
       return null;
     }
 
     const enabled = historyPosition === 0;
     const activeClass = enabled ? "replay-inactive" : "";
-    return debugBtn(() => this.setHistory(-1), `replay-previous ${activeClass}`, "active", L10N.getStr("replayPrevious"), enabled);
+    return (0, _CommandBarButton.debugBtn)(() => this.setHistory(-1), `replay-previous ${activeClass}`, "active", L10N.getStr("replayPrevious"), enabled);
   }
 
   replayNextButton() {
     const { history, historyPosition, canRewind } = this.props;
     const historyLength = history.length;
 
     if (canRewind || !historyLength || historyLength <= 1 || !_prefs.features.replay) {
       return null;
     }
 
     const enabled = historyPosition + 1 === historyLength;
     const activeClass = enabled ? "replay-inactive" : "";
-    return debugBtn(() => this.setHistory(1), `replay-next ${activeClass}`, "active", L10N.getStr("replayNext"), enabled);
+    return (0, _CommandBarButton.debugBtn)(() => this.setHistory(1), `replay-next ${activeClass}`, "active", L10N.getStr("replayNext"), enabled);
   }
 
   renderStepPosition() {
     const { history, historyPosition, canRewind } = this.props;
     const historyLength = history.length;
 
     if (canRewind || !historyLength || !_prefs.features.replay) {
       return null;
@@ -24577,29 +24599,29 @@ Object.defineProperty(exports, "__esModu
 var _react = __webpack_require__(0);
 
 var _react2 = _interopRequireDefault(_react);
 
 var _classnames = __webpack_require__(175);
 
 var _classnames2 = _interopRequireDefault(_classnames);
 
-var _CommandBar = __webpack_require__(1608);
+var _CommandBarButton = __webpack_require__(1764);
 
 __webpack_require__(1295);
 
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
 /* 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/>. */
 
 class UtilsBar extends _react.Component {
   renderUtilButtons() {
-    return [(0, _CommandBar.debugBtn)(this.props.toggleShortcutsModal, "shortcuts", "active", L10N.getStr("shortcuts.buttonName"), false)];
+    return [(0, _CommandBarButton.debugBtn)(this.props.toggleShortcutsModal, "shortcuts", "active", L10N.getStr("shortcuts.buttonName"), false)];
   }
 
   render() {
     return _react2.default.createElement(
       "div",
       {
         className: (0, _classnames2.default)("command-bar bottom", {
           vertical: !this.props.horizontal
@@ -26272,17 +26294,17 @@ function buildGeneratedBindingList(scope
         }
       }
     }
   }
 
   // Sort so we can binary-search.
   return generatedBindings.sort((a, b) => {
     const aStart = a.loc.start;
-    const bStart = a.loc.start;
+    const bStart = b.loc.start;
 
     if (aStart.line === bStart.line) {
       return (0, _locColumn.locColumn)(aStart) - (0, _locColumn.locColumn)(bStart);
     }
     return aStart.line - bStart.line;
   });
 }
 
@@ -29232,32 +29254,49 @@ InlineSVG.propTypes = {
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
 
 var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
 
+exports.debugBtn = debugBtn;
+
 var _classnames = __webpack_require__(175);
 
 var _classnames2 = _interopRequireDefault(_classnames);
 
 var _react = __webpack_require__(0);
 
 var _react2 = _interopRequireDefault(_react);
 
 __webpack_require__(1788);
 
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
 function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; } /* 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/>. */
 
+function debugBtn(onClick, type, className, tooltip, disabled = false, ariaPressed = false) {
+  return _react2.default.createElement(
+    CommandBarButton,
+    {
+      className: (0, _classnames2.default)(type, className),
+      disabled: disabled,
+      key: type,
+      onClick: onClick,
+      pressed: ariaPressed,
+      title: tooltip
+    },
+    _react2.default.createElement("img", { className: type })
+  );
+}
+
 const CommandBarButton = props => {
   const { children, className, pressed = false } = props,
         rest = _objectWithoutProperties(props, ["children", "className", "pressed"]);
 
   return _react2.default.createElement(
     "button",
     _extends({
       "aria-pressed": pressed,
@@ -32542,23 +32581,54 @@ module.exports = ReactPropTypesSecret;
 
 /* 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 client = __webpack_require__(1939);
 const loadProperties = __webpack_require__(2017);
 const node = __webpack_require__(1940);
+const { nodeIsError, nodeIsPrimitive } = node;
 const selection = __webpack_require__(3616);
 
+const { MODE } = __webpack_require__(1357);
+const {
+  REPS: {
+    Rep,
+    Grip
+  }
+} = __webpack_require__(1372);
+
+
+function shouldRenderRootsInReps(roots) {
+  if (roots.length > 1) {
+    return false;
+  }
+
+  const root = roots[0];
+  const name = root && root.name;
+  return (name === null || typeof name === "undefined") && (nodeIsPrimitive(root) || nodeIsError(root));
+}
+
+function renderRep(item, props) {
+  return Rep({
+    ...props,
+    object: node.getValue(item),
+    mode: props.mode || MODE.TINY,
+    defaultRep: Grip
+  });
+}
+
 module.exports = {
   client,
   loadProperties,
   node,
-  selection
+  renderRep,
+  selection,
+  shouldRenderRootsInReps
 };
 
 /***/ }),
 
 /***/ 1939:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
@@ -32645,21 +32715,22 @@ module.exports = {
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const { get, has } = __webpack_require__(2);
 const { maybeEscapePropertyName } = __webpack_require__(1353);
 const ArrayRep = __webpack_require__(1448);
 const GripArrayRep = __webpack_require__(1450);
 const GripMap = __webpack_require__(1584);
 const GripMapEntryRep = __webpack_require__(1451);
+const ErrorRep = __webpack_require__(1580);
 
 const MAX_NUMERICAL_PROPERTIES = 100;
 
 const NODE_TYPES = {
-  BUCKET: Symbol("[n…n]"),
+  BUCKET: Symbol("[n…m]"),
   DEFAULT_PROPERTIES: Symbol("<default properties>"),
   ENTRIES: Symbol("<entries>"),
   GET: Symbol("<get>"),
   GRIP: Symbol("GRIP"),
   MAP_ENTRY_KEY: Symbol("<key>"),
   MAP_ENTRY_VALUE: Symbol("<value>"),
   PROMISE_REASON: Symbol("<reason>"),
   PROMISE_STATE: Symbol("<state>"),
@@ -32672,18 +32743,16 @@ const NODE_TYPES = {
 };
 
 let WINDOW_PROPERTIES = {};
 
 if (typeof window === "object") {
   WINDOW_PROPERTIES = Object.getOwnPropertyNames(window);
 }
 
-const SAFE_PATH_PREFIX = "##-";
-
 function getType(item) {
   return item.type;
 }
 
 function getValue(item) {
   if (has(item, "contents.value")) {
     return get(item, "contents.value");
   }
@@ -32815,16 +32884,20 @@ function nodeIsGetter(item) {
 function nodeIsSetter(item) {
   return getType(item) === NODE_TYPES.SET;
 }
 
 function nodeIsBlock(item) {
   return getType(item) === NODE_TYPES.BLOCK;
 }
 
+function nodeIsError(item) {
+  return ErrorRep.supportsObject(getValue(item));
+}
+
 function nodeHasAccessors(item) {
   return !!getNodeGetter(item) || !!getNodeSetter(item);
 }
 
 function nodeSupportsNumericalBucketing(item) {
   // We exclude elements with entries since it's the <entries> node
   // itself that can have buckets.
   return nodeIsArrayLike(item) && !nodeHasEntries(item) || nodeIsEntries(item) || nodeIsBucket(item);
@@ -32864,94 +32937,155 @@ function nodeNeedsNumericalBuckets(item)
 }
 
 function makeNodesForPromiseProperties(item) {
   const { promiseState: { reason, value, state } } = getValue(item);
 
   const properties = [];
 
   if (state) {
-    properties.push(createNode(item, "<state>", `${item.path}/${SAFE_PATH_PREFIX}state`, { value: state }, NODE_TYPES.PROMISE_STATE));
+    properties.push(createNode({
+      parent: item,
+      name: "<state>",
+      contents: { value: state },
+      type: NODE_TYPES.PROMISE_STATE
+    }));
   }
 
   if (reason) {
-    properties.push(createNode(item, "<reason>", `${item.path}/${SAFE_PATH_PREFIX}reason`, { value: reason }, NODE_TYPES.PROMISE_REASON));
+    properties.push(createNode({
+      parent: item,
+      name: "<reason>",
+      contents: { value: reason },
+      type: NODE_TYPES.PROMISE_REASON
+    }));
   }
 
   if (value) {
-    properties.push(createNode(item, "<value>", `${item.path}/${SAFE_PATH_PREFIX}value`, { value: value }, NODE_TYPES.PROMISE_VALUE));
+    properties.push(createNode({
+      parent: item,
+      name: "<value>",
+      contents: { value: value },
+      type: NODE_TYPES.PROMISE_VALUE
+    }));
   }
 
   return properties;
 }
 
 function makeNodesForProxyProperties(item) {
   const {
     proxyHandler,
     proxyTarget
   } = getValue(item);
 
-  return [createNode(item, "<target>", `${item.path}/${SAFE_PATH_PREFIX}target`, { value: proxyTarget }, NODE_TYPES.PROXY_TARGET), createNode(item, "<handler>", `${item.path}/${SAFE_PATH_PREFIX}handler`, { value: proxyHandler }, NODE_TYPES.PROXY_HANDLER)];
+  return [createNode({
+    parent: item,
+    name: "<target>",
+    contents: { value: proxyTarget },
+    type: NODE_TYPES.PROXY_TARGET
+  }), createNode({
+    parent: item,
+    name: "<handler>",
+    contents: { value: proxyHandler },
+    type: NODE_TYPES.PROXY_HANDLER
+  })];
 }
 
 function makeNodesForEntries(item) {
-  const { path } = item;
   const nodeName = "<entries>";
-  const entriesPath = `${path}/${SAFE_PATH_PREFIX}entries`;
+  const entriesPath = "<entries>";
 
   if (nodeHasAllEntriesInPreview(item)) {
     let entriesNodes = [];
     const { preview } = getValue(item);
     if (preview.entries) {
       entriesNodes = preview.entries.map(([key, value], index) => {
-        return createNode(item, index, `${entriesPath}/${index}`, {
-          value: GripMapEntryRep.createGripMapEntry(key, value)
+        return createNode({
+          parent: item,
+          name: index,
+          path: `${entriesPath}/${index}`,
+          contents: { value: GripMapEntryRep.createGripMapEntry(key, value) }
         });
       });
     } else if (preview.items) {
       entriesNodes = preview.items.map((value, index) => {
-        return createNode(item, index, `${entriesPath}/${index}`, { value });
-      });
-    }
-    return createNode(item, nodeName, entriesPath, entriesNodes, NODE_TYPES.ENTRIES);
-  }
-  return createNode(item, nodeName, entriesPath, null, NODE_TYPES.ENTRIES);
+        return createNode({
+          parent: item,
+          name: index,
+          path: `${entriesPath}/${index}`,
+          contents: { value }
+        });
+      });
+    }
+    return createNode({
+      parent: item,
+      name: nodeName,
+      contents: entriesNodes,
+      type: NODE_TYPES.ENTRIES
+    });
+  }
+  return createNode({
+    parent: item,
+    name: nodeName,
+    contents: null,
+    type: NODE_TYPES.ENTRIES
+  });
 }
 
 function makeNodesForMapEntry(item) {
   const nodeValue = getValue(item);
   if (!nodeValue || !nodeValue.preview) {
     return [];
   }
 
   const { key, value } = nodeValue.preview;
-  const path = item.path;
-
-  return [createNode(item, "<key>", `${path}/##key`, { value: key }, NODE_TYPES.MAP_ENTRY_KEY), createNode(item, "<value>", `${path}/##value`, { value }, NODE_TYPES.MAP_ENTRY_VALUE)];
+
+  return [createNode({
+    parent: item,
+    name: "<key>",
+    contents: { value: key },
+    type: NODE_TYPES.MAP_ENTRY_KEY
+  }), createNode({
+    parent: item,
+    name: "<value>",
+    contents: { value },
+    type: NODE_TYPES.MAP_ENTRY_VALUE
+  })];
 }
 
 function getNodeGetter(item) {
   return get(item, "contents.get", undefined);
 }
 
 function getNodeSetter(item) {
   return get(item, "contents.set", undefined);
 }
 
 function makeNodesForAccessors(item) {
   const accessors = [];
 
   const getter = getNodeGetter(item);
   if (getter && getter.type !== "undefined") {
-    accessors.push(createNode(item, "<get>", `${item.path}/${SAFE_PATH_PREFIX}get`, { value: getter }, NODE_TYPES.GET));
+    accessors.push(createNode({
+      parent: item,
+      name: "<get>",
+      contents: { value: getter },
+      type: NODE_TYPES.GET
+    }));
   }
 
   const setter = getNodeSetter(item);
   if (setter && setter.type !== "undefined") {
-    accessors.push(createNode(item, "<set>", `${item.path}/${SAFE_PATH_PREFIX}set`, { value: setter }, NODE_TYPES.SET));
+    accessors.push(createNode({
+      parent: item,
+      name: "<set>",
+      contents: { value: setter },
+      type: NODE_TYPES.SET
+    }));
   }
 
   return accessors;
 }
 
 function sortProperties(properties) {
   return properties.sort((a, b) => {
     // Sort numbers in ascending order and sort strings lexicographically
@@ -32962,80 +33096,94 @@ function sortProperties(properties) {
       return a > b ? 1 : -1;
     }
 
     return aInt - bInt;
   });
 }
 
 function makeNumericalBuckets(parent) {
-  const parentPath = parent.path;
   const numProperties = getNumericalPropertiesCount(parent);
 
   // We want to have at most a hundred slices.
   const bucketSize = 10 ** Math.max(2, Math.ceil(Math.log10(numProperties)) - 2);
   const numBuckets = Math.ceil(numProperties / bucketSize);
 
   let buckets = [];
   for (let i = 1; i <= numBuckets; i++) {
     const minKey = (i - 1) * bucketSize;
     const maxKey = Math.min(i * bucketSize - 1, numProperties - 1);
     const startIndex = nodeIsBucket(parent) ? parent.meta.startIndex : 0;
     const minIndex = startIndex + minKey;
     const maxIndex = startIndex + maxKey;
-    const bucketKey = `${SAFE_PATH_PREFIX}bucket_${minIndex}-${maxIndex}`;
     const bucketName = `[${minIndex}…${maxIndex}]`;
 
-    buckets.push(createNode(parent, bucketName, `${parentPath}/${bucketKey}`, null, NODE_TYPES.BUCKET, {
-      startIndex: minIndex,
-      endIndex: maxIndex
+    buckets.push(createNode({
+      parent,
+      name: bucketName,
+      contents: null,
+      type: NODE_TYPES.BUCKET,
+      meta: {
+        startIndex: minIndex,
+        endIndex: maxIndex
+      }
     }));
   }
   return buckets;
 }
 
 function makeDefaultPropsBucket(propertiesNames, parent, ownProperties) {
-  const parentPath = parent.path;
-
   const userPropertiesNames = [];
   const defaultProperties = [];
 
   propertiesNames.forEach(name => {
     if (isDefaultWindowProperty(name)) {
       defaultProperties.push(name);
     } else {
       userPropertiesNames.push(name);
     }
   });
 
   let nodes = makeNodesForOwnProps(userPropertiesNames, parent, ownProperties);
 
   if (defaultProperties.length > 0) {
-    const defaultPropertiesNode = createNode(parent, "<default properties>", `${parentPath}/${SAFE_PATH_PREFIX}default`, null, NODE_TYPES.DEFAULT_PROPERTIES);
-
-    const defaultNodes = defaultProperties.map((name, index) => createNode(defaultPropertiesNode, maybeEscapePropertyName(name), `${parentPath}/${SAFE_PATH_PREFIX}bucket${index}/${name}`, ownProperties[name]));
+    const defaultPropertiesNode = createNode({
+      parent,
+      name: "<default properties>",
+      contents: null,
+      type: NODE_TYPES.DEFAULT_PROPERTIES
+    });
+
+    const defaultNodes = defaultProperties.map((name, index) => createNode({
+      parent: defaultPropertiesNode,
+      name: maybeEscapePropertyName(name),
+      path: `${index}/${name}`,
+      contents: ownProperties[name]
+    }));
     nodes.push(setNodeChildren(defaultPropertiesNode, defaultNodes));
   }
   return nodes;
 }
 
 function makeNodesForOwnProps(propertiesNames, parent, ownProperties) {
-  const parentPath = parent.path;
-  return propertiesNames.map(name => createNode(parent, maybeEscapePropertyName(name), `${parentPath}/${name}`, ownProperties[name]));
+  return propertiesNames.map(name => createNode({
+    parent,
+    name: maybeEscapePropertyName(name),
+    contents: ownProperties[name]
+  }));
 }
 
 function makeNodesForProperties(objProps, parent) {
   const {
     ownProperties = {},
     ownSymbols,
     prototype,
     safeGetterValues
   } = objProps;
 
-  const parentPath = parent.path;
   const parentValue = getValue(parent);
 
   let allProperties = { ...ownProperties, ...safeGetterValues };
 
   // Ignore properties that are neither non-concrete nor getters/setters.
   const propertiesNames = sortProperties(Object.keys(allProperties)).filter(name => {
     if (!allProperties[name]) {
       return false;
@@ -33049,17 +33197,22 @@ function makeNodesForProperties(objProps
   if (parentValue && parentValue.class == "Window") {
     nodes = makeDefaultPropsBucket(propertiesNames, parent, allProperties);
   } else {
     nodes = makeNodesForOwnProps(propertiesNames, parent, allProperties);
   }
 
   if (Array.isArray(ownSymbols)) {
     ownSymbols.forEach((ownSymbol, index) => {
-      nodes.push(createNode(parent, ownSymbol.name, `${parentPath}/${SAFE_PATH_PREFIX}symbol-${index}`, ownSymbol.descriptor || null));
+      nodes.push(createNode({
+        parent,
+        name: ownSymbol.name,
+        path: `symbol-${index}`,
+        contents: ownSymbol.descriptor || null
+      }));
     }, this);
   }
 
   if (nodeIsPromise(parent)) {
     nodes.push(...makeNodesForPromiseProperties(parent));
   }
 
   if (nodeHasEntries(parent)) {
@@ -33076,42 +33229,61 @@ function makeNodesForProperties(objProps
 
 function makeNodeForPrototype(objProps, parent) {
   const {
     prototype
   } = objProps || {};
 
   // Add the prototype if it exists and is not null
   if (prototype && prototype.type !== "null") {
-    return createNode(parent, "<prototype>", `${parent.path}/<prototype>`, { value: prototype }, NODE_TYPES.PROTOTYPE);
+    return createNode({
+      parent,
+      name: "<prototype>",
+      contents: { value: prototype },
+      type: NODE_TYPES.PROTOTYPE
+    });
   }
 
   return null;
 }
 
-function createNode(parent, name, path, contents, type = NODE_TYPES.GRIP, meta) {
+function createNode(options) {
+  const {
+    parent,
+    name,
+    path,
+    contents,
+    type = NODE_TYPES.GRIP,
+    meta
+  } = options;
+
   if (contents === undefined) {
     return null;
   }
 
   // The path is important to uniquely identify the item in the entire
   // tree. This helps debugging & optimizes React's rendering of large
-  // lists. The path will be separated by property name,
-  // i.e. `{ foo: { bar: { baz: 5 }}}` will have a path of `foo/bar/baz`
+  // lists. The path will be separated by property name, wrapped in a Symbol to avoid
+  // name clashing,
+  // i.e. `{ foo: { bar: { baz: 5 }}}` will have a path of Symbol(`foo/bar/baz`)
   // for the inner object.
   return {
     parent,
     name,
-    path,
+    path: parent ? Symbol(`${getSymbolDescriptor(parent.path)}/${path || name}`) : Symbol(path || name),
     contents,
     type,
     meta
   };
 }
 
+function getSymbolDescriptor(symbol) {
+  return symbol.toString().replace(/^(Symbol\()(.*)(\))$/, "$2");
+}
+
 function setNodeChildren(node, children) {
   node.contents = children;
   return node;
 }
 
 function getChildren(options) {
   const {
     cachedNodes,
@@ -33120,23 +33292,17 @@ function getChildren(options) {
   } = options;
 
   const key = item.path;
   if (cachedNodes && cachedNodes.has(key)) {
     return cachedNodes.get(key);
   }
 
   const loadedProps = loadedProperties.get(key);
-  const {
-    ownProperties,
-    ownSymbols,
-    safeGetterValues,
-    prototype
-  } = loadedProps || {};
-  const hasLoadedProps = ownProperties || ownSymbols || safeGetterValues || prototype;
+  const hasLoadedProps = loadedProperties.has(key);
 
   // Because we are dynamically creating the tree as the user
   // expands it (not precalculated tree structure), we cache child
   // arrays. This not only helps performance, but is necessary
   // because the expanded state depends on instances of nodes
   // being the same across renders. If we didn't do this, each
   // node would be a new instance every render.
   // If the node needs properties, we only add children to
@@ -33158,35 +33324,24 @@ function getChildren(options) {
     return addToCache(makeNodesForAccessors(item));
   }
 
   if (nodeIsMapEntry(item)) {
     return addToCache(makeNodesForMapEntry(item));
   }
 
   if (nodeIsProxy(item)) {
-    const nodes = makeNodesForProxyProperties(item);
-    const protoNode = makeNodeForPrototype(loadedProps, item);
-    if (protoNode) {
-      return addToCache(nodes.concat(protoNode));
-    }
-    return nodes;
-  }
-
-  if (nodeNeedsNumericalBuckets(item)) {
+    return addToCache(makeNodesForProxyProperties(item));
+  }
+
+  if (nodeNeedsNumericalBuckets(item) && hasLoadedProps) {
+    // Even if we have numerical buckets, we should have loaded non indexed properties,
+    // like length for example.
     const bucketNodes = makeNumericalBuckets(item);
-    // Even if we have numerical buckets, we might have loaded non indexed properties,
-    // like length for example.
-    if (hasLoadedProps) {
-      return addToCache(bucketNodes.concat(makeNodesForProperties(loadedProps, item)));
-    }
-
-    // We don't cache the result here so we can have the prototype, properties and symbols
-    // when they are loaded.
-    return bucketNodes;
+    return addToCache(bucketNodes.concat(makeNodesForProperties(loadedProps, item)));
   }
 
   if (!nodeIsEntries(item) && !nodeIsBucket(item) && !nodeHasProperties(item)) {
     return [];
   }
 
   if (!hasLoadedProps) {
     return [];
@@ -33269,16 +33424,17 @@ module.exports = {
   nodeHasAllEntriesInPreview,
   nodeHasChildren,
   nodeHasEntries,
   nodeHasProperties,
   nodeIsBlock,
   nodeIsBucket,
   nodeIsDefaultProperties,
   nodeIsEntries,
+  nodeIsError,
   nodeIsFunction,
   nodeIsGetter,
   nodeIsMapEntry,
   nodeIsMissingArguments,
   nodeIsObject,
   nodeIsOptimizedOut,
   nodeIsPrimitive,
   nodeIsPromise,
@@ -33288,19 +33444,17 @@ module.exports = {
   nodeIsUninitializedBinding,
   nodeIsUnmappedBinding,
   nodeIsUnscopedBinding,
   nodeIsWindow,
   nodeNeedsNumericalBuckets,
   nodeSupportsNumericalBucketing,
   setNodeChildren,
   sortProperties,
-  NODE_TYPES,
-  // Export for testing purpose.
-  SAFE_PATH_PREFIX
+  NODE_TYPES
 };
 
 /***/ }),
 
 /***/ 1941:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
@@ -34465,17 +34619,17 @@ module.exports = "<!-- This Source Code 
 
 module.exports = "<!-- 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/. --><svg version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" viewBox=\"0 0 28 28\"><path d=\"M9.633 7.968h3.751v10.514c0 4.738-2.271 6.392-5.899 6.392-0.888 0-2.024-0.148-2.764-0.395l0.42-3.036c0.518 0.173 1.185 0.296 1.925 0.296 1.58 0 2.567-0.716 2.567-3.282v-10.489zM16.641 20.753c0.987 0.518 2.567 1.037 4.171 1.037 1.728 0 2.641-0.716 2.641-1.826 0-1.012-0.79-1.629-2.789-2.32-2.764-0.987-4.59-2.517-4.59-4.961 0-2.838 2.394-4.985 6.293-4.985 1.9 0 3.258 0.37 4.245 0.839l-0.839 3.011c-0.642-0.321-1.851-0.79-3.455-0.79-1.629 0-2.419 0.765-2.419 1.604 0 1.061 0.913 1.53 3.085 2.369 2.937 1.086 4.294 2.616 4.294 4.985 0 2.789-2.122 5.158-6.688 5.158-1.9 0-3.776-0.518-4.714-1.037l0.765-3.085z\"></path></svg>"
 
 /***/ }),
 
 /***/ 2252:
 /***/ (function(module, exports) {
 
-module.exports = "<!-- 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/. --><svg viewBox=\"0 0 128 128\"><path xmlns=\"http://www.w3.org/2000/svg\" class=\"cls-2\" id=\"original-2\" d=\"M 1.5 63.91 v 62.5 h 125 V 1.41 H 1.5 Z m 100.73 -5 a 15.56 15.56 0 0 1 7.82 4.5 a 20.58 20.58 0 0 1 3 4 c 0 0.16 -5.4 3.81 -8.69 5.85 c -0.12 0.08 -0.6 -0.44 -1.13 -1.23 a 7.09 7.09 0 0 0 -5.87 -3.53 c -3.79 -0.26 -6.23 1.73 -6.21 5 a 4.58 4.58 0 0 0 0.54 2.34 c 0.83 1.73 2.38 2.76 7.24 4.86 c 8.95 3.85 12.78 6.39 15.16 10 c 2.66 4 3.25 10.46 1.45 15.24 c -2 5.2 -6.9 8.73 -13.83 9.9 a 38.32 38.32 0 0 1 -9.52 -0.1 a 23 23 0 0 1 -12.72 -6.63 c -1.15 -1.27 -3.39 -4.58 -3.25 -4.82 a 9.34 9.34 0 0 1 1.15 -0.73 L 82 101 l 3.59 -2.08 l 0.75 1.11 a 16.78 16.78 0 0 0 4.74 4.54 c 4 2.1 9.46 1.81 12.16 -0.62 a 5.43 5.43 0 0 0 0.69 -6.92 c -1 -1.39 -3 -2.56 -8.59 -5 c -6.45 -2.78 -9.23 -4.5 -11.77 -7.24 a 16.48 16.48 0 0 1 -3.43 -6.25 a 25 25 0 0 1 -0.22 -8 c 1.33 -6.23 6 -10.58 12.82 -11.87 A 31.66 31.66 0 0 1 102.23 58.93 Z M 72.89 64.15 l 0 5.12 H 56.66 V 115.5 H 45.15 V 69.26 H 28.88 v -5 A 49.19 49.19 0 0 1 29 59.09 C 29.08 59 39 59 51 59 L 72.83 59 Z\" data-name=\"original\"></path></svg>"
+module.exports = "<!-- 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/. --><svg viewBox=\"0 0 128 128\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M 1.5 63.91 v 62.5 h 125 V 1.41 H 1.5 Z m 100.73 -5 a 15.56 15.56 0 0 1 7.82 4.5 a 20.58 20.58 0 0 1 3 4 c 0 0.16 -5.4 3.81 -8.69 5.85 c -0.12 0.08 -0.6 -0.44 -1.13 -1.23 a 7.09 7.09 0 0 0 -5.87 -3.53 c -3.79 -0.26 -6.23 1.73 -6.21 5 a 4.58 4.58 0 0 0 0.54 2.34 c 0.83 1.73 2.38 2.76 7.24 4.86 c 8.95 3.85 12.78 6.39 15.16 10 c 2.66 4 3.25 10.46 1.45 15.24 c -2 5.2 -6.9 8.73 -13.83 9.9 a 38.32 38.32 0 0 1 -9.52 -0.1 a 23 23 0 0 1 -12.72 -6.63 c -1.15 -1.27 -3.39 -4.58 -3.25 -4.82 a 9.34 9.34 0 0 1 1.15 -0.73 L 82 101 l 3.59 -2.08 l 0.75 1.11 a 16.78 16.78 0 0 0 4.74 4.54 c 4 2.1 9.46 1.81 12.16 -0.62 a 5.43 5.43 0 0 0 0.69 -6.92 c -1 -1.39 -3 -2.56 -8.59 -5 c -6.45 -2.78 -9.23 -4.5 -11.77 -7.24 a 16.48 16.48 0 0 1 -3.43 -6.25 a 25 25 0 0 1 -0.22 -8 c 1.33 -6.23 6 -10.58 12.82 -11.87 A 31.66 31.66 0 0 1 102.23 58.93 Z M 72.89 64.15 l 0 5.12 H 56.66 V 115.5 H 45.15 V 69.26 H 28.88 v -5 A 49.19 49.19 0 0 1 29 59.09 C 29.08 59 39 59 51 59 L 72.83 59 Z\" data-name=\"original\"></path></svg>"
 
 /***/ }),
 
 /***/ 2253:
 /***/ (function(module, exports) {
 
 // removed by extract-text-webpack-plugin
 
@@ -34875,29 +35029,36 @@ var _extends = Object.assign || function
                                                                                                                                                                                                                                                                    * 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/>. */
 
 // eslint-disable-next-line max-len
 
 
 exports.findGeneratedBindingFromPosition = findGeneratedBindingFromPosition;
 
-var _lodash = __webpack_require__(2);
-
 var _locColumn = __webpack_require__(2349);
 
+var _filtering = __webpack_require__(3635);
+
 var _firefox = __webpack_require__(1500);
 
 async function findGeneratedBindingFromPosition(sourceMaps, client, source, pos, name, type, generatedAstBindings) {
   const range = await getGeneratedLocationRange(pos, source, sourceMaps);
 
   if (range) {
-    const result = await findGeneratedReference(type, generatedAstBindings, _extends({
-      type: pos.type
-    }, range));
+    let result;
+    if (type === "import") {
+      result = await findGeneratedImportReference(type, generatedAstBindings, _extends({
+        type: pos.type
+      }, range));
+    } else {
+      result = await findGeneratedReference(type, generatedAstBindings, _extends({
+        type: pos.type
+      }, range));
+    }
 
     if (result) {
       return result;
     }
   }
 
   if (type === "import" && pos.type === "decl") {
     let importRange = range;
@@ -34921,38 +35082,82 @@ async function findGeneratedBindingFromP
     return await findGeneratedImportDeclaration(generatedAstBindings, _extends({
       importName
     }, importRange));
   }
 
   return null;
 }
 
+function filterApplicableBindings(bindings, mapped) {
+  // Any binding overlapping a part of the mapping range.
+  return (0, _filtering.filterSortedArray)(bindings, binding => {
+    if (positionCmp(binding.loc.end, mapped.start) < 0) {
+      return -1;
+    }
+
+    // Currently we allow ranges to count if they start 1 character before,
+    // so we allow that when filtering here.
+    // See mapBindingReferenceToDescriptor for more info.
+    if (positionCmp(_extends({}, binding.loc.start, {
+      column: (0, _locColumn.locColumn)(binding.loc.start) - 1
+    }), mapped.end) > 0) {
+      return 1;
+    }
+
+    return 0;
+  });
+}
+
 /**
  * Given a mapped range over the generated source, attempt to resolve a real
  * binding descriptor that can be used to access the value.
  */
 async function findGeneratedReference(type, generatedAstBindings, mapped) {
-  return generatedAstBindings.reduce(async (acc, val) => {
+  const bindings = filterApplicableBindings(generatedAstBindings, mapped);
+
+  return bindings.reduce(async (acc, val) => {
     const accVal = await acc;
     if (accVal) {
       return accVal;
     }
 
-    return type === "import" ? await mapImportReferenceToDescriptor(val, mapped) : await mapBindingReferenceToDescriptor(val, mapped);
+    return mapBindingReferenceToDescriptor(val, mapped);
+  }, null);
+}
+
+async function findGeneratedImportReference(type, generatedAstBindings, mapped) {
+  let bindings = filterApplicableBindings(generatedAstBindings, mapped);
+
+  // When wrapped, for instance as `Object(ns.default)`, the `Object` binding
+  // will be the first in the list. To avoid resolving `Object` as the
+  // value of the import itself, we potentially skip the first binding.
+  if (bindings.length > 1 && !bindings[0].loc.meta && bindings[1].loc.meta) {
+    bindings = bindings.slice(1);
+  }
+
+  return bindings.reduce(async (acc, val) => {
+    const accVal = await acc;
+    if (accVal) {
+      return accVal;
+    }
+
+    return mapImportReferenceToDescriptor(val, mapped);
   }, null);
 }
 
 /**
  * Given a mapped range over the generated source and the name of the imported
  * value that is referenced, attempt to resolve a binding descriptor for
  * the import's value.
  */
 async function findGeneratedImportDeclaration(generatedAstBindings, mapped) {
-  return generatedAstBindings.reduce(async (acc, val) => {
+  const bindings = filterApplicableBindings(generatedAstBindings, mapped);
+
+  return bindings.reduce(async (acc, val) => {
     const accVal = await acc;
     if (accVal) {
       return accVal;
     }
 
     return await mapImportDeclarationToDescriptor(val, mapped);
   }, null);
 }
@@ -35147,18 +35352,29 @@ function positionCmp(p1, p2) {
 async function getGeneratedLocationRange(pos, source, sourceMaps) {
   const start = await getGeneratedLocation(sourceMaps, pos.start, source);
   const end = await getGeneratedLocation(sourceMaps, pos.end, source);
 
   // Since the map takes the closest location, sometimes mapping a
   // binding's location can point at the start of a binding listed after
   // it, so we need to make sure it maps to a location that actually has
   // a size in order to avoid picking up the wrong descriptor.
-  if ((0, _lodash.isEqual)(start, end)) {
-    return null;
+  if (positionCmp(start, end) === 0) {
+    return null;
+  }
+  if (positionCmp(start, end) > 0) {
+    // This will be fixed in future range work, but right now it is
+    // possible for the start to be after the end because of the way we
+    // map ranges. For now we create a placeholder single-character range.
+    return {
+      start,
+      end: _extends({}, start, {
+        column: start.column + 1
+      })
+    };
   }
 
   return { start, end };
 }
 
 async function getGeneratedLocation(sourceMaps, pos, source) {
   const all = await sourceMaps.getAllGeneratedLocations(pos, source);
   if (all.length > 0) {
@@ -38211,23 +38427,16 @@ const {
 const dom = __webpack_require__(1758);
 const { connect } = __webpack_require__(3592);
 const { bindActionCreators } = __webpack_require__(3593);
 
 const Tree = createFactory(_devtoolsComponents2.default.Tree);
 __webpack_require__(1325);
 
 const classnames = __webpack_require__(175);
-
-const {
-  REPS: {
-    Rep,
-    Grip
-  }
-} = __webpack_require__(1372);
 const {
   MODE
 } = __webpack_require__(1357);
 
 const Utils = __webpack_require__(1938);
 
 const {
   getChildren,
@@ -38302,17 +38511,21 @@ class ObjectInspector extends Component 
 
     if (roots !== nextProps.roots) {
       // Since the roots changed, we assume the properties did as well. Thus we can clear
       // the cachedNodes to avoid bugs and memory leaks.
       this.cachedNodes.clear();
       return true;
     }
 
-    return expandedPaths.size !== nextProps.expandedPaths.size || loadedProperties.size !== nextProps.loadedProperties.size || [...expandedPaths].some(key => !nextProps.expandedPaths.has(key));
+    // We should update if:
+    // - there are new loaded properties
+    // - OR the expanded paths number changed, and all of them have properties loaded
+    // - OR the expanded paths number did not changed, but old and new sets differ
+    return loadedProperties.size !== nextProps.loadedProperties.size || expandedPaths.size !== nextProps.expandedPaths.size && [...nextProps.expandedPaths].every(path => nextProps.loadedProperties.has(path)) || expandedPaths.size === nextProps.expandedPaths.size && [...nextProps.expandedPaths].some(key => !expandedPaths.has(key));
   }
 
   componentWillUnmount() {
     const { releaseActor } = this.props;
     if (typeof releaseActor !== "function") {
       return;
     }
 
@@ -38335,17 +38548,17 @@ class ObjectInspector extends Component 
     });
   }
 
   getRoots() {
     return this.props.roots;
   }
 
   getNodeKey(item) {
-    return item.path || JSON.stringify(item);
+    return item.path && typeof item.path.toString === "function" ? item.path.toString() : JSON.stringify(item);
   }
 
   setExpanded(item, expand) {
     if (nodeIsPrimitive(item)) {
       return;
     }
 
     const {
@@ -38427,17 +38640,17 @@ class ObjectInspector extends Component 
       return {
         label,
         value: dom.span({ className: "unavailable" }, "(unavailable)")
       };
     }
 
     if (nodeIsFunction(item) && !nodeIsGetter(item) && !nodeIsSetter(item) && (this.props.mode === MODE.TINY || !this.props.mode)) {
       return {
-        label: this.renderGrip(item, {
+        label: Utils.renderRep(item, {
           ...this.props,
           functionName: label
         })
       };
     }
 
     if (nodeHasProperties(item) || nodeHasAccessors(item) || nodeIsMapEntry(item) || isPrimitive) {
       let repsProp = { ...this.props };
@@ -38445,17 +38658,17 @@ class ObjectInspector extends Component 
         repsProp.mode = this.props.mode === MODE.LONG ? MODE.SHORT : MODE.TINY;
       }
       if (expanded) {
         repsProp.mode = MODE.TINY;
       }
 
       return {
         label,
-        value: this.renderGrip(item, repsProp)
+        value: Utils.renderRep(item, repsProp)
       };
     }
 
     return {
       label
     };
   }
 
@@ -38529,57 +38742,38 @@ class ObjectInspector extends Component 
   renderTreeItem(item, depth, focused, arrow, expanded) {
     const { label, value } = this.getTreeItemLabelAndValue(item, depth, expanded);
     const labelElement = this.renderTreeItemLabel(label, item, depth, focused, expanded);
     const delimiter = value && labelElement ? dom.span({ className: "object-delimiter" }, ": ") : null;
 
     return dom.div(this.getTreeTopElementProps(item, depth, focused, expanded), arrow, labelElement, delimiter, value);
   }
 
-  renderGrip(item, props) {
-    const object = getValue(item);
-    return Rep({
-      ...props,
-      object,
-      mode: props.mode || MODE.TINY,
-      defaultRep: Grip
-    });
-  }
-
   render() {
     const {
       autoExpandAll = true,
       autoExpandDepth = 1,
       disabledFocus,
       disableWrap = false,
       expandedPaths,
       focusedItem,
       inline
     } = this.props;
 
-    let roots = this.getRoots();
-    if (roots.length === 1) {
-      const root = roots[0];
-      const name = root && root.name;
-      if (nodeIsPrimitive(root) && (name === null || typeof name === "undefined")) {
-        return this.renderGrip(root, this.props);
-      }
-    }
-
     return Tree({
       className: classnames({
         inline,
         nowrap: disableWrap,
         "object-inspector": true
       }),
       autoExpandAll,
       autoExpandDepth,
       disabledFocus,
 
-      isExpanded: item => expandedPaths && expandedPaths.has(this.getNodeKey(item)),
+      isExpanded: item => expandedPaths && expandedPaths.has(item.path),
       isExpandable: item => nodeIsPrimitive(item) === false,
       focused: focusedItem,
 
       getRoots: this.getRoots,
       getParent,
       getChildren: this.getItemChildren,
       getKey: this.getNodeKey,
 
@@ -38665,17 +38859,20 @@ const {
  * which also means that it will call the action responsible to fetch properties.
  */
 function nodeExpand(node, actor, loadedProperties, createObjectClient) {
   return async ({ dispatch }) => {
     dispatch({
       type: "NODE_EXPAND",
       data: { node }
     });
-    dispatch(nodeLoadProperties(node, actor, loadedProperties, createObjectClient));
+
+    if (!loadedProperties.has(node.path)) {
+      dispatch(nodeLoadProperties(node, actor, loadedProperties, createObjectClient));
+    }
   };
 }
 
 function nodeCollapse(node) {
   return {
     type: "NODE_COLLAPSE",
     data: { node }
   };
@@ -38690,19 +38887,17 @@ function nodeFocus(node) {
 /*
  * This action checks if we need to fetch properties, entries, prototype and symbols
  * for a given node. If we do, it will call the appropriate ObjectClient functions.
  */
 function nodeLoadProperties(item, actor, loadedProperties, createObjectClient) {
   return async ({ dispatch }) => {
     try {
       const properties = await loadItemProperties(item, createObjectClient, loadedProperties);
-      if (Object.keys(properties).length > 0) {
-        dispatch(nodePropertiesLoaded(item, actor, properties));
-      }
+      dispatch(nodePropertiesLoaded(item, actor, properties));
     } catch (e) {
       console.error(e);
     }
   };
 }
 
 function nodePropertiesLoaded(node, actor, properties) {
   return {
@@ -39006,17 +39201,17 @@ const { createNode, getChildren } = _dev
 const { loadItemProperties } = _devtoolsReps.ObjectInspectorUtils.loadProperties;
 
 class FrameworkComponent extends _react.PureComponent {
   async componentWillMount() {
     const expression = "this;";
     const { selectedFrame, setPopupObjectProperties } = this.props;
     const value = selectedFrame.this;
 
-    const root = createNode(null, expression, expression, { value });
+    const root = createNode({ name: expression, contents: { value } });
     const properties = await loadItemProperties(root, _firefox.createObjectClient);
     if (properties) {
       setPopupObjectProperties(value, properties);
     }
   }
 
   renderReactComponent() {
     const { selectedFrame, popupObjectProperties } = this.props;
@@ -39024,16 +39219,19 @@ class FrameworkComponent extends _react.
     const value = selectedFrame.this;
     const root = {
       name: expression,
       path: expression,
       contents: { value }
     };
 
     const loadedRootProperties = popupObjectProperties[value.actor];
+    if (!loadedRootProperties) {
+      return null;
+    }
 
     let roots = getChildren({
       item: root,
       loadedProperties: new Map([[root.path, loadedRootProperties]])
     });
 
     roots = roots.filter(r => ["state", "props"].includes(r.name));
 
@@ -39420,16 +39618,66 @@ class Breakpoint extends _react.Componen
     );
   }
 }
 
 exports.default = Breakpoint;
 
 /***/ }),
 
+/***/ 3635:
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.filterSortedArray = filterSortedArray;
+function findInsertionLocation(array, callback) {
+  let left = 0;
+  let right = array.length;
+  while (left < right) {
+    const mid = Math.floor((left + right) / 2);
+    const item = array[mid];
+
+    const result = callback(item);
+    if (result === 0) {
+      left = mid;
+      break;
+    }
+    if (result >= 0) {
+      right = mid;
+    } else {
+      left = mid + 1;
+    }
+  }
+
+  // Ensure the value is the start of any set of matches.
+  let i = left;
+  while (i > 0 && callback(array[i]) >= 0) {
+    i--;
+  }
+  return i + 1;
+}
+
+function filterSortedArray(array, callback) {
+  const start = findInsertionLocation(array, callback);
+
+  const results = [];
+  for (let i = start; i < array.length && callback(array[i]) === 0; i++) {
+    results.push(array[i]);
+  }
+
+  return results;
+}
+
+/***/ }),
+
 /***/ 364:
 /***/ (function(module, exports) {
 
 module.exports = "<!-- 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/. --><svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" viewBox=\"0 0 33 12\"><path id=\"base-path\" d=\"M27.1,0H1C0.4,0,0,0.4,0,1v10c0,0.6,0.4,1,1,1h26.1 c0.6,0,1.2-0.3,1.5-0.7L33,6l-4.4-5.3C28.2,0.3,27.7,0,27.1,0z\"></path></svg>"
 
 /***/ }),
 
 /***/ 365:
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-asm.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-asm.js
@@ -4,16 +4,20 @@ add_task(async function() {
 
   // After reload() we are getting getSources notifiction for old sources,
   // using the debugger statement to really stop are reloaded page.
   await waitForPaused(dbg);
   await resume(dbg);
 
   await waitForSources(dbg, "doc-asm.html", "asm.js");
 
+  // Expand nodes and make sure more sources appear.
+  is(findAllElements(dbg, "sourceNodes").length, 2);
+
+  await clickElement(dbg, "sourceDirectoryLabel", 2);
   is(findAllElements(dbg, "sourceNodes").length, 4);
 
   await selectSource(dbg, "asm.js");
 
   await addBreakpoint(dbg, "asm.js", 7);
   invokeInTab("runAsm");
 
   await waitForPaused(dbg);
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-sources.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-sources.js
@@ -19,33 +19,16 @@ async function assertSourceCount(dbg, co
 function getLabel(dbg, index) {
   return findElement(dbg, "sourceNode", index)
     .textContent.trim()
     .replace(/^[\s\u200b]*/g, "");
 }
 
 add_task(async function() {
   const dbg = await initDebugger("doc-sources.html");
-  const { selectors: { getSelectedSource, getExpandedState }, getState } = dbg;
-
-  await waitForSources(dbg, "nested-source");
-  await selectSource(dbg, "nested-source");
-
-  const expanded = getExpandedState(getState());
-
-  ok(
-    expanded.has(
-      `example.com/browser/devtools/client/debugger/new/test/mochitest/examples/nested/nested/`
-    ),
-    "Nodes in path are automatically expanded"
-  );
-});
-
-add_task(async function() {
-  const dbg = await initDebugger("doc-sources.html");
   const { selectors: { getSelectedSource }, getState } = dbg;
 
   await waitForSources(dbg, "simple1", "simple2", "nested-source", "long.js");
 
   // Expand nodes and make sure more sources appear.
   await assertSourceCount(dbg, 2);
   await clickElement(dbg, "sourceDirectoryLabel", 2);
 
--- a/devtools/client/themes/images/debugger/typescript.svg
+++ b/devtools/client/themes/images/debugger/typescript.svg
@@ -1,6 +1,6 @@
 <!-- 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/. -->
-<svg viewBox="0 0 128 128">
-<path xmlns="http://www.w3.org/2000/svg" class="cls-2" id="original-2" d="M 1.5 63.91 v 62.5 h 125 V 1.41 H 1.5 Z m 100.73 -5 a 15.56 15.56 0 0 1 7.82 4.5 a 20.58 20.58 0 0 1 3 4 c 0 0.16 -5.4 3.81 -8.69 5.85 c -0.12 0.08 -0.6 -0.44 -1.13 -1.23 a 7.09 7.09 0 0 0 -5.87 -3.53 c -3.79 -0.26 -6.23 1.73 -6.21 5 a 4.58 4.58 0 0 0 0.54 2.34 c 0.83 1.73 2.38 2.76 7.24 4.86 c 8.95 3.85 12.78 6.39 15.16 10 c 2.66 4 3.25 10.46 1.45 15.24 c -2 5.2 -6.9 8.73 -13.83 9.9 a 38.32 38.32 0 0 1 -9.52 -0.1 a 23 23 0 0 1 -12.72 -6.63 c -1.15 -1.27 -3.39 -4.58 -3.25 -4.82 a 9.34 9.34 0 0 1 1.15 -0.73 L 82 101 l 3.59 -2.08 l 0.75 1.11 a 16.78 16.78 0 0 0 4.74 4.54 c 4 2.1 9.46 1.81 12.16 -0.62 a 5.43 5.43 0 0 0 0.69 -6.92 c -1 -1.39 -3 -2.56 -8.59 -5 c -6.45 -2.78 -9.23 -4.5 -11.77 -7.24 a 16.48 16.48 0 0 1 -3.43 -6.25 a 25 25 0 0 1 -0.22 -8 c 1.33 -6.23 6 -10.58 12.82 -11.87 A 31.66 31.66 0 0 1 102.23 58.93 Z M 72.89 64.15 l 0 5.12 H 56.66 V 115.5 H 45.15 V 69.26 H 28.88 v -5 A 49.19 49.19 0 0 1 29 59.09 C 29.08 59 39 59 51 59 L 72.83 59 Z" data-name="original" />
-</svg> 
\ No newline at end of file
+<svg viewBox="0 0 128 128" xmlns="http://www.w3.org/2000/svg">
+<path d="M 1.5 63.91 v 62.5 h 125 V 1.41 H 1.5 Z m 100.73 -5 a 15.56 15.56 0 0 1 7.82 4.5 a 20.58 20.58 0 0 1 3 4 c 0 0.16 -5.4 3.81 -8.69 5.85 c -0.12 0.08 -0.6 -0.44 -1.13 -1.23 a 7.09 7.09 0 0 0 -5.87 -3.53 c -3.79 -0.26 -6.23 1.73 -6.21 5 a 4.58 4.58 0 0 0 0.54 2.34 c 0.83 1.73 2.38 2.76 7.24 4.86 c 8.95 3.85 12.78 6.39 15.16 10 c 2.66 4 3.25 10.46 1.45 15.24 c -2 5.2 -6.9 8.73 -13.83 9.9 a 38.32 38.32 0 0 1 -9.52 -0.1 a 23 23 0 0 1 -12.72 -6.63 c -1.15 -1.27 -3.39 -4.58 -3.25 -4.82 a 9.34 9.34 0 0 1 1.15 -0.73 L 82 101 l 3.59 -2.08 l 0.75 1.11 a 16.78 16.78 0 0 0 4.74 4.54 c 4 2.1 9.46 1.81 12.16 -0.62 a 5.43 5.43 0 0 0 0.69 -6.92 c -1 -1.39 -3 -2.56 -8.59 -5 c -6.45 -2.78 -9.23 -4.5 -11.77 -7.24 a 16.48 16.48 0 0 1 -3.43 -6.25 a 25 25 0 0 1 -0.22 -8 c 1.33 -6.23 6 -10.58 12.82 -11.87 A 31.66 31.66 0 0 1 102.23 58.93 Z M 72.89 64.15 l 0 5.12 H 56.66 V 115.5 H 45.15 V 69.26 H 28.88 v -5 A 49.19 49.19 0 0 1 29 59.09 C 29.08 59 39 59 51 59 L 72.83 59 Z" data-name="original" />
+</svg>