Merge inbound to mozilla-central. a=merge
authorCiure Andrei <aciure@mozilla.com>
Sun, 02 Dec 2018 11:46:09 +0200
changeset 508378 8e021c409c6a16d9bab9713d20e30fa05c5b8365
parent 508335 93bdaf4be8e4042cadf8fddcc20d43155622474c (current diff)
parent 508377 0ad042243cf0ea701257465d72e780d4dbfc0225 (diff)
child 508379 08e30fbc9ab1de03a15011808497ac373683ef25
child 508380 f23d8b5068f7905a1f708c7aa1037ccb75943610
child 508415 adb3cb65badb25f414c3b3eedc4f2f5d8ec7cf29
push id1905
push userffxbld-merge
push dateMon, 21 Jan 2019 12:33:13 +0000
treeherdermozilla-release@c2fca1944d8c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone65.0a1
first release with
nightly linux32
8e021c409c6a / 65.0a1 / 20181202094648 / files
nightly linux64
8e021c409c6a / 65.0a1 / 20181202094648 / files
nightly mac
8e021c409c6a / 65.0a1 / 20181202094648 / files
nightly win32
8e021c409c6a / 65.0a1 / 20181202094648 / files
nightly win64
8e021c409c6a / 65.0a1 / 20181202094648 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central. a=merge
devtools/client/debugger/new/images/column-marker.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 105
+Version 106
 
-Comparison: https://github.com/devtools-html/debugger.html/compare/release-104...release-105
+Comparison: https://github.com/devtools-html/debugger.html/compare/release-105...release-106
 
 Packages:
 - babel-plugin-transform-es2015-modules-commonjs @6.26.2
 - babel-preset-react @6.24.1
 - react @16.4.1
 - react-dom @16.4.1
 - webpack @3.12.0
--- a/devtools/client/debugger/new/dist/debugger.css
+++ b/devtools/client/debugger/new/dist/debugger.css
@@ -431,17 +431,18 @@ html[dir="rtl"] .tree-node button.arrow 
 }
 
 /* Focused styles */
 .tree.object-inspector .tree-node.focused * {
   color: inherit;
 }
 
 .tree-node.focused button.jump-definition,
-.tree-node.focused button.open-inspector {
+.tree-node.focused button.open-inspector,
+.tree-node.focused button.invoke-getter {
   background-color: currentColor;
 }
 .A11y-mouse :focus {
   outline: 0;
 }
 /* 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/>. */
@@ -2307,26 +2308,31 @@ menuseparator {
   background: var(--theme-body-color);
 }
 
 .source-footer > .commands > .blackboxed > img.blackBox {
   background: var(--theme-highlight-blue);
 }
 
 .source-footer .blackbox-summary,
-.source-footer .mapped-source {
+.source-footer .mapped-source,
+.source-footer .cursor-position {
   color: var(--theme-body-color);
   padding-left: 2.5px;
 }
 
 .source-footer .mapped-source {
   white-space: nowrap;
   overflow: hidden;
   text-overflow: ellipsis;
 }
+
+.source-footer .cursor-position {
+  padding: 5px;
+}
 /* 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/>. */
 
 .search-bar {
   display: flex;
   border: 1px solid transparent;
   border-top: 1px solid var(--theme-splitter-color);
@@ -2680,52 +2686,47 @@ menuseparator {
   font-size: 14px;
   color: var(--theme-comment);
 }
 /* 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/>. */
 
 .call-site {
-  position: relative;
+  display: inline;
+}
+
+.call-site svg {
   display: inline;
   cursor: pointer;
-}
-
-.call-site::before {
-  content: "";
-  mask: url("resource://devtools/client/debugger/new/images/column-marker.svg") no-repeat 100% 100%;
-  mask-size: contain;
-  display: inline-block;
-  background-color: var(--blue-55);
-  opacity: 0.5;
+  height: 12px;
   width: 9px;
-  height: 12px;
-}
-
-.call-site.active::before {
-  opacity: 1;
-}
-
-.theme-dark .call-site {
-  border-bottom: none;
-}
-
-.theme-dark .call-site-bp {
-  border-bottom: none;
-}
-
-.theme-dark .call-site::before {
-  background-color: var(--blue-60);
-  opacity: 1;
-}
-
-.theme-dark .call-site-bp::before {
-  background-color: var(--blue-50);
-  opacity: 0.5;
+  vertical-align: top;
+}
+
+.call-site.active svg {
+  fill: var(--blue-50);
+  stroke: var(--blue-60);
+}
+
+.call-site.disabled svg {
+  fill: var(--blue-50);
+  stroke: var(--blue-40);
+  fill-opacity: 0.5;
+}
+
+.theme-dark .call-site.active svg {
+  fill: var(--blue-55);
+  stroke: var(--blue-40);
+}
+
+.theme-dark .call-site.disabled svg {
+  fill: var(--blue-50);
+  stroke: var(--blue-60);
+  fill-opacity: 0.5;
 }
 /* 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/>. */
 
 .conditional-breakpoint-panel {
   cursor: initial;
   margin: 1em 0;
@@ -2809,20 +2810,28 @@ menuseparator {
 }
 
 html[dir="rtl"] .editor-mount {
   direction: ltr;
 }
 
 .theme-light {
   --gutter-hover-background-color: #dde1e4;
+  --breakpoint-fill: var(--blue-50);
+  --breakpoint-stroke: var(--blue-60);
+  --breakpoint-fill-disabled: var(--blue-50);
+  --breakpoint-stroke-disabled: var(--blue-40);
 }
 
 .theme-dark {
   --gutter-hover-background-color: #414141;
+  --breakpoint-fill: var(--blue-55);
+  --breakpoint-stroke: var(--blue-40);
+  --breakpoint-fill-disabled: var(--blue-50);
+  --breakpoint-stroke-disabled: var(--blue-60);
 }
 
 :not(.empty-line):not(.new-breakpoint)
   > .CodeMirror-gutter-wrapper:hover
   > .CodeMirror-linenumber {
   height: 13px;
   color: var(--theme-body-color);
   /* Add 1px offset to the background to match it
@@ -2867,17 +2876,18 @@ html[dir="rtl"] .editor-mount {
   height: 14px;
 }
 
 .editor-wrapper .highlight-lines {
   background: var(--theme-selection-background-hover);
 }
 
 .editor.new-breakpoint svg {
-  fill: var(--theme-selection-background);
+  fill: var(--breakpoint-fill);
+  stroke: var(--breakpoint-stroke);
   width: 60px;
   height: 14px;
   position: absolute;
   top: 0px;
   right: -4px;
 }
 
 .inline-bp {
@@ -2894,22 +2904,25 @@ html[dir="rtl"] .editor-mount {
   position: absolute;
   right: -2px;
 }
 
 .editor.new-breakpoint.folding-enabled svg {
   right: -16px;
 }
 
-.new-breakpoint.has-condition svg {
+.new-breakpoint.has-condition .CodeMirror-gutter-wrapper svg {
   fill: var(--theme-graphs-yellow);
+  stroke: var(--theme-graphs-orange);
 }
 
 .editor.new-breakpoint.breakpoint-disabled svg {
-  opacity: 0.3;
+  fill: var(--breakpoint-fill-disabled);
+  stroke: var(--breakpoint-stroke-disabled);
+  fill-opacity: 0.5;
 }
 
 .editor.column-breakpoint svg {
   fill: var(--theme-selection-background);
   vertical-align: middle;
   width: 17px;
   height: 14px;
 }
@@ -3624,65 +3637,16 @@ html[dir="rtl"] .breakpoints-list .break
 
 :root.theme-dark .annotation-logo:not(.angular) svg path {
   fill: var(--theme-highlight-blue);
 }
 /* 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/>. */
 
-.event-listeners {
-  list-style: none;
-  margin: 0;
-  padding: 0;
-}
-
-.event-listeners .listener {
-  padding: 7px 10px 7px 21px;
-  clear: both;
-  overflow: hidden;
-}
-
-.event-listeners .listener * {
-  -moz-user-select: none;
-  user-select: none;
-}
-
-.event-listeners .listener:nth-of-type(2n) {
-  background-color: var(--theme-tab-toolbar-background);
-}
-
-.event-listeners .listener .type {
-  color: var(--theme-highlight-bluegrey);
-  padding-right: 5px;
-}
-
-.event-listeners .listener .selector {
-  color: var(--theme-content-color2);
-}
-
-.event-listeners .listener-checkbox {
-  margin-left: 0;
-}
-
-.event-listeners .listener .close-btn {
-  float: right;
-}
-
-.event-listeners .listener .close {
-  display: none;
-}
-
-.event-listeners .listener:hover .close {
-  display: block;
-}
-/* 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/>. */
-
 .workers-list * {
   user-select: none;
 }
 
 .workers-list .worker {
   font-size: 1rem;
   color: var(--theme-content-color1);
   padding: 0 1em;
--- a/devtools/client/debugger/new/dist/parser-worker.js
+++ b/devtools/client/debugger/new/dist/parser-worker.js
@@ -1003,47 +1003,43 @@ Object.defineProperty(exports, "__esModu
   value: true
 });
 exports.isFunction = isFunction;
 exports.isAwaitExpression = isAwaitExpression;
 exports.isYieldExpression = isYieldExpression;
 exports.isObjectShorthand = isObjectShorthand;
 exports.getObjectExpressionValue = getObjectExpressionValue;
 exports.getCode = getCode;
-exports.getVariableNames = getVariableNames;
 exports.getComments = getComments;
 exports.getSpecifiers = getSpecifiers;
-exports.isVariable = isVariable;
 exports.isComputedExpression = isComputedExpression;
 exports.getMemberExpression = getMemberExpression;
 exports.getVariables = getVariables;
 exports.getPatternIdentifiers = getPatternIdentifiers;
 exports.isTopLevel = isTopLevel;
 
 var _types = __webpack_require__(2268);
 
 var t = _interopRequireWildcard(_types);
 
 var _generator = __webpack_require__(2365);
 
 var _generator2 = _interopRequireDefault(_generator);
 
-var _flatten = __webpack_require__(706);
-
-var _flatten2 = _interopRequireDefault(_flatten);
-
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
 function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
 
+/* 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 isFunction(node) {
   return t.isFunction(node) || t.isArrowFunctionExpression(node) || t.isObjectMethod(node) || t.isClassMethod(node);
-} /* 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 isAwaitExpression(path) {
   const { node, parent } = path;
   return t.isAwaitExpression(node) || t.isAwaitExpression(parent.init) || t.isAwaitExpression(parent);
 }
 
 function isYieldExpression(path) {
   const { node, parent } = path;
@@ -1069,47 +1065,16 @@ function getObjectExpressionValue(node) 
   const shouldWrap = t.isObjectExpression(value);
   return shouldWrap ? `(${code})` : code;
 }
 
 function getCode(node) {
   return (0, _generator2.default)(node).code;
 }
 
-function getVariableNames(path) {
-  if (t.isObjectProperty(path.node) && !isFunction(path.node.value)) {
-    if (path.node.key.type === "StringLiteral") {
-      return [{
-        name: path.node.key.value,
-        location: path.node.loc
-      }];
-    } else if (path.node.value.type === "Identifier") {
-      return [{ name: path.node.value.name, location: path.node.loc }];
-    } else if (path.node.value.type === "AssignmentPattern") {
-      return [{ name: path.node.value.left.name, location: path.node.loc }];
-    }
-
-    return [{
-      name: path.node.key.name,
-      location: path.node.loc
-    }];
-  }
-
-  if (!path.node.declarations) {
-    return path.node.params.map(dec => ({
-      name: dec.name,
-      location: dec.loc
-    }));
-  }
-
-  const declarations = path.node.declarations.filter(dec => dec.id.type !== "ObjectPattern").map(getVariables);
-
-  return (0, _flatten2.default)(declarations);
-}
-
 function getComments(ast) {
   if (!ast || !ast.comments) {
     return [];
   }
   return ast.comments.map(comment => ({
     name: comment.location,
     location: comment.loc
   }));
@@ -1118,21 +1083,16 @@ function getComments(ast) {
 function getSpecifiers(specifiers) {
   if (!specifiers) {
     return [];
   }
 
   return specifiers.map(specifier => specifier.local && specifier.local.name);
 }
 
-function isVariable(path) {
-  const node = path.node;
-  return t.isVariableDeclaration(node) || isFunction(path) && path.node.params != null && path.node.params.length || t.isObjectProperty(node) && !isFunction(path.node.value);
-}
-
 function isComputedExpression(expression) {
   return (/^\[/m.test(expression)
   );
 }
 
 function getMemberExpression(root) {
   function _getMemberExpression(node, expr) {
     if (t.isMemberExpression(node)) {
@@ -1375,20 +1335,16 @@ function getFunctionParameterNames(path)
       }
     });
   }
   return [];
 }
 
 /* eslint-disable complexity */
 function extractSymbol(path, symbols) {
-  if ((0, _helpers.isVariable)(path)) {
-    symbols.variables.push(...(0, _helpers.getVariableNames)(path));
-  }
-
   if ((0, _helpers.isFunction)(path)) {
     symbols.functions.push({
       name: (0, _getFunctionName2.default)(path.node, path.parent),
       klass: (0, _inferClassName.inferClassName)(path),
       location: path.node.loc,
       parameterNames: getFunctionParameterNames(path),
       identifier: path.node.id
     });
@@ -1527,17 +1483,16 @@ function extractSymbol(path, symbols) {
   }
 }
 
 /* eslint-enable complexity */
 
 function extractSymbols(sourceId) {
   const symbols = {
     functions: [],
-    variables: [],
     callExpressions: [],
     memberExpressions: [],
     objectProperties: [],
     comments: [],
     identifiers: [],
     classes: [],
     imports: [],
     literals: [],
@@ -22967,25 +22922,27 @@ function onEnter(node, ancestors, state)
     return addEmptyPoint(state, startLocation);
   }
 }
 
 function hasPoint(state, { line, column }) {
   return state[line] && state[line][column];
 }
 
-function addPoint(state, { line, column }, types) {
+function addPoint(state, location, types) {
   if (typeof types === "boolean") {
     types = { step: types, break: types };
   }
 
+  const { line, column } = location;
+
   if (!state[line]) {
     state[line] = {};
   }
-  state[line][column] = types;
+  state[line][column] = { types, location };
   return state;
 }
 
 function addStopPoint(state, location) {
   return addPoint(state, location, { break: true, step: true });
 }
 
 function addEmptyPoint(state, location) {
@@ -46583,117 +46540,16 @@ module.exports = Symbol;
  */
 var isArray = Array.isArray;
 
 module.exports = isArray;
 
 
 /***/ }),
 
-/***/ 706:
-/***/ (function(module, exports, __webpack_require__) {
-
-var baseFlatten = __webpack_require__(707);
-
-/**
- * Flattens `array` a single level deep.
- *
- * @static
- * @memberOf _
- * @since 0.1.0
- * @category Array
- * @param {Array} array The array to flatten.
- * @returns {Array} Returns the new flattened array.
- * @example
- *
- * _.flatten([1, [2, [3, [4]], 5]]);
- * // => [1, 2, [3, [4]], 5]
- */
-function flatten(array) {
-  var length = array == null ? 0 : array.length;
-  return length ? baseFlatten(array, 1) : [];
-}
-
-module.exports = flatten;
-
-
-/***/ }),
-
-/***/ 707:
-/***/ (function(module, exports, __webpack_require__) {
-
-var arrayPush = __webpack_require__(287),
-    isFlattenable = __webpack_require__(708);
-
-/**
- * The base implementation of `_.flatten` with support for restricting flattening.
- *
- * @private
- * @param {Array} array The array to flatten.
- * @param {number} depth The maximum recursion depth.
- * @param {boolean} [predicate=isFlattenable] The function invoked per iteration.
- * @param {boolean} [isStrict] Restrict to values that pass `predicate` checks.
- * @param {Array} [result=[]] The initial result value.
- * @returns {Array} Returns the new flattened array.
- */
-function baseFlatten(array, depth, predicate, isStrict, result) {
-  var index = -1,
-      length = array.length;
-
-  predicate || (predicate = isFlattenable);
-  result || (result = []);
-
-  while (++index < length) {
-    var value = array[index];
-    if (depth > 0 && predicate(value)) {
-      if (depth > 1) {
-        // Recursively flatten arrays (susceptible to call stack limits).
-        baseFlatten(value, depth - 1, predicate, isStrict, result);
-      } else {
-        arrayPush(result, value);
-      }
-    } else if (!isStrict) {
-      result[result.length] = value;
-    }
-  }
-  return result;
-}
-
-module.exports = baseFlatten;
-
-
-/***/ }),
-
-/***/ 708:
-/***/ (function(module, exports, __webpack_require__) {
-
-var Symbol = __webpack_require__(7),
-    isArguments = __webpack_require__(208),
-    isArray = __webpack_require__(70);
-
-/** Built-in value references. */
-var spreadableSymbol = Symbol ? Symbol.isConcatSpreadable : undefined;
-
-/**
- * Checks if `value` is a flattenable `arguments` object or array.
- *
- * @private
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is flattenable, else `false`.
- */
-function isFlattenable(value) {
-  return isArray(value) || isArguments(value) ||
-    !!(spreadableSymbol && value && value[spreadableSymbol]);
-}
-
-module.exports = isFlattenable;
-
-
-/***/ }),
-
 /***/ 71:
 /***/ (function(module, exports, __webpack_require__) {
 
 var isArray = __webpack_require__(70),
     isSymbol = __webpack_require__(72);
 
 /** Used to match property names within property paths. */
 var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,
--- a/devtools/client/debugger/new/dist/vendors.js
+++ b/devtools/client/debugger/new/dist/vendors.js
@@ -4547,17 +4547,17 @@ var classNamesShape = exports.classNames
 
 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=\"-1 73 16 11\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"><g id=\"Shape-Copy-3-+-Shape-Copy-4\" stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(0.000000, 74.000000)\"><path d=\"M0.749321284,4.16081709 L4.43130681,0.242526751 C4.66815444,-0.00952143591 5.06030999,-0.0211407611 5.30721074,0.216574262 C5.55411149,0.454289284 5.56226116,0.851320812 5.32541353,1.103369 L1.95384971,4.69131519 L5.48809879,8.09407556 C5.73499955,8.33179058 5.74314922,8.72882211 5.50630159,8.9808703 C5.26945396,9.23291849 4.87729841,9.24453781 4.63039766,9.00682279 L0.827097345,5.34502101 C0.749816996,5.31670099 0.677016974,5.27216098 0.613753508,5.21125118 C0.427367989,5.03179997 0.377040713,4.7615583 0.465458792,4.53143559 C0.492371834,4.43667624 0.541703274,4.34676528 0.613628034,4.27022448 C0.654709457,4.22650651 0.70046335,4.19002189 0.749321284,4.16081709 Z\" id=\"Shape-Copy-3\" stroke=\"#FFFFFF\" stroke-width=\"0.05\" fill=\"#DDE1E4\"></path><path d=\"M13.7119065,5.44453032 L9.77062746,9.09174784 C9.51677479,9.3266604 9.12476399,9.31089603 8.89504684,9.05653714 C8.66532968,8.80217826 8.68489539,8.40554539 8.93874806,8.17063283 L12.5546008,4.82456128 L9.26827469,1.18571135 C9.03855754,0.931352463 9.05812324,0.534719593 9.31197591,0.299807038 C9.56582858,0.0648944831 9.95783938,0.0806588502 10.1875565,0.335017737 L13.72891,4.25625178 C13.8013755,4.28980469 13.8684335,4.3382578 13.9254821,4.40142604 C14.0883019,4.58171146 14.1258883,4.83347168 14.0435812,5.04846202 C14.0126705,5.15680232 13.9526426,5.2583679 13.8641331,5.34027361 C13.8174417,5.38348136 13.7660763,5.41820853 13.7119065,5.44453032 Z\" id=\"Shape-Copy-4\" stroke=\"#FFFFFF\" stroke-width=\"0.05\" fill=\"#DDE1E4\"></path></g></svg>"
 
 /***/ }),
 
 /***/ 348:
 /***/ (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 16 16\"><path d=\"M8 13.4c-.5 0-.9-.2-1.2-.6L.4 5.2C0 4.7-.1 4.3.2 3.7S1 3 1.6 3h12.8c.6 0 1.2.1 1.4.7.3.6.2 1.1-.2 1.6l-6.4 7.6c-.3.4-.7.5-1.2.5z\"></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 xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" viewBox=\"0 0 16 16\" fill=\"context-fill #9B9B9B\"><path d=\"M8 13.4c-.5 0-.9-.2-1.2-.6L.4 5.2C0 4.7-.1 4.3.2 3.7S1 3 1.6 3h12.8c.6 0 1.2.1 1.4.7.3.6.2 1.1-.2 1.6l-6.4 7.6c-.3.4-.7.5-1.2.5z\"></path></svg>"
 
 /***/ }),
 
 /***/ 349:
 /***/ (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 16 16\" xmlns=\"http://www.w3.org/2000/svg\"><g fill-rule=\"evenodd\"><circle cx=\"8\" cy=\"8.5\" r=\"1.5\"></circle><path d=\"M15.498 8.28l-.001-.03v-.002-.004l-.002-.018-.004-.031c0-.002 0-.002 0 0l-.004-.035.006.082c-.037-.296-.133-.501-.28-.661-.4-.522-.915-1.042-1.562-1.604-1.36-1.182-2.74-1.975-4.178-2.309a6.544 6.544 0 0 0-2.755-.042c-.78.153-1.565.462-2.369.91C3.252 5.147 2.207 6 1.252 7.035c-.216.233-.36.398-.499.577-.338.437-.338 1 0 1.437.428.552.941 1.072 1.59 1.635 1.359 1.181 2.739 1.975 4.177 2.308.907.21 1.829.223 2.756.043.78-.153 1.564-.462 2.369-.91 1.097-.612 2.141-1.464 3.097-2.499.217-.235.36-.398.498-.578.12-.128.216-.334.248-.554 0 .01 0 .01-.008.04l.013-.079-.001.011.003-.031.001-.017v.005l.001-.02v.008l.002-.03.001-.05-.001-.044v-.004-.004zm-.954.045v.007l.001.004V8.33v.012l-.001.01v-.005-.005l.002-.015-.001.008c-.002.014-.002.014 0 0l-.007.084c.003-.057-.004-.041-.014-.031-.143.182-.27.327-.468.543-.89.963-1.856 1.752-2.86 2.311-.724.404-1.419.677-2.095.81a5.63 5.63 0 0 1-2.374-.036c-1.273-.295-2.523-1.014-3.774-2.101-.604-.525-1.075-1.001-1.457-1.496-.054-.07-.054-.107 0-.177.117-.152.244-.298.442-.512.89-.963 1.856-1.752 2.86-2.311.724-.404 1.419-.678 2.095-.81a5.631 5.631 0 0 1 2.374.036c1.272.295 2.523 1.014 3.774 2.101.603.524 1.074 1 1.457 1.496.035.041.043.057.046.076 0 .01 0 .01.008.043l-.009-.047.003.02-.002-.013v-.008.016c0-.004 0-.004 0 0v-.004z\"></path></g></svg>"
 
@@ -8428,17 +8428,17 @@ function createStructuredSelector(select
   });
 }
 
 /***/ }),
 
 /***/ 3801:
 /***/ (function(module, exports) {
 
-module.exports = "<svg viewBox=\"0 0 9 12\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"><g id=\"columnmarkergroup\" stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\"><polygon id=\"columnmarker\" fill=\"#1B1B1D\" points=\"0 0 4 0 9 6 4 12 0 12\"></polygon></g></svg>"
+module.exports = "<svg viewBox=\"0 0 9 12\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"><g id=\"columnmarkergroup\" stroke-width=\"1\" fill-rule=\"evenodd\"><polygon id=\"columnmarker\" points=\"0 0 4 0 9 6 4 12 0 12\"></polygon></g></svg>"
 
 /***/ }),
 
 /***/ 3802:
 /***/ (function(module, exports, __webpack_require__) {
 
 const SplitBox = __webpack_require__(3803);
 
--- a/devtools/client/debugger/new/images/arrow.svg
+++ b/devtools/client/debugger/new/images/arrow.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 xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 16 16">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 16 16" fill="context-fill #9B9B9B">
   <path d="M8 13.4c-.5 0-.9-.2-1.2-.6L.4 5.2C0 4.7-.1 4.3.2 3.7S1 3 1.6 3h12.8c.6 0 1.2.1 1.4.7.3.6.2 1.1-.2 1.6l-6.4 7.6c-.3.4-.7.5-1.2.5z"/>
 </svg>
\ No newline at end of file
deleted file mode 100644
--- a/devtools/client/debugger/new/images/column-marker.svg
+++ /dev/null
@@ -1,5 +0,0 @@
-<svg width="9px" height="12px" viewBox="0 0 9 12" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
-    <g id="columnmarkergroup" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
-        <polygon id="columnmarker" fill="#1B1B1D" points="0 0 4 0 9 6 4 12 0 12"></polygon>
-    </g>
-</svg>
\ No newline at end of file
--- a/devtools/client/debugger/new/images/moz.build
+++ b/devtools/client/debugger/new/images/moz.build
@@ -7,17 +7,16 @@
 
 DevToolsModules(
     'angular.svg',
     'arrow.svg',
     'blackBox.svg',
     'breakpoint.svg',
     'close.svg',
     'coffeescript.svg',
-    'column-marker.svg',
     'command-chevron.svg',
     'disable-pausing.svg',
     'domain.svg',
     'extension.svg',
     'file.svg',
     'folder.svg',
     'help.svg',
     'javascript.svg',
--- a/devtools/client/debugger/new/src/actions/ast.js
+++ b/devtools/client/debugger/new/src/actions/ast.js
@@ -13,27 +13,27 @@ import {
 } from "../selectors";
 
 import { mapFrames, fetchExtra } from "./pause";
 import { updateTab } from "./tabs";
 
 import { PROMISE } from "./utils/middleware/promise";
 
 import { setInScopeLines } from "./ast/setInScopeLines";
-import { updateSymbolLocations } from "./utils/symbols";
+import { setPausePoints } from "./ast/setPausePoints";
+export { setPausePoints };
+
 import {
   getSymbols,
   findOutOfScopeLocations,
   getFramework,
-  getPausePoints,
   type AstPosition
 } from "../workers/parser";
 
-import { features } from "../utils/prefs";
-import { isLoaded, isGenerated } from "../utils/source";
+import { isLoaded } from "../utils/source";
 
 import type { SourceId } from "../types";
 import type { ThunkArgs, Action } from "./types";
 
 export function setSourceMetaData(sourceId: SourceId) {
   return async ({ dispatch, getState }: ThunkArgs) => {
     const source = getSource(getState(), sourceId);
     if (!source || !isLoaded(source) || source.isWasm) {
@@ -63,25 +63,17 @@ export function setSymbols(sourceId: Sou
 
     if (source.isWasm || hasSymbols(getState(), source) || !isLoaded(source)) {
       return;
     }
 
     await dispatch({
       type: "SET_SYMBOLS",
       sourceId,
-      [PROMISE]: (async function() {
-        const symbols = await getSymbols(sourceId);
-        const mappedSymbols = updateSymbolLocations(
-          symbols,
-          source,
-          sourceMaps
-        );
-        return mappedSymbols;
-      })()
+      [PROMISE]: getSymbols(sourceId)
     });
 
     if (isPaused(getState())) {
       await dispatch(fetchExtra());
       await dispatch(mapFrames());
     }
 
     await dispatch(setPausePoints(sourceId));
@@ -110,50 +102,8 @@ export function setOutOfScopeLocations()
       ({
         type: "OUT_OF_SCOPE_LOCATIONS",
         locations
       }: Action)
     );
     dispatch(setInScopeLines());
   };
 }
-
-function compressPausePoints(pausePoints) {
-  const compressed = {};
-  for (const line in pausePoints) {
-    compressed[line] = {};
-    for (const col in pausePoints[line]) {
-      const point = pausePoints[line][col];
-      compressed[line][col] = (point.break ? 1 : 0) | (point.step ? 2 : 0);
-    }
-  }
-
-  return compressed;
-}
-
-export function setPausePoints(sourceId: SourceId) {
-  return async ({ dispatch, getState, client }: ThunkArgs) => {
-    const source = getSourceFromId(getState(), sourceId);
-    if (!features.pausePoints || !source || !source.text) {
-      return;
-    }
-
-    if (source.isWasm) {
-      return;
-    }
-
-    const pausePoints = await getPausePoints(sourceId);
-    const compressed = compressPausePoints(pausePoints);
-
-    if (isGenerated(source)) {
-      await client.setPausePoints(sourceId, compressed);
-    }
-
-    dispatch(
-      ({
-        type: "SET_PAUSE_POINTS",
-        sourceText: source.text || "",
-        sourceId,
-        pausePoints
-      }: Action)
-    );
-  };
-}
--- a/devtools/client/debugger/new/src/actions/ast/moz.build
+++ b/devtools/client/debugger/new/src/actions/ast/moz.build
@@ -4,9 +4,10 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DIRS += [
 
 ]
 
 DebuggerModules(
     'setInScopeLines.js',
+    'setPausePoints.js',
 )
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/src/actions/ast/setPausePoints.js
@@ -0,0 +1,66 @@
+/* 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/>. */
+
+import { getSourceFromId } from "../../selectors";
+import * as parser from "../../workers/parser";
+import { isGenerated } from "../../utils/source";
+import { mapPausePoints } from "../../utils/pause/pausePoints";
+import { features } from "../../utils/prefs";
+
+import type { SourceId } from "../types";
+import type { ThunkArgs, Action } from "./types";
+
+function compressPausePoints(pausePoints) {
+  const compressed = {};
+  for (const line in pausePoints) {
+    compressed[line] = {};
+    for (const col in pausePoints[line]) {
+      const { types } = pausePoints[line][col];
+      compressed[line][col] = (types.break ? 1 : 0) | (types.step ? 2 : 0);
+    }
+  }
+
+  return compressed;
+}
+
+async function mapLocations(pausePoints, source, sourceMaps) {
+  const sourceId = source.id;
+  return mapPausePoints(pausePoints, async ({ types, location }) => {
+    const generatedLocation = await sourceMaps.getGeneratedLocation(
+      { ...location, sourceId },
+      source
+    );
+
+    return { types, location, generatedLocation };
+  });
+}
+export function setPausePoints(sourceId: SourceId) {
+  return async ({ dispatch, getState, client, sourceMaps }: ThunkArgs) => {
+    const source = getSourceFromId(getState(), sourceId);
+    if (!features.pausePoints || !source || !source.text) {
+      return;
+    }
+
+    if (source.isWasm) {
+      return;
+    }
+
+    let pausePoints = await parser.getPausePoints(sourceId);
+    pausePoints = await mapLocations(pausePoints, source, sourceMaps);
+
+    if (isGenerated(source)) {
+      const compressed = compressPausePoints(pausePoints);
+      await client.setPausePoints(sourceId, compressed);
+    }
+
+    dispatch(
+      ({
+        type: "SET_PAUSE_POINTS",
+        sourceText: source.text || "",
+        sourceId,
+        pausePoints
+      }: Action)
+    );
+  };
+}
--- a/devtools/client/debugger/new/src/actions/breakpoints/addBreakpoint.js
+++ b/devtools/client/debugger/new/src/actions/breakpoints/addBreakpoint.js
@@ -89,31 +89,31 @@ async function addBreakpointPromise(getS
 
 /**
  * Add a new hidden breakpoint
  *
  * @memberOf actions/breakpoints
  * @param location
  * @return {function(ThunkArgs)}
  */
-export function addHiddenBreakpoint(location: Location) {
+export function addHiddenBreakpoint(location: SourceLocation) {
   return ({ dispatch }: ThunkArgs) => {
     return dispatch(addBreakpoint(location, { hidden: true }));
   };
 }
 
 /**
  * Enabling a breakpoint
  * will reuse the existing breakpoint information that is stored.
  *
  * @memberof actions/breakpoints
  * @static
- * @param {Location} $1.location Location  value
+ * @param {SourceLocation} $1.location Location  value
  */
-export function enableBreakpoint(location: Location) {
+export function enableBreakpoint(location: SourceLocation) {
   return async ({ dispatch, getState, client, sourceMaps }: ThunkArgs) => {
     const breakpoint = getBreakpoint(getState(), location);
     if (!breakpoint || breakpoint.loading) {
       return;
     }
 
     // To instantly reflect in the UI, we optimistically enable the breakpoint
     const enabledBreakpoint = {
@@ -134,17 +134,17 @@ export function enableBreakpoint(locatio
  *
  * @memberof actions/breakpoints
  * @static
  * @param {String} $1.condition Conditional breakpoint condition value
  * @param {Boolean} $1.disabled Disable value for breakpoint value
  */
 
 export function addBreakpoint(
-  location: Location,
+  location: SourceLocation,
   { condition, hidden }: addBreakpointOptions = {}
 ) {
   const breakpoint = createBreakpoint(location, { condition, hidden });
   return ({ dispatch, getState, sourceMaps, client }: ThunkArgs) => {
     recordEvent("add_breakpoint");
 
     return dispatch({
       type: "ADD_BREAKPOINT",
--- a/devtools/client/debugger/new/src/actions/breakpoints/index.js
+++ b/devtools/client/debugger/new/src/actions/breakpoints/index.js
@@ -27,32 +27,32 @@ import {
 } from "./addBreakpoint";
 import remapLocations from "./remapLocations";
 import { syncBreakpoint } from "./syncBreakpoint";
 import { isEmptyLineInSource } from "../../reducers/ast";
 
 // this will need to be changed so that addCLientBreakpoint is removed
 
 import type { ThunkArgs, Action } from "../types";
-import type { Breakpoint, Location, XHRBreakpoint } from "../../types";
+import type { Breakpoint, SourceLocation, XHRBreakpoint } from "../../types";
 
 import { recordEvent } from "../../utils/telemetry";
 
 type addBreakpointOptions = {
   condition?: string,
   hidden?: boolean
 };
 
 /**
  * Remove a single breakpoint
  *
  * @memberof actions/breakpoints
  * @static
  */
-export function removeBreakpoint(location: Location) {
+export function removeBreakpoint(location: SourceLocation) {
   return ({ dispatch, getState, client }: ThunkArgs) => {
     const bp = getBreakpoint(getState(), location);
     if (!bp || bp.loading) {
       return;
     }
 
     recordEvent("remove_breakpoint");
 
@@ -79,17 +79,17 @@ export function removeBreakpoint(locatio
 }
 
 /**
  * Disable a single breakpoint
  *
  * @memberof actions/breakpoints
  * @static
  */
-export function disableBreakpoint(location: Location) {
+export function disableBreakpoint(location: SourceLocation) {
   return async ({ dispatch, getState, client }: ThunkArgs) => {
     const bp = getBreakpoint(getState(), location);
 
     if (!bp || bp.loading) {
       return;
     }
 
     await client.removeBreakpoint(bp.generatedLocation);
@@ -215,24 +215,24 @@ export function remapBreakpoints(sourceI
 }
 
 /**
  * Update the condition of a breakpoint.
  *
  * @throws {Error} "not implemented"
  * @memberof actions/breakpoints
  * @static
- * @param {Location} location
+ * @param {SourceLocation} location
  *        @see DebuggerController.Breakpoints.addBreakpoint
  * @param {string} condition
  *        The condition to set on the breakpoint
  * @param {Boolean} $1.disabled Disable value for breakpoint value
  */
 export function setBreakpointCondition(
-  location: Location,
+  location: SourceLocation,
   { condition }: addBreakpointOptions = {}
 ) {
   return async ({ dispatch, getState, client, sourceMaps }: ThunkArgs) => {
     const bp = getBreakpoint(getState(), location);
     if (!bp) {
       return dispatch(addBreakpoint(location, { condition }));
     }
 
--- a/devtools/client/debugger/new/src/actions/breakpoints/syncBreakpoint.js
+++ b/devtools/client/debugger/new/src/actions/breakpoints/syncBreakpoint.js
@@ -13,31 +13,31 @@ import {
 
 import { getGeneratedLocation } from "../../utils/source-maps";
 import { getTextAtPosition } from "../../utils/source";
 import { originalToGeneratedId, isOriginalId } from "devtools-source-map";
 import { getSource } from "../../selectors";
 import type { ThunkArgs, Action } from "../types";
 
 import type {
-  Location,
+  SourceLocation,
   ASTLocation,
   PendingBreakpoint,
   SourceId,
   Breakpoint
 } from "../../types";
 
 type BreakpointSyncData = {
-  previousLocation: Location,
+  previousLocation: SourceLocation,
   breakpoint: ?Breakpoint
 };
 
 async function makeScopedLocation(
   { name, offset }: ASTLocation,
-  location: Location,
+  location: SourceLocation,
   source
 ) {
   const scope = await findScopeByName(source, name);
   // fallback onto the location line, if the scope is not found
   // note: we may at some point want to delete the breakpoint if the scope
   // disappears
   const line = scope ? scope.location.start.line + offset.line : location.line;
   return {
@@ -46,19 +46,19 @@ async function makeScopedLocation(
     sourceUrl: source.url,
     sourceId: source.id
   };
 }
 
 function createSyncData(
   id: SourceId,
   pendingBreakpoint: PendingBreakpoint,
-  location: Location,
-  generatedLocation: Location,
-  previousLocation: Location,
+  location: SourceLocation,
+  generatedLocation: SourceLocation,
+  previousLocation: SourceLocation,
   text: string,
   originalText: string
 ): BreakpointSyncData {
   const overrides = {
     ...pendingBreakpoint,
     generatedLocation,
     id,
     text,
--- a/devtools/client/debugger/new/src/actions/index.js
+++ b/devtools/client/debugger/new/src/actions/index.js
@@ -1,17 +1,16 @@
 /* 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/>. */
 
 // @flow
 
 import * as breakpoints from "./breakpoints";
 import * as expressions from "./expressions";
-import * as eventListeners from "./event-listeners";
 import * as pause from "./pause";
 import * as navigation from "./navigation";
 import * as ui from "./ui";
 import * as fileSearch from "./file-search";
 import * as ast from "./ast";
 import * as projectTextSearch from "./project-text-search";
 import * as quickOpen from "./quick-open";
 import * as sourceTree from "./source-tree";
@@ -20,17 +19,16 @@ import * as tabs from "./tabs";
 import * as debuggee from "./debuggee";
 import * as toolbox from "./toolbox";
 import * as preview from "./preview";
 
 export default {
   ...navigation,
   ...breakpoints,
   ...expressions,
-  ...eventListeners,
   ...sources,
   ...tabs,
   ...pause,
   ...ui,
   ...fileSearch,
   ...ast,
   ...projectTextSearch,
   ...quickOpen,
--- a/devtools/client/debugger/new/src/actions/moz.build
+++ b/devtools/client/debugger/new/src/actions/moz.build
@@ -9,17 +9,16 @@ DIRS += [
     'pause',
     'sources',
     'utils',
 ]
 
 DebuggerModules(
     'ast.js',
     'debuggee.js',
-    'event-listeners.js',
     'expressions.js',
     'file-search.js',
     'index.js',
     'navigation.js',
     'preview.js',
     'project-text-search.js',
     'quick-open.js',
     'source-tree.js',
--- a/devtools/client/debugger/new/src/actions/preview.js
+++ b/devtools/client/debugger/new/src/actions/preview.js
@@ -17,17 +17,17 @@ import {
   getSelectedFrame,
   getSymbols
 } from "../selectors";
 
 import { getMappedExpression } from "./expressions";
 import { getExtra } from "./pause";
 
 import type { Action, ThunkArgs } from "./types";
-import type { ColumnPosition } from "../types";
+import type { Position } from "../types";
 import type { AstLocation } from "../workers/parser";
 
 function findExpressionMatch(state, codeMirror, tokenPos) {
   const source = getSelectedSource(state);
   if (!source) {
     return;
   }
 
@@ -70,17 +70,17 @@ export function updatePreview(
 
     dispatch(setPreview(expression, location, tokenPos, cursorPos));
   };
 }
 
 export function setPreview(
   expression: string,
   location: AstLocation,
-  tokenPos: ColumnPosition,
+  tokenPos: Position,
   cursorPos: ClientRect
 ) {
   return async ({ dispatch, getState, client, sourceMaps }: ThunkArgs) => {
     await dispatch({
       type: "SET_PREVIEW",
       [PROMISE]: (async function() {
         const source = getSelectedSource(getState());
         if (!source) {
--- a/devtools/client/debugger/new/src/actions/sources/newSources.js
+++ b/devtools/client/debugger/new/src/actions/sources/newSources.js
@@ -120,17 +120,22 @@ function checkSelectedSource(sourceId: s
 
     if (rawPendingUrl === source.url) {
       if (isPrettyURL(pendingUrl)) {
         const prettySource = await dispatch(togglePrettyPrint(source.id));
         return dispatch(checkPendingBreakpoints(prettySource.id));
       }
 
       await dispatch(
-        selectLocation({ ...pendingLocation, sourceId: source.id })
+        selectLocation({
+          sourceId: source.id,
+          line:
+            typeof pendingLocation.line === "number" ? pendingLocation.line : 0,
+          column: pendingLocation.column
+        })
       );
     }
   };
 }
 
 function checkPendingBreakpoints(sourceId: string) {
   return async ({ dispatch, getState }: ThunkArgs) => {
     // source may have been modified by selectLocation
--- a/devtools/client/debugger/new/src/actions/sources/select.js
+++ b/devtools/client/debugger/new/src/actions/sources/select.js
@@ -29,20 +29,23 @@ import {
   getSource,
   getSourceByURL,
   getPrettySource,
   getActiveSearch,
   getSelectedLocation,
   getSelectedSource
 } from "../../selectors";
 
-import type { Location, Position, Source } from "../../types";
+import type { SourceLocation, PartialPosition, Source } from "../../types";
 import type { ThunkArgs } from "../types";
 
-export const setSelectedLocation = (source: Source, location: Location) => ({
+export const setSelectedLocation = (
+  source: Source,
+  location: SourceLocation
+) => ({
   type: "SET_SELECTED_LOCATION",
   source,
   location
 });
 
 export const setPendingSelectedLocation = (url: string, options: Object) => ({
   type: "SET_PENDING_SELECTED_LOCATION",
   url: url,
@@ -59,17 +62,20 @@ export const clearSelectedLocation = () 
  * yet.
  *
  * This exists mostly for external things to interact with the
  * debugger.
  *
  * @memberof actions/sources
  * @static
  */
-export function selectSourceURL(url: string, options: Position = { line: 1 }) {
+export function selectSourceURL(
+  url: string,
+  options: PartialPosition = { line: 1 }
+) {
   return async ({ dispatch, getState, sourceMaps }: ThunkArgs) => {
     const source = getSourceByURL(getState(), url);
     if (!source) {
       return dispatch(setPendingSelectedLocation(url, options));
     }
 
     const sourceId = source.id;
     const location = createLocation({ ...options, sourceId });
@@ -88,17 +94,17 @@ export function selectSource(sourceId: s
   };
 }
 
 /**
  * @memberof actions/sources
  * @static
  */
 export function selectLocation(
-  location: Location,
+  location: SourceLocation,
   { keepContext = true }: Object = {}
 ) {
   return async ({ dispatch, getState, sourceMaps, client }: ThunkArgs) => {
     const currentSource = getSelectedSource(getState());
 
     if (!client) {
       // No connection, do nothing. This happens when the debugger is
       // shut down too fast and it tries to display a default source.
@@ -164,25 +170,25 @@ export function selectLocation(
     }
   };
 }
 
 /**
  * @memberof actions/sources
  * @static
  */
-export function selectSpecificLocation(location: Location) {
+export function selectSpecificLocation(location: SourceLocation) {
   return selectLocation(location, { keepContext: false });
 }
 
 /**
  * @memberof actions/sources
  * @static
  */
-export function jumpToMappedLocation(location: Location) {
+export function jumpToMappedLocation(location: SourceLocation) {
   return async function({ dispatch, getState, client, sourceMaps }: ThunkArgs) {
     if (!client) {
       return;
     }
 
     const pairedLocation = await getMappedLocation(
       getState(),
       sourceMaps,
--- a/devtools/client/debugger/new/src/actions/utils/middleware/promise.js
+++ b/devtools/client/debugger/new/src/actions/utils/middleware/promise.js
@@ -77,33 +77,29 @@ function promiseMiddleware({
     // Create a new action that doesn't have the promise field and has
     // the `seqId` field that represents the sequence id
     action = { ...filterAction(action), seqId };
 
     dispatch({ ...action, status: "start" });
 
     // Return the promise so action creators can still compose if they
     // want to.
-    return new Promise((resolve, reject) => {
-      promiseInst.then(
+    return Promise.resolve(promiseInst)
+      .finally(() => new Promise(resolve => executeSoon(resolve)))
+      .then(
         value => {
-          executeSoon(() => {
-            dispatch({ ...action, status: "done", value: value });
-            resolve(value);
-          });
+          dispatch({ ...action, status: "done", value: value });
+          return value;
         },
         error => {
-          executeSoon(() => {
-            dispatch({
-              ...action,
-              status: "error",
-              error: error.message || error
-            });
-            reject(error);
+          dispatch({
+            ...action,
+            status: "error",
+            error: error.message || error
           });
+          return Promise.reject(error);
         }
       );
-    });
   };
 }
 
 export const PROMISE = "@@dispatch/promise";
 export { promiseMiddleware as promise };
--- a/devtools/client/debugger/new/src/actions/utils/moz.build
+++ b/devtools/client/debugger/new/src/actions/utils/moz.build
@@ -4,10 +4,9 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DIRS += [
     'middleware',
 ]
 
 DebuggerModules(
     'create-store.js',
-    'symbols.js',
 )
--- a/devtools/client/debugger/new/src/client/firefox/commands.js
+++ b/devtools/client/debugger/new/src/client/firefox/commands.js
@@ -4,17 +4,17 @@
 
 // @flow
 
 import type {
   BreakpointId,
   BreakpointResult,
   Frame,
   FrameId,
-  Location,
+  SourceLocation,
   Script,
   Source,
   SourceId,
   Worker
 } from "../../types";
 
 import type {
   TabTarget,
@@ -124,17 +124,17 @@ function breakOnNext(): Promise<*> {
   return threadClient.breakOnNext();
 }
 
 function sourceContents(sourceId: SourceId): Source {
   const sourceClient = threadClient.source({ actor: sourceId });
   return sourceClient.source();
 }
 
-function getBreakpointByLocation(location: Location) {
+function getBreakpointByLocation(location: SourceLocation) {
   const id = makePendingLocationId(location);
   const bpClient = bpClients[id];
 
   if (bpClient) {
     const { actor, url, line, column, condition } = bpClient.location;
     return {
       id: bpClient.actor,
       condition,
@@ -153,17 +153,17 @@ function setXHRBreakpoint(path: string, 
   return threadClient.setXHRBreakpoint(path, method);
 }
 
 function removeXHRBreakpoint(path: string, method: string) {
   return threadClient.removeXHRBreakpoint(path, method);
 }
 
 function setBreakpoint(
-  location: Location,
+  location: SourceLocation,
   condition: boolean,
   noSliding: boolean
 ): Promise<BreakpointResult> {
   const sourceClient = threadClient.source({ actor: location.sourceId });
 
   return sourceClient
     .setBreakpoint({
       line: location.line,
@@ -179,17 +179,17 @@ function setBreakpoint(
       bpClient.location.column = actualLocation.column;
       bpClient.location.url = actualLocation.sourceUrl || "";
 
       return { id, actualLocation };
     });
 }
 
 function removeBreakpoint(
-  generatedLocation: Location
+  generatedLocation: SourceLocation
 ): Promise<void> | ?BreakpointResult {
   try {
     const id = makePendingLocationId(generatedLocation);
     const bpClient = bpClients[id];
     if (!bpClient) {
       console.warn("No breakpoint to delete on server");
       return Promise.resolve();
     }
@@ -197,17 +197,17 @@ function removeBreakpoint(
     return bpClient.remove();
   } catch (_error) {
     console.warn("No breakpoint to delete on server");
   }
 }
 
 function setBreakpointCondition(
   breakpointId: BreakpointId,
-  location: Location,
+  location: SourceLocation,
   condition: boolean,
   noSliding: boolean
 ) {
   const bpClient = bpClients[breakpointId];
   delete bpClients[breakpointId];
 
   return bpClient
     .setCondition(threadClient, condition, noSliding)
--- a/devtools/client/debugger/new/src/client/firefox/create.js
+++ b/devtools/client/debugger/new/src/client/firefox/create.js
@@ -1,16 +1,16 @@
 /* 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/>. */
 
 // @flow
 // This module converts Firefox specific types to the generic types
 
-import type { Frame, Source, Location } from "../../types";
+import type { Frame, Source, SourceLocation } from "../../types";
 import type {
   PausedPacket,
   FramesResponse,
   FramePacket,
   SourcePayload
 } from "./types";
 
 export function createFrame(frame: FramePacket): ?Frame {
@@ -73,19 +73,19 @@ export function createPause(
   };
 }
 
 // Firefox only returns `actualLocation` if it actually changed,
 // but we want it always to exist. Format `actualLocation` if it
 // exists, otherwise use `location`.
 
 export function createBreakpointLocation(
-  location: Location,
+  location: SourceLocation,
   actualLocation?: Object
-): Location {
+): SourceLocation {
   if (!actualLocation) {
     return location;
   }
 
   return {
     sourceId: actualLocation.source.actor,
     sourceUrl: actualLocation.source.url,
     line: actualLocation.line,
--- a/devtools/client/debugger/new/src/client/firefox/events.js
+++ b/devtools/client/debugger/new/src/client/firefox/events.js
@@ -9,17 +9,16 @@ import type {
   ResumedPacket,
   PausedPacket,
   ThreadClient,
   Actions
 } from "./types";
 
 import { createPause, createSource } from "./create";
 import sourceQueue from "../../utils/source-queue";
-import { features } from "../../utils/prefs";
 
 const CALL_STACK_PAGE_SIZE = 1000;
 
 type Dependencies = {
   threadClient: ThreadClient,
   actions: Actions,
   supportsWasm: boolean
 };
@@ -95,20 +94,16 @@ function resumed(_: "resumed", packet: R
     return;
   }
 
   actions.resumed(packet);
 }
 
 function newSource(_: "newSource", { source }: SourcePacket) {
   sourceQueue.queue(source);
-
-  if (features.eventListeners) {
-    actions.fetchEventListeners();
-  }
 }
 
 function workerListChanged() {
   actions.updateWorkers();
 }
 
 const clientEvents = {
   paused,
--- a/devtools/client/debugger/new/src/components/Editor/ColumnBreakpoint.js
+++ b/devtools/client/debugger/new/src/components/Editor/ColumnBreakpoint.js
@@ -1,16 +1,18 @@
 /* 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/>. */
 
 // @flow
-import { PureComponent } from "react";
+import React, { PureComponent } from "react";
+import ReactDOM from "react-dom";
 import classnames from "classnames";
 import { getDocument } from "../../utils/editor";
+import Svg from "../shared/Svg";
 
 // eslint-disable-next-line max-len
 import type { ColumnBreakpoint as ColumnBreakpointType } from "../../selectors/visibleColumnBreakpoints";
 
 type Bookmark = {
   clear: Function
 };
 
@@ -19,21 +21,21 @@ type Props = {
   editor: Object,
   source: Object,
   enabled: boolean,
   toggleBreakpoint: (number, number) => void,
   columnBreakpoint: ColumnBreakpointType
 };
 
 const breakpointImg = document.createElement("div");
+ReactDOM.render(<Svg name={"column-marker"} />, breakpointImg);
 function makeBookmark(isActive, { onClick }) {
   const bp = breakpointImg.cloneNode(true);
-  bp.className = classnames("call-site", {
-    active: isActive
-  });
+  const className = isActive ? "active" : "disabled";
+  bp.className = classnames("call-site", className);
   bp.onclick = onClick;
   return bp;
 }
 
 export default class CallSite extends PureComponent<Props> {
   addCallSite: Function;
   bookmark: ?Bookmark;
 
--- a/devtools/client/debugger/new/src/components/Editor/Footer.js
+++ b/devtools/client/debugger/new/src/components/Editor/Footer.js
@@ -25,28 +25,54 @@ import { getGeneratedSource } from "../.
 import { shouldShowFooter, shouldShowPrettyPrint } from "../../utils/editor";
 
 import { PaneToggleButton } from "../shared/Button";
 
 import type { Source } from "../../types";
 
 import "./Footer.css";
 
+type CursorPosition = {
+  line: number,
+  column: number
+};
+
 type Props = {
   selectedSource: Source,
   mappedSource: Source,
   endPanelCollapsed: boolean,
+  editor: Object,
   horizontal: boolean,
   togglePrettyPrint: string => void,
   toggleBlackBox: Object => void,
   jumpToMappedLocation: (Source: any) => void,
   togglePaneCollapse: () => void
 };
 
-class SourceFooter extends PureComponent<Props> {
+type State = {
+  cursorPosition: CursorPosition
+};
+
+class SourceFooter extends PureComponent<Props, State> {
+  constructor() {
+    super();
+
+    this.state = { cursorPosition: { line: 1, column: 1 } };
+  }
+
+  componentDidMount() {
+    const { editor } = this.props;
+    editor.codeMirror.on("cursorActivity", this.onCursorChange);
+  }
+
+  componentWillUnmount() {
+    const { editor } = this.props;
+    editor.codeMirror.off("cursorActivity", this.onCursorChange);
+  }
+
   prettyPrintButton() {
     const { selectedSource, togglePrettyPrint } = this.props;
 
     if (isLoading(selectedSource) && selectedSource.isPrettyPrinted) {
       return (
         <div className="loader">
           <Svg name="loader" />
         </div>
@@ -169,26 +195,43 @@ class SourceFooter extends PureComponent
         onClick={() => jumpToMappedLocation(mappedSourceLocation)}
         title={tooltip}
       >
         <span>{title}</span>
       </button>
     );
   }
 
+  onCursorChange = event => {
+    const { line, ch } = event.doc.getCursor();
+    this.setState({ cursorPosition: { line, column: ch } });
+  };
+
+  renderCursorPosition() {
+    const { cursorPosition } = this.state;
+
+    const text = L10N.getFormatStr(
+      "sourceFooter.currentCursorPosition",
+      cursorPosition.line + 1,
+      cursorPosition.column + 1
+    );
+    return <span className="cursor-position">{text}</span>;
+  }
+
   render() {
     const { selectedSource, horizontal } = this.props;
 
     if (!shouldShowFooter(selectedSource, horizontal)) {
       return null;
     }
 
     return (
       <div className="source-footer">
         {this.renderCommands()}
+        {this.renderCursorPosition()}
         {this.renderSourceSummary()}
         {this.renderToggleButton()}
       </div>
     );
   }
 }
 
 const mapStateToProps = state => {
--- a/devtools/client/debugger/new/src/components/Editor/HighlightLine.js
+++ b/devtools/client/debugger/new/src/components/Editor/HighlightLine.js
@@ -11,27 +11,27 @@ import { isLoaded } from "../../utils/so
 import { connect } from "react-redux";
 import {
   getVisibleSelectedFrame,
   getSelectedLocation,
   getSelectedSource,
   getPauseCommand
 } from "../../selectors";
 
-import type { Frame, Location, Source } from "../../types";
+import type { Frame, SourceLocation, Source } from "../../types";
 import type { Command } from "../../reducers/types";
 
 type Props = {
   pauseCommand: Command,
   selectedFrame: Frame,
-  selectedLocation: Location,
+  selectedLocation: SourceLocation,
   selectedSource: Source
 };
 
-function isDebugLine(selectedFrame: Frame, selectedLocation: Location) {
+function isDebugLine(selectedFrame: Frame, selectedLocation: SourceLocation) {
   if (!selectedFrame) {
     return;
   }
 
   return (
     selectedFrame.location.sourceId == selectedLocation.sourceId &&
     selectedFrame.location.line == selectedLocation.line
   );
@@ -49,17 +49,20 @@ export class HighlightLine extends Compo
   isStepping: boolean = false;
   previousEditorLine: ?number = null;
 
   shouldComponentUpdate(nextProps: Props) {
     const { selectedLocation, selectedSource } = nextProps;
     return this.shouldSetHighlightLine(selectedLocation, selectedSource);
   }
 
-  shouldSetHighlightLine(selectedLocation: Location, selectedSource: Source) {
+  shouldSetHighlightLine(
+    selectedLocation: SourceLocation,
+    selectedSource: Source
+  ) {
     const { sourceId, line } = selectedLocation;
     const editorLine = toEditorLine(sourceId, line);
 
     if (!isDocumentReady(selectedSource, selectedLocation)) {
       return false;
     }
 
     if (this.isStepping && editorLine === this.previousEditorLine) {
@@ -85,17 +88,17 @@ export class HighlightLine extends Compo
       prevProps.selectedLocation,
       prevProps.selectedSource
     );
     this.setHighlightLine(selectedLocation, selectedFrame, selectedSource);
     endOperation();
   }
 
   setHighlightLine(
-    selectedLocation: Location,
+    selectedLocation: SourceLocation,
     selectedFrame: Frame,
     selectedSource: Source
   ) {
     const { sourceId, line } = selectedLocation;
     if (!this.shouldSetHighlightLine(selectedLocation, selectedSource)) {
       return;
     }
     this.isStepping = false;
@@ -105,17 +108,17 @@ export class HighlightLine extends Compo
     if (!line || isDebugLine(selectedFrame, selectedLocation)) {
       return;
     }
 
     const doc = getDocument(sourceId);
     doc.addLineClass(editorLine, "line", "highlight-line");
   }
 
-  clearHighlightLine(selectedLocation: Location, selectedSource: Source) {
+  clearHighlightLine(selectedLocation: SourceLocation, selectedSource: Source) {
     if (!isDocumentReady(selectedSource, selectedLocation)) {
       return;
     }
 
     const { line, sourceId } = selectedLocation;
     const editorLine = toEditorLine(sourceId, line);
     const doc = getDocument(sourceId);
     doc.removeLineClass(editorLine, "line", "highlight-line");
--- a/devtools/client/debugger/new/src/components/Editor/index.js
+++ b/devtools/client/debugger/new/src/components/Editor/index.js
@@ -62,25 +62,25 @@ import {
 
 import { resizeToggleButton, resizeBreakpointGutter } from "../../utils/ui";
 
 import "./Editor.css";
 import "./Highlight.css";
 
 import type SourceEditor from "../../utils/editor/source-editor";
 import type { SymbolDeclarations } from "../../workers/parser";
-import type { Location, Source } from "../../types";
+import type { SourceLocation, Source } from "../../types";
 
 const cssVars = {
   searchbarHeight: "var(--editor-searchbar-height)",
   footerHeight: "var(--editor-footer-height)"
 };
 
 export type Props = {
-  selectedLocation: ?Location,
+  selectedLocation: ?SourceLocation,
   selectedSource: ?Source,
   searchOn: boolean,
   horizontal: boolean,
   startPanelSize: number,
   endPanelSize: number,
   conditionalPanelLine: number,
   symbols: SymbolDeclarations,
 
@@ -544,17 +544,17 @@ class Editor extends PureComponent<Props
 
     return (
       <div>
         <DebugLine editor={editor} />
         <HighlightLine />
         <EmptyLines editor={editor} />
         <Breakpoints editor={editor} />
         <Preview editor={editor} editorRef={this.$editorWrapper} />;
-        <Footer horizontal={horizontal} />
+        <Footer editor={editor} horizontal={horizontal} />
         <HighlightLines editor={editor} />
         <EditorMenu editor={editor} />
         <GutterMenu editor={editor} />
         <ConditionalPanel editor={editor} />
         {features.columnBreakpoints ? (
           <ColumnBreakpoints editor={editor} />
         ) : null}
       </div>
--- a/devtools/client/debugger/new/src/components/ProjectSearch.js
+++ b/devtools/client/debugger/new/src/components/ProjectSearch.js
@@ -23,17 +23,17 @@ import {
   getTextSearchQuery
 } from "../selectors";
 
 import Svg from "./shared/Svg";
 import ManagedTree from "./shared/ManagedTree";
 import SearchInput from "./shared/SearchInput";
 
 import type { List } from "immutable";
-import type { Location } from "../types";
+import type { SourceLocation } from "../types";
 import type { ActiveSearchType } from "../reducers/types";
 import type { StatusType } from "../reducers/project-text-search";
 type Editor = ?Object;
 
 import "./ProjectSearch.css";
 
 export type Match = {
   type: "MATCH",
@@ -63,17 +63,17 @@ type Props = {
   sources: Object,
   query: string,
   results: List<Result>,
   status: StatusType,
   activeSearch: ActiveSearchType,
   closeProjectSearch: () => void,
   searchSources: (query: string) => void,
   clearSearch: () => void,
-  selectSpecificLocation: (location: Location, tabIndex?: string) => void,
+  selectSpecificLocation: (location: SourceLocation, tabIndex?: string) => void,
   setActiveSearch: (activeSearch?: ActiveSearchType) => void,
   doSearchForHighlight: (
     query: string,
     editor: Editor,
     line: number,
     column: number
   ) => void
 };
@@ -145,17 +145,21 @@ export class ProjectSearch extends Compo
     }
 
     return setActiveSearch("project");
   };
 
   isProjectSearchEnabled = () => this.props.activeSearch === "project";
 
   selectMatchItem = (matchItem: Match) => {
-    this.props.selectSpecificLocation({ ...matchItem });
+    this.props.selectSpecificLocation({
+      sourceId: matchItem.sourceId,
+      line: matchItem.line,
+      column: matchItem.column
+    });
     this.props.doSearchForHighlight(
       this.state.inputValue,
       getEditor(),
       matchItem.line,
       matchItem.column
     );
   };
 
--- a/devtools/client/debugger/new/src/components/QuickOpenModal.js
+++ b/devtools/client/debugger/new/src/components/QuickOpenModal.js
@@ -30,32 +30,33 @@ import Modal from "./shared/Modal";
 import SearchInput from "./shared/SearchInput";
 import ResultList from "./shared/ResultList";
 
 import type {
   FormattedSymbolDeclarations,
   QuickOpenResult
 } from "../utils/quick-open";
 
-import type { Location, Source } from "../types";
+import type { SourceLocation, Source } from "../types";
 import type { QuickOpenType } from "../reducers/quick-open";
+import type { Tab } from "../reducers/tabs";
 
 import "./QuickOpenModal.css";
 
 type Props = {
   enabled: boolean,
   sources: Array<Object>,
   selectedSource?: Source,
   query: string,
   searchType: QuickOpenType,
   symbols: FormattedSymbolDeclarations,
   symbolsLoading: boolean,
-  tabs: string[],
+  tabs: Tab[],
   shortcutsModalEnabled: boolean,
-  selectSpecificLocation: Location => void,
+  selectSpecificLocation: SourceLocation => void,
   setQuickOpenQuery: (query: string) => void,
   highlightLineRange: ({ start: number, end: number }) => void,
   closeQuickOpen: () => void,
   toggleShortcutsModal: () => void
 };
 
 type State = {
   results: ?Array<QuickOpenResult>,
@@ -120,22 +121,22 @@ export class QuickOpenModal extends Comp
     const { sources } = this.props;
     const results =
       query == "" ? sources : filter(sources, this.dropGoto(query));
     return this.setState({ results });
   };
 
   searchSymbols = (query: string) => {
     const {
-      symbols: { functions, variables }
+      symbols: { functions, identifiers }
     } = this.props;
 
     let results = functions;
     if (this.isVariableQuery()) {
-      results = variables;
+      results = identifiers;
     } else {
       results = results.filter(result => result.title !== "anonymous");
     }
 
     if (query === "@" || query === "#") {
       return this.setState({ results });
     }
 
@@ -149,18 +150,20 @@ export class QuickOpenModal extends Comp
     } else {
       this.setState({ results: filter(results, query.slice(1)) });
     }
   };
 
   showTopSources = () => {
     const { tabs, sources } = this.props;
     if (tabs.length > 0) {
+      const tabUrls = tabs.map((tab: Tab) => tab.url);
+
       this.setState({
-        results: sources.filter(source => tabs.includes(source.url))
+        results: sources.filter(source => tabUrls.includes(source.url))
       });
     } else {
       this.setState({ results: sources.slice(0, 100) });
     }
   };
 
   updateResults = (query: string) => {
     if (this.isGotoQuery()) {
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/Breakpoint.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/Breakpoint.js
@@ -20,22 +20,22 @@ import { features } from "../../../utils
 import { getEditor } from "../../../utils/editor";
 
 import type { FormattedBreakpoint } from "../../../selectors/breakpointSources";
 
 import type {
   Breakpoint as BreakpointType,
   Frame,
   Source,
-  Location
+  SourceLocation
 } from "../../../types";
 
 type FormattedFrame = {
   ...Frame,
-  selectedLocation: Location
+  selectedLocation: SourceLocation
 };
 
 import {
   getBreakpointsList,
   getSelectedFrame,
   getSelectedSource
 } from "../../../selectors";
 
@@ -47,16 +47,17 @@ type Props = {
   enableBreakpoint: typeof actions.enableBreakpoint,
   removeBreakpoint: typeof actions.removeBreakpoint,
   removeBreakpoints: typeof actions.removeBreakpoints,
   removeAllBreakpoints: typeof actions.removeAllBreakpoints,
   disableBreakpoint: typeof actions.disableBreakpoint,
   setBreakpointCondition: typeof actions.setBreakpointCondition,
   toggleAllBreakpoints: typeof actions.toggleAllBreakpoints,
   toggleBreakpoints: typeof actions.toggleBreakpoints,
+  toggleDisabledBreakpoint: typeof actions.toggleDisabledBreakpoint,
   openConditionalPanel: typeof actions.openConditionalPanel,
   selectSpecificLocation: typeof actions.selectSpecificLocation
 };
 
 class Breakpoint extends PureComponent<Props> {
   onContextMenu = e => {
     showContextMenu({ ...this.props, contextMenuEvent: e });
   };
@@ -200,11 +201,12 @@ export default connect(
     removeBreakpoints: actions.removeBreakpoints,
     removeAllBreakpoints: actions.removeAllBreakpoints,
     disableBreakpoint: actions.disableBreakpoint,
     selectSpecificLocation: actions.selectSpecificLocation,
     selectLocation: actions.selectLocation,
     setBreakpointCondition: actions.setBreakpointCondition,
     toggleAllBreakpoints: actions.toggleAllBreakpoints,
     toggleBreakpoints: actions.toggleBreakpoints,
+    toggleDisabledBreakpoint: actions.toggleDisabledBreakpoint,
     openConditionalPanel: actions.openConditionalPanel
   }
 )(Breakpoint);
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/index.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/index.js
@@ -24,17 +24,16 @@ import {
 
 import Svg from "../shared/Svg";
 import { prefs, features } from "../../utils/prefs";
 
 import Breakpoints from "./Breakpoints";
 import Expressions from "./Expressions";
 import SplitBox from "devtools-splitter";
 import Frames from "./Frames";
-import EventListeners from "./EventListeners";
 import Workers from "./Workers";
 import Accordion from "../shared/Accordion";
 import CommandBar from "./CommandBar";
 import UtilsBar from "./UtilsBar";
 import FrameworkComponent from "./FrameworkComponent";
 import XHRBreakpoints from "./XHRBreakpoints";
 
 import Scopes from "./Scopes";
@@ -343,24 +342,16 @@ class SecondaryPanes extends Component<P
         items.push(this.getScopeItem());
       }
     }
 
     if (features.xhrBreakpoints) {
       items.push(this.getXHRItem());
     }
 
-    if (features.eventListeners) {
-      items.push({
-        header: L10N.getStr("eventListenersHeader"),
-        className: "event-listeners-pane",
-        component: <EventListeners />
-      });
-    }
-
     return items.filter(item => item);
   }
 
   renderHorizontalLayout() {
     return <Accordion items={this.getItems()} />;
   }
 
   getEndItems() {
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/moz.build
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/moz.build
@@ -5,17 +5,16 @@
 
 DIRS += [
     'Breakpoints',
     'Frames',
 ]
 
 DebuggerModules(
     'CommandBar.js',
-    'EventListeners.js',
     'Expressions.js',
     'FrameworkComponent.js',
     'index.js',
     'Scopes.js',
     'UtilsBar.js',
     'Workers.js',
     'XHRBreakpoints.js',
 )
--- a/devtools/client/debugger/new/src/reducers/ast.js
+++ b/devtools/client/debugger/new/src/reducers/ast.js
@@ -16,17 +16,17 @@ import { findEmptyLines } from "../utils
 import type {
   AstLocation,
   SymbolDeclarations,
   PausePoints,
   PausePoint
 } from "../workers/parser";
 
 import type { Map } from "immutable";
-import type { Location, Source } from "../types";
+import type { SourceLocation, Source } from "../types";
 import type { Action, DonePromiseAction } from "../actions/types";
 import type { Record } from "../utils/makeRecord";
 
 type EmptyLinesType = number[];
 
 export type Symbols = SymbolDeclarations | {| loading: true |};
 export type SymbolsMap = Map<string, Symbols>;
 export type EmptyLinesMap = Map<string, EmptyLinesType>;
@@ -204,17 +204,17 @@ export function getPausePoints(
   state: OuterState,
   sourceId: string
 ): ?PausePoints {
   return state.ast.pausePoints.get(sourceId);
 }
 
 export function getPausePoint(
   state: OuterState,
-  location: ?Location
+  location: ?SourceLocation
 ): ?PausePoint {
   if (!location) {
     return;
   }
 
   const { column, line, sourceId } = location;
   const pausePoints = getPausePoints(state, sourceId);
   if (!pausePoints) {
--- a/devtools/client/debugger/new/src/reducers/breakpoints.js
+++ b/devtools/client/debugger/new/src/reducers/breakpoints.js
@@ -9,17 +9,17 @@
  * @module reducers/breakpoints
  */
 
 import * as I from "immutable";
 
 import { isGeneratedId } from "devtools-source-map";
 import { makeLocationId } from "../utils/breakpoint";
 
-import type { XHRBreakpoint, Breakpoint, Location } from "../types";
+import type { XHRBreakpoint, Breakpoint, SourceLocation } from "../types";
 import type { Action, DonePromiseAction } from "../actions/types";
 
 export type BreakpointsMap = { [string]: Breakpoint };
 export type XHRBreakpointsList = I.List<XHRBreakpoint>;
 
 export type BreakpointsState = {
   breakpoints: BreakpointsMap,
   xhrBreakpoints: XHRBreakpointsList
@@ -265,17 +265,17 @@ export function getBreakpointsList(state
 }
 
 export function getBreakpointCount(state: OuterState): number {
   return getBreakpointsList(state).length;
 }
 
 export function getBreakpoint(
   state: OuterState,
-  location: Location
+  location: SourceLocation
 ): ?Breakpoint {
   const breakpoints = getBreakpointsMap(state);
   return breakpoints[makeLocationId(location)];
 }
 
 export function getBreakpointsDisabled(state: OuterState): boolean {
   const breakpoints = getBreakpointsList(state);
   return breakpoints.every(breakpoint => breakpoint.disabled);
@@ -317,17 +317,19 @@ export function getBreakpointForLine(
   return breakpoints.find(breakpoint => breakpoint.location.line === line);
 }
 
 export function getHiddenBreakpoint(state: OuterState): ?Breakpoint {
   const breakpoints = getBreakpointsList(state);
   return breakpoints.find(bp => bp.hidden);
 }
 
-export function getHiddenBreakpointLocation(state: OuterState): ?Location {
+export function getHiddenBreakpointLocation(
+  state: OuterState
+): ?SourceLocation {
   const hiddenBreakpoint = getHiddenBreakpoint(state);
   if (!hiddenBreakpoint) {
     return null;
   }
   return hiddenBreakpoint.location;
 }
 
 export default update;
--- a/devtools/client/debugger/new/src/reducers/index.js
+++ b/devtools/client/debugger/new/src/reducers/index.js
@@ -3,17 +3,16 @@
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 /**
  * Reducer index
  * @module reducers/index
  */
 
 import expressions from "./expressions";
-import eventListeners from "./event-listeners";
 import sources from "./sources";
 import tabs from "./tabs";
 import breakpoints from "./breakpoints";
 import pendingBreakpoints from "./pending-breakpoints";
 import asyncRequests from "./async-requests";
 import pause from "./pause";
 import ui from "./ui";
 import fileSearch from "./file-search";
@@ -21,17 +20,16 @@ import ast from "./ast";
 import projectTextSearch from "./project-text-search";
 import quickOpen from "./quick-open";
 import sourceTree from "./source-tree";
 import debuggee from "./debuggee";
 import { objectInspector } from "devtools-reps";
 
 export default {
   expressions,
-  eventListeners,
   sources,
   tabs,
   breakpoints,
   pendingBreakpoints,
   asyncRequests,
   pause,
   ui,
   fileSearch,
--- a/devtools/client/debugger/new/src/reducers/moz.build
+++ b/devtools/client/debugger/new/src/reducers/moz.build
@@ -7,17 +7,16 @@ DIRS += [
 
 ]
 
 DebuggerModules(
     'ast.js',
     'async-requests.js',
     'breakpoints.js',
     'debuggee.js',
-    'event-listeners.js',
     'expressions.js',
     'file-search.js',
     'index.js',
     'pause.js',
     'pending-breakpoints.js',
     'project-text-search.js',
     'quick-open.js',
     'source-tree.js',
--- a/devtools/client/debugger/new/src/reducers/sources.js
+++ b/devtools/client/debugger/new/src/reducers/sources.js
@@ -15,31 +15,31 @@ import {
   underRoot,
   getRelativeUrl,
   isGenerated,
   isOriginal as isOriginalSource
 } from "../utils/source";
 import { originalToGeneratedId } from "devtools-source-map";
 import { prefs } from "../utils/prefs";
 
-import type { Source, SourceId, Location } from "../types";
+import type { Source, SourceId, SourceLocation } from "../types";
 import type { PendingSelectedLocation } from "./types";
 import type { Action, DonePromiseAction } from "../actions/types";
 import type { LoadSourceAction } from "../actions/types/SourceAction";
 
 export type SourcesMap = { [string]: Source };
 
 type UrlsMap = { [string]: SourceId[] };
 
 export type SourcesState = {
   sources: SourcesMap,
   urls: UrlsMap,
   relativeSources: SourcesMap,
   pendingSelectedLocation?: PendingSelectedLocation,
-  selectedLocation: ?Location,
+  selectedLocation: ?SourceLocation,
   projectDirectoryRoot: string
 };
 
 export function initialSourcesState(): SourcesState {
   return {
     sources: {},
     urls: {},
     relativeSources: {},
@@ -478,17 +478,17 @@ export const getSourceCount = createSele
 export const getSelectedLocation = createSelector(
   getSourcesState,
   sources => sources.selectedLocation
 );
 
 export const getSelectedSource = createSelector(
   getSelectedLocation,
   getSources,
-  (selectedLocation: ?Location, sources: SourcesMap): ?Source => {
+  (selectedLocation: ?SourceLocation, sources: SourcesMap): ?Source => {
     if (!selectedLocation) {
       return;
     }
 
     return sources[selectedLocation.sourceId];
   }
 );
 
--- a/devtools/client/debugger/new/src/reducers/tabs.js
+++ b/devtools/client/debugger/new/src/reducers/tabs.js
@@ -21,17 +21,17 @@ import {
   getSpecificSourceByURL,
   getSpecificSourceByUrlInSources
 } from "./sources";
 
 import type { Action } from "../actions/types";
 import type { SourcesState } from "./sources";
 import type { Source } from "../types";
 
-type Tab = {
+export type Tab = {
   url: string,
   framework?: string | null,
   isOriginal: boolean,
   sourceId?: string
 };
 export type TabList = Tab[];
 
 function isSimilarTab(tab: Tab, url: string, isOriginal: boolean) {
--- a/devtools/client/debugger/new/src/reducers/ui.js
+++ b/devtools/client/debugger/new/src/reducers/ui.js
@@ -7,28 +7,28 @@
 /**
  * UI reducer
  * @module reducers/ui
  */
 
 import makeRecord from "../utils/makeRecord";
 import { prefs } from "../utils/prefs";
 
-import type { Source, Range } from "../types";
+import type { Source, PartialRange } from "../types";
 
 import type { Action, panelPositionType } from "../actions/types";
 import type { Record } from "../utils/makeRecord";
 
 export type ActiveSearchType = "project" | "file";
 
 export type OrientationType = "horizontal" | "vertical";
 
 export type SelectedPrimaryPaneTabType = "sources" | "outline";
 
-type Viewport = Range;
+type Viewport = PartialRange;
 
 export type UIState = {
   selectedPrimaryPaneTab: SelectedPrimaryPaneTabType,
   activeSearch: ?ActiveSearchType,
   contextMenu: any,
   shownSource: ?Source,
   startPanelCollapsed: boolean,
   endPanelCollapsed: boolean,
--- a/devtools/client/debugger/new/src/selectors/breakpointSources.js
+++ b/devtools/client/debugger/new/src/selectors/breakpointSources.js
@@ -9,30 +9,35 @@ import { createSelector } from "reselect
 import {
   getSources,
   getBreakpointsList,
   getSelectedSource
 } from "../selectors";
 import { isGenerated, getFilename } from "../utils/source";
 import { getSelectedLocation } from "../utils/source-maps";
 
-import type { Source, Breakpoint, BreakpointId, Location } from "../types";
+import type {
+  Source,
+  Breakpoint,
+  BreakpointId,
+  SourceLocation
+} from "../types";
 import type { SourcesMap } from "../reducers/types";
 
 export type BreakpointSources = Array<{
   source: Source,
   breakpoints: FormattedBreakpoint[]
 }>;
 
 export type FormattedBreakpoint = {|
   id: BreakpointId,
   condition: ?string,
   disabled: boolean,
   text: string,
-  selectedLocation: Location
+  selectedLocation: SourceLocation
 |};
 
 function formatBreakpoint(
   breakpoint: Breakpoint,
   selectedSource: Source
 ): FormattedBreakpoint {
   const { id, condition, disabled } = breakpoint;
 
--- a/devtools/client/debugger/new/src/selectors/index.js
+++ b/devtools/client/debugger/new/src/selectors/index.js
@@ -12,17 +12,16 @@ export * from "../reducers/debuggee";
 export * from "../reducers/breakpoints";
 export * from "../reducers/pending-breakpoints";
 export * from "../reducers/ui";
 export * from "../reducers/file-search";
 export * from "../reducers/ast";
 export * from "../reducers/project-text-search";
 export * from "../reducers/source-tree";
 
-export { getEventListeners } from "../reducers/event-listeners";
 export {
   getQuickOpenEnabled,
   getQuickOpenQuery,
   getQuickOpenType
 } from "../reducers/quick-open";
 
 export {
   getBreakpointAtLocation,
--- a/devtools/client/debugger/new/src/selectors/visibleColumnBreakpoints.js
+++ b/devtools/client/debugger/new/src/selectors/visibleColumnBreakpoints.js
@@ -1,23 +1,24 @@
 /* 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/>. */
 
-import { groupBy, hasIn } from "lodash";
+import { groupBy, hasIn, sortedUniqBy } from "lodash";
 import { createSelector } from "reselect";
 
 import { getViewport } from "../selectors";
 import { getVisibleBreakpoints } from "./visibleBreakpoints";
 import { getVisiblePausePoints } from "./visiblePausePoints";
+import { makeLocationId } from "../utils/breakpoint";
 
-import type { Location } from "../types";
+import type { SourceLocation } from "../types";
 
 export type ColumnBreakpoint = {|
-  +location: Location,
+  +location: SourceLocation,
   +enabled: boolean
 |};
 
 function contains(location, range) {
   return (
     location.line >= range.start.line &&
     location.line <= range.end.line &&
     location.column >= range.start.column &&
@@ -34,16 +35,29 @@ function groupBreakpoints(breakpoints) {
   return map;
 }
 
 function isEnabled(location, breakpointMap) {
   const { line, column } = location;
   return hasIn(breakpointMap, [line, column]);
 }
 
+function getLineCount(columnBreakpoints) {
+  const lineCount = {};
+  columnBreakpoints.forEach(({ location: { line } }) => {
+    if (!lineCount[line]) {
+      lineCount[line] = 0;
+    }
+
+    lineCount[line] = lineCount[line] + 1;
+  });
+
+  return lineCount;
+}
+
 export function formatColumnBreakpoints(columnBreakpoints) {
   console.log(
     "Column Breakpoints\n\n",
     columnBreakpoints
       .map(
         ({ location, enabled }) =>
           `(${location.line}, ${location.column}) ${enabled}`
       )
@@ -52,20 +66,45 @@ export function formatColumnBreakpoints(
 }
 
 export function getColumnBreakpoints(pausePoints, breakpoints, viewport) {
   if (!pausePoints) {
     return [];
   }
 
   const breakpointMap = groupBreakpoints(breakpoints);
-  const columnBreakpoints = pausePoints
-    .filter(({ types }) => types.break)
-    .filter(({ location }) => breakpointMap[location.line])
-    .filter(({ location }) => viewport && contains(location, viewport));
+
+  // We only want to show a column breakpoint if several conditions are matched
+  // 1. it is a "break" point and not a "step" point
+  // 2. there is a breakpoint on that line
+  // 3. the breakpoint is in the current viewport
+  // 4. it is the first breakpoint to appear at that generated location
+  // 5. there is atleast one other breakpoint on that line
+
+  let columnBreakpoints = pausePoints.filter(
+    ({ types, location }) =>
+      // 1. check that the pause point is a "break" point
+      types.break &&
+      // 2. check that there is a registered breakpoint on the line
+      breakpointMap[location.line] &&
+      // 3. check that the breakpoint is visible
+      viewport &&
+      contains(location, viewport)
+  );
+
+  // 4. Only show one column breakpoint per generated location
+  columnBreakpoints = sortedUniqBy(columnBreakpoints, ({ generatedLocation }) =>
+    makeLocationId(generatedLocation)
+  );
+
+  // 5. Check that there is atleast one other possible breakpoint on the line
+  const lineCount = getLineCount(columnBreakpoints);
+  columnBreakpoints = columnBreakpoints.filter(
+    ({ location: { line } }) => lineCount[line] > 1
+  );
 
   return columnBreakpoints.map(({ location }) => ({
     location,
     enabled: isEnabled(location, breakpointMap)
   }));
 }
 
 export const visibleColumnBreakpoints = createSelector(
--- a/devtools/client/debugger/new/src/selectors/visibleSelectedFrame.js
+++ b/devtools/client/debugger/new/src/selectors/visibleSelectedFrame.js
@@ -4,19 +4,19 @@
 
 // @flow
 
 import { getSelectedLocation } from "../reducers/sources";
 import { getSelectedFrame } from "../reducers/pause";
 import { isOriginalId } from "devtools-source-map";
 import { createSelector } from "reselect";
 
-import type { Frame, Location } from "../types";
+import type { Frame, SourceLocation } from "../types";
 
-function getLocation(frame: Frame, location?: Location) {
+function getLocation(frame: Frame, location?: SourceLocation) {
   if (!location) {
     return frame.location;
   }
 
   return !isOriginalId(location.sourceId)
     ? frame.generatedLocation || frame.location
     : frame.location;
 }
--- a/devtools/client/debugger/new/src/utils/ast.js
+++ b/devtools/client/debugger/new/src/utils/ast.js
@@ -2,31 +2,28 @@
  * 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/>. */
 
 // @flow
 
 import { xor, range } from "lodash";
 import { convertToList } from "./pause/pausePoints";
 
-import type { Location, ColumnPosition } from "../types";
+import type { SourceLocation, Position } from "../types";
 import type { Symbols } from "../reducers/ast";
 
 import type {
   AstPosition,
   AstLocation,
   PausePoints,
   FunctionDeclaration,
   ClassDeclaration
 } from "../workers/parser";
 
-export function findBestMatchExpression(
-  symbols: Symbols,
-  tokenPos: ColumnPosition
-) {
+export function findBestMatchExpression(symbols: Symbols, tokenPos: Position) {
   if (symbols.loading) {
     return null;
   }
 
   const { line, column } = tokenPos;
   const { memberExpressions, identifiers, literals } = symbols;
   const members = memberExpressions.filter(({ computed }) => !computed);
 
@@ -70,17 +67,17 @@ export function containsPosition(a: AstL
     a.start.line < b.line ||
     (a.start.line === b.line && a.start.column <= b.column);
   const endsAfter =
     a.end.line > b.line || (a.end.line === b.line && a.end.column >= b.column);
 
   return startsBefore && endsAfter;
 }
 
-function findClosestofSymbol(declarations: any[], location: Location) {
+function findClosestofSymbol(declarations: any[], location: SourceLocation) {
   if (!declarations) {
     return null;
   }
 
   return declarations.reduce((found, currNode) => {
     if (
       currNode.name === "anonymous" ||
       !containsPosition(currNode.location, {
@@ -106,27 +103,27 @@ function findClosestofSymbol(declaration
     }
 
     return currNode;
   }, null);
 }
 
 export function findClosestFunction(
   symbols: ?Symbols,
-  location: Location
+  location: SourceLocation
 ): FunctionDeclaration | null {
   if (!symbols || symbols.loading) {
     return null;
   }
 
   return findClosestofSymbol(symbols.functions, location);
 }
 
 export function findClosestClass(
   symbols: Symbols,
-  location: Location
+  location: SourceLocation
 ): ClassDeclaration | null {
   if (!symbols || symbols.loading) {
     return null;
   }
 
   return findClosestofSymbol(symbols.classes, location);
 }
--- a/devtools/client/debugger/new/src/utils/bootstrap.js
+++ b/devtools/client/debugger/new/src/utils/bootstrap.js
@@ -14,17 +14,17 @@ import { startSourceMapWorker, stopSourc
 import * as search from "../workers/search";
 import * as prettyPrint from "../workers/pretty-print";
 import * as parser from "../workers/parser";
 
 import configureStore from "../actions/utils/create-store";
 import reducers from "../reducers";
 import * as selectors from "../selectors";
 import App from "../components/App";
-import { asyncStore } from "./prefs";
+import { asyncStore, prefs } from "./prefs";
 
 function renderPanel(component, store) {
   const root = document.createElement("div");
   root.className = "launchpad-root theme-body";
   root.style.setProperty("flex", "1");
   const mount = document.querySelector("#mount");
   if (!mount) {
     return;
@@ -38,17 +38,17 @@ function renderPanel(component, store) {
 }
 
 export function bootstrapStore(
   client: any,
   { services, toolboxActions }: any,
   initialState: Object
 ) {
   const createStore = configureStore({
-    log: isTesting(),
+    log: prefs.logging || isTesting(),
     timing: isDevelopment(),
     makeThunkArgs: (args, state) => {
       return { ...args, client, ...services, ...toolboxActions };
     }
   });
 
   const store = createStore(combineReducers(reducers), initialState);
   store.subscribe(() => updatePrefs(store.getState()));
--- a/devtools/client/debugger/new/src/utils/breakpoint/astBreakpointLocation.js
+++ b/devtools/client/debugger/new/src/utils/breakpoint/astBreakpointLocation.js
@@ -4,22 +4,22 @@
 
 // @flow
 
 import { getSymbols } from "../../workers/parser";
 import { findClosestFunction } from "../ast";
 
 import type { SymbolDeclarations } from "../../workers/parser";
 
-import type { Location, Source, ASTLocation } from "../../types";
+import type { SourceLocation, Source, ASTLocation } from "../../types";
 
 export function getASTLocation(
   source: Source,
   symbols: SymbolDeclarations,
-  location: Location
+  location: SourceLocation
 ): ASTLocation {
   if (source.isWasm || !symbols || symbols.loading) {
     return { name: undefined, offset: location };
   }
 
   const scope = findClosestFunction(symbols, location);
   if (scope) {
     // we only record the line, but at some point we may
--- a/devtools/client/debugger/new/src/utils/breakpoint/index.js
+++ b/devtools/client/debugger/new/src/utils/breakpoint/index.js
@@ -9,17 +9,17 @@ import { sortBy } from "lodash";
 import { getBreakpoint } from "../../selectors";
 import assert from "../assert";
 import { features } from "../prefs";
 
 export { getASTLocation, findScopeByName } from "./astBreakpointLocation";
 
 import type { FormattedBreakpoint } from "../../selectors/breakpointSources";
 import type {
-  Location,
+  SourceLocation,
   PendingLocation,
   Breakpoint,
   PendingBreakpoint
 } from "../../types";
 
 import type { State } from "../../reducers/types";
 
 // Return the first argument that is a string, or null if nothing is a
@@ -28,34 +28,37 @@ export function firstString(...args: str
   for (const arg of args) {
     if (typeof arg === "string") {
       return arg;
     }
   }
   return null;
 }
 
-export function locationMoved(location: Location, newLocation: Location) {
+export function locationMoved(
+  location: SourceLocation,
+  newLocation: SourceLocation
+) {
   return (
     location.line !== newLocation.line || location.column !== newLocation.column
   );
 }
 
-export function makeLocationId(location: Location) {
+export function makeLocationId(location: SourceLocation) {
   const { sourceId, line, column } = location;
   const columnString = column || "";
   return `${sourceId}:${line}:${columnString}`;
 }
 
-export function getLocationWithoutColumn(location: Location) {
+export function getLocationWithoutColumn(location: SourceLocation) {
   const { sourceId, line } = location;
   return `${sourceId}:${line}`;
 }
 
-export function makePendingLocationId(location: Location) {
+export function makePendingLocationId(location: SourceLocation) {
   assertPendingLocation(location);
   const { sourceUrl, line, column } = location;
   const sourceUrlString = sourceUrl || "";
   const columnString = column || "";
 
   return `${sourceUrlString}:${line}:${columnString}`;
 }
 
@@ -64,17 +67,17 @@ export function assertBreakpoint(breakpo
   assertLocation(breakpoint.generatedLocation);
 }
 
 export function assertPendingBreakpoint(pendingBreakpoint: PendingBreakpoint) {
   assertPendingLocation(pendingBreakpoint.location);
   assertPendingLocation(pendingBreakpoint.generatedLocation);
 }
 
-export function assertLocation(location: Location) {
+export function assertLocation(location: SourceLocation) {
   assertPendingLocation(location);
   const { sourceId } = location;
   assert(!!sourceId, "location must have a source id");
 }
 
 export function assertPendingLocation(location: PendingLocation) {
   assert(!!location, "location must exist");
 
@@ -87,17 +90,17 @@ export function assertPendingLocation(lo
     location.hasOwnProperty("column") != null,
     "location must have a column"
   );
 }
 
 // syncing
 export function breakpointAtLocation(
   breakpoints: Breakpoint[],
-  { line, column }: Location
+  { line, column }: SourceLocation
 ) {
   return breakpoints.find(breakpoint => {
     const sameLine = breakpoint.location.line === line;
     if (!sameLine) {
       return false;
     }
 
     // NOTE: when column breakpoints are disabled we want to find
@@ -105,23 +108,23 @@ export function breakpointAtLocation(
     if (!features.columnBreakpoints) {
       return true;
     }
 
     return breakpoint.location.column === column;
   });
 }
 
-export function breakpointExists(state: State, location: Location) {
+export function breakpointExists(state: State, location: SourceLocation) {
   const currentBp = getBreakpoint(state, location);
   return currentBp && !currentBp.disabled;
 }
 
 export function createBreakpoint(
-  location: Location,
+  location: SourceLocation,
   overrides: Object = {}
 ): Breakpoint {
   const {
     condition,
     disabled,
     hidden,
     generatedLocation,
     astLocation,
--- a/devtools/client/debugger/new/src/utils/dbg.js
+++ b/devtools/client/debugger/new/src/utils/dbg.js
@@ -44,18 +44,19 @@ function bindSelectors(obj: Object): Obj
 }
 
 function getCM() {
   const cm: any = document.querySelector(".CodeMirror");
   return cm && cm.CodeMirror;
 }
 
 function _formatPausePoints(dbg: Object, url: string) {
-  const source = dbg.helpers.findSource(url);
-  const pausePoints = dbg.selectors.getPausePoints(source);
+  const source =
+    dbg.helpers.findSource(url) || dbg.selectors.getSelectedSource();
+  const pausePoints = dbg.selectors.getPausePoints(source.id);
   console.log(formatPausePoints(source.text, pausePoints));
 }
 
 function _formatColumnBreapoints(dbg: Object) {
   console.log(
     dbg.selectors.formatColumnBreakpoints(
       dbg.selectors.visibleColumnBreakpoints()
     )
--- a/devtools/client/debugger/new/src/utils/editor/get-expression.js
+++ b/devtools/client/debugger/new/src/utils/editor/get-expression.js
@@ -1,41 +1,41 @@
 /* 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/>. */
 
 // @flow
 
-import type { ColumnPosition } from "../../types";
+import type { Position } from "../../types";
 
 type Token = {
   startColumn: number,
   endColumn: number,
   type: string
 };
 
 export function tokenAtTextPosition(
   cm: any,
-  { line, column }: ColumnPosition
+  { line, column }: Position
 ): Token | null {
   if (line < 0 || line >= cm.lineCount()) {
     return null;
   }
 
   const token = cm.getTokenAt({ line: line - 1, ch: column });
   if (!token) {
     return null;
   }
 
   return { startColumn: token.start, endColumn: token.end, type: token.type };
 }
 
 // The strategy of querying codeMirror tokens was borrowed
 // from Chrome's inital implementation in JavaScriptSourceFrame.js#L414
-export function getExpressionFromCoords(cm: any, coord: ColumnPosition) {
+export function getExpressionFromCoords(cm: any, coord: Position) {
   const token = tokenAtTextPosition(cm, coord);
   if (!token) {
     return null;
   }
 
   let startHighlight = token.startColumn;
   const endHighlight = token.endColumn;
   const lineNumber = coord.line;
--- a/devtools/client/debugger/new/src/utils/editor/get-token-location.js
+++ b/devtools/client/debugger/new/src/utils/editor/get-token-location.js
@@ -1,19 +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/>. */
 
 // @flow
-import type { ColumnPosition } from "../../types";
+import type { Position } from "../../types";
 
 export function getTokenLocation(
   codeMirror: any,
   tokenEl: HTMLElement
-): ColumnPosition {
+): Position {
   const { left, top, width, height } = tokenEl.getBoundingClientRect();
   const { line, ch } = codeMirror.coordsChar({
     left: left + width / 2,
     top: top + height / 2
   });
 
   return {
     line: line + 1,
--- a/devtools/client/debugger/new/src/utils/editor/index.js
+++ b/devtools/client/debugger/new/src/utils/editor/index.js
@@ -13,17 +13,17 @@ export { onMouseOver } from "./token-eve
 import { createEditor } from "./create-editor";
 import { shouldPrettyPrint, isOriginal } from "../source";
 import { findNext, findPrev } from "./source-search";
 
 import { isWasm, lineToWasmOffset, wasmOffsetToLine } from "../wasm";
 
 import type { AstLocation } from "../../workers/parser";
 import type { EditorPosition, EditorRange } from "../editor/types";
-import type { Location } from "../../types";
+import type { SourceLocation } from "../../types";
 type Editor = Object;
 
 let editor: ?Editor;
 
 export function getEditor() {
   if (editor) {
     return editor;
   }
@@ -91,17 +91,17 @@ export function toEditorLine(sourceId: s
   if (isWasm(sourceId)) {
     // TODO ensure offset is always "mappable" to edit line.
     return wasmOffsetToLine(sourceId, lineOrOffset) || 0;
   }
 
   return lineOrOffset ? lineOrOffset - 1 : 1;
 }
 
-export function toEditorPosition(location: Location): EditorPosition {
+export function toEditorPosition(location: SourceLocation): EditorPosition {
   return {
     line: toEditorLine(location.sourceId, location.line),
     column: isWasm(location.sourceId) || !location.column ? 0 : location.column
   };
 }
 
 export function toEditorRange(
   sourceId: string,
@@ -196,17 +196,17 @@ export function markText(_editor: any, c
 
 export function lineAtHeight(_editor, sourceId, event) {
   const _editorLine = _editor.codeMirror.lineAtHeight(event.clientY);
   return toSourceLine(sourceId, _editorLine);
 }
 
 export function getSourceLocationFromMouseEvent(
   _editor: Object,
-  selectedLocation: Location,
+  selectedLocation: SourceLocation,
   e: MouseEvent
 ) {
   const { line, ch } = _editor.codeMirror.coordsChar({
     left: e.clientX,
     top: e.clientY
   });
 
   return {
--- a/devtools/client/debugger/new/src/utils/location.js
+++ b/devtools/client/debugger/new/src/utils/location.js
@@ -1,28 +1,28 @@
 /* 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/>. */
 
 // @flow
 
-import type { Location, SourceId } from "../types";
+import type { SourceLocation, SourceId } from "../types";
 
 type IncompleteLocation = {
   sourceId: SourceId,
   line?: number,
   column?: number,
   sourceUrl?: string
 };
 
 export function createLocation({
   sourceId,
   line,
   column,
   sourceUrl
-}: IncompleteLocation): Location {
+}: IncompleteLocation): SourceLocation {
   return {
     sourceId,
     line: line || 0,
     column,
     sourceUrl: sourceUrl || ""
   };
 }
--- a/devtools/client/debugger/new/src/utils/log.js
+++ b/devtools/client/debugger/new/src/utils/log.js
@@ -7,26 +7,24 @@
 /**
  *
  * Utils for logging to the console
  * Suppresses logging in non-development environment
  *
  * @module utils/log
  */
 
-import { isDevelopment } from "devtools-environment";
+import { prefs } from "./prefs";
 
 /**
  * Produces a formatted console log line by imploding args, prefixed by [log]
  *
  * function input: log(["hello", "world"])
  * console output: [log] hello world
  *
  * @memberof utils/log
  * @static
  */
 export function log(...args: any[]) {
-  if (!isDevelopment()) {
-    return;
+  if (prefs.logging) {
+    console.log(...args);
   }
-
-  console.log.apply(console, ["[log]", ...args]);
 }
--- a/devtools/client/debugger/new/src/utils/pause/mapScopes/getApplicableBindingsForOriginalPosition.js
+++ b/devtools/client/debugger/new/src/utils/pause/mapScopes/getApplicableBindingsForOriginalPosition.js
@@ -4,40 +4,40 @@
 
 // @flow
 
 import type { BindingLocationType, BindingType } from "../../../workers/parser";
 import { positionCmp } from "./positionCmp";
 import { filterSortedArray } from "./filtering";
 import { mappingContains } from "./mappingContains";
 
-import type { Source, Location, Position } from "../../../types";
+import type { Source, SourceLocation, PartialPosition } from "../../../types";
 
 import type { GeneratedBindingLocation } from "./buildGeneratedBindingList";
 
 export type ApplicableBinding = {
   binding: GeneratedBindingLocation,
   range: GeneratedRange,
   firstInRange: boolean,
   firstOnLine: boolean
 };
 
 type GeneratedRange = {
-  start: Position,
-  end: Position
+  start: PartialPosition,
+  end: PartialPosition
 };
 
 export async function originalRangeStartsInside(
   source: Source,
   {
     start,
     end
   }: {
-    start: Location,
-    end: Location
+    start: SourceLocation,
+    end: SourceLocation
   },
   sourceMaps: any
 ) {
   const endPosition = await sourceMaps.getGeneratedLocation(end, source);
   const startPosition = await sourceMaps.getGeneratedLocation(start, source);
 
   // If the start and end positions collapse into eachother, it means that
   // the range in the original content didn't _start_ at the start position.
@@ -48,18 +48,18 @@ export async function originalRangeStart
 
 export async function getApplicableBindingsForOriginalPosition(
   generatedAstBindings: Array<GeneratedBindingLocation>,
   source: Source,
   {
     start,
     end
   }: {
-    start: Location,
-    end: Location
+    start: SourceLocation,
+    end: SourceLocation
   },
   bindingType: BindingType,
   locationType: BindingLocationType,
   sourceMaps: any
 ): Promise<Array<ApplicableBinding>> {
   const ranges = await sourceMaps.getGeneratedRanges(start, source);
 
   const resultRanges = ranges.map(mapRange => ({
--- a/devtools/client/debugger/new/src/utils/pause/mapScopes/index.js
+++ b/devtools/client/debugger/new/src/utils/pause/mapScopes/index.js
@@ -31,17 +31,17 @@ import {
 } from "./buildGeneratedBindingList";
 import {
   originalRangeStartsInside,
   getApplicableBindingsForOriginalPosition
 } from "./getApplicableBindingsForOriginalPosition";
 
 import { log } from "../../log";
 import type {
-  Position,
+  PartialPosition,
   Frame,
   Scope,
   Source,
   BindingContents,
   ScopeBindings
 } from "../../../types";
 
 export type OriginalScope = RenderableScope;
@@ -249,17 +249,17 @@ function batchScopeMappings(
       if (s !== source || !precalculatedLocations.has(key)) {
         log("Bad precalculated mapping");
         return sourceMaps.getGeneratedLocation(pos, s);
       }
       return precalculatedLocations.get(key);
     }
   };
 }
-function buildLocationKey(loc: Position): string {
+function buildLocationKey(loc: PartialPosition): string {
   return `${loc.line}:${locColumn(loc)}`;
 }
 
 function generateClientScope(
   scopes: Scope,
   originalScopes: Array<SourceScope & { generatedBindings: ScopeBindings }>
 ): OriginalScope {
   // Pull the root object scope and root lexical scope to reuse them in
--- a/devtools/client/debugger/new/src/utils/pause/mapScopes/locColumn.js
+++ b/devtools/client/debugger/new/src/utils/pause/mapScopes/locColumn.js
@@ -1,17 +1,17 @@
 /* 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/>. */
 
 // @flow
 
-import type { Position } from "../../../types";
+import type { PartialPosition } from "../../../types";
 
-export function locColumn(loc: Position): number {
+export function locColumn(loc: PartialPosition): number {
   if (typeof loc.column !== "number") {
     // This shouldn't really happen with locations from the AST, but
     // the datatype we are using allows null/undefined column.
     return 0;
   }
 
   return loc.column;
 }
--- a/devtools/client/debugger/new/src/utils/pause/mapScopes/mappingContains.js
+++ b/devtools/client/debugger/new/src/utils/pause/mapScopes/mappingContains.js
@@ -1,18 +1,18 @@
 /* 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/>. */
 
 // @flow
 
-import type { Position } from "../../../types";
+import type { PartialPosition } from "../../../types";
 import { positionCmp } from "./positionCmp";
 
 export function mappingContains(
-  mapped: { +start: Position, +end: Position },
-  item: { +start: Position, +end: Position }
+  mapped: { +start: PartialPosition, +end: PartialPosition },
+  item: { +start: PartialPosition, +end: PartialPosition }
 ) {
   return (
     positionCmp(item.start, mapped.start) >= 0 &&
     positionCmp(item.end, mapped.end) <= 0
   );
 }
--- a/devtools/client/debugger/new/src/utils/pause/mapScopes/positionCmp.js
+++ b/devtools/client/debugger/new/src/utils/pause/mapScopes/positionCmp.js
@@ -1,23 +1,23 @@
 /* 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/>. */
 
 // @flow
 
-import type { Position } from "../../../types";
+import type { PartialPosition } from "../../../types";
 import { locColumn } from "./locColumn";
 
 /**
  * * === 0 - Positions are equal.
  * * < 0 - first position before second position
  * * > 0 - first position after second position
  */
-export function positionCmp(p1: Position, p2: Position) {
+export function positionCmp(p1: PartialPosition, p2: PartialPosition) {
   if (p1.line === p2.line) {
     const l1 = locColumn(p1);
     const l2 = locColumn(p2);
 
     if (l1 === l2) {
       return 0;
     }
     return l1 < l2 ? -1 : 1;
--- a/devtools/client/debugger/new/src/utils/pause/mapScopes/rangeMetadata.js
+++ b/devtools/client/debugger/new/src/utils/pause/mapScopes/rangeMetadata.js
@@ -4,17 +4,17 @@
 
 // @flow
 
 import { locColumn } from "./locColumn";
 import { positionCmp } from "./positionCmp";
 import { filterSortedArray } from "./filtering";
 
 import type { SourceScope } from "../../../workers/parser";
-import type { Position, Frame, Source } from "../../../types";
+import type { PartialPosition, Frame, Source } from "../../../types";
 
 type SourceOriginalRange = {
   line: number,
   columnStart: number,
   columnEnd: number
 };
 
 // * match - Range contains a single identifier with matching start location
@@ -113,17 +113,17 @@ export async function loadRangeMetadata(
       singleDeclaration,
       ...range
     };
   });
 }
 
 export function findMatchingRange(
   sortedOriginalRanges: Array<MappedOriginalRange>,
-  bindingRange: { +end: Position, +start: Position }
+  bindingRange: { +end: PartialPosition, +start: PartialPosition }
 ): ?MappedOriginalRange {
   return filterSortedArray(sortedOriginalRanges, range => {
     if (range.line < bindingRange.start.line) {
       return -1;
     }
     if (range.line > bindingRange.start.line) {
       return 1;
     }
--- a/devtools/client/debugger/new/src/utils/pause/pausePoints.js
+++ b/devtools/client/debugger/new/src/utils/pause/pausePoints.js
@@ -1,38 +1,34 @@
 /* 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/>. */
 
 // @flow
 import { reverse } from "lodash";
 
 import type { PausePoints } from "../../workers/parser";
-import type { ColumnPosition } from "../../types";
+import type { Position } from "../../types";
 
 type PausePoint = {
-  location: ColumnPosition,
+  location: Position,
   types: { break: boolean, step: boolean }
 };
 
 function insertStrtAt(string, index, newString) {
   const start = string.slice(0, index);
   const end = string.slice(index);
   return `${start}${newString}${end}`;
 }
 
 export function convertToList(pausePoints: PausePoints): PausePoint[] {
   const list = [];
   for (const line in pausePoints) {
     for (const column in pausePoints[line]) {
-      const point = pausePoints[line][column];
-      list.push({
-        location: { line: parseInt(line, 10), column: parseInt(column, 10) },
-        types: point
-      });
+      list.push(pausePoints[line][column]);
     }
   }
   return list;
 }
 
 export function formatPausePoints(text: string, pausePoints: PausePoints) {
   const nodes = reverse(convertToList(pausePoints));
   const lines = text.split("\n");
@@ -40,8 +36,21 @@ export function formatPausePoints(text: 
     const { line, column } = node.location;
     const { break: breakPoint, step } = node.types;
     const types = `${breakPoint ? "b" : ""}${step ? "s" : ""}`;
     lines[line - 1] = insertStrtAt(lines[line - 1], column, `/*${types}*/`);
   });
 
   return lines.join("\n");
 }
+
+export async function mapPausePoints(pausePoints, iteratee) {
+  const results = await Promise.all(convertToList(pausePoints).map(iteratee));
+
+  for (const line in pausePoints) {
+    const linePoints = pausePoints[line];
+    for (const column in linePoints) {
+      linePoints[column] = results.shift();
+    }
+  }
+
+  return pausePoints;
+}
--- a/devtools/client/debugger/new/src/utils/pause/stepping.js
+++ b/devtools/client/debugger/new/src/utils/pause/stepping.js
@@ -32,17 +32,17 @@ export function shouldStep(rootFrame: ?F
     return false;
   }
 
   const previousFrameLoc = getFrameLocation(selectedSource, previousFrameInfo);
   const frameLoc = getFrameLocation(selectedSource, rootFrame);
 
   const sameLocation = previousFrameLoc && isEqual(previousFrameLoc, frameLoc);
   const pausePoint = getPausePoint(state, frameLoc);
-  const invalidPauseLocation = pausePoint && !pausePoint.step;
+  const invalidPauseLocation = pausePoint && !pausePoint.types.step;
 
   // We always want to pause in generated locations
   if (!frameLoc || isGeneratedId(frameLoc.sourceId)) {
     return false;
   }
 
   return sameLocation || invalidPauseLocation;
 }
--- a/devtools/client/debugger/new/src/utils/prefs.js
+++ b/devtools/client/debugger/new/src/utils/prefs.js
@@ -9,16 +9,17 @@ import { isDevelopment } from "devtools-
 import Services from "devtools-services";
 import { asyncStoreHelper } from "./asyncStoreHelper";
 
 const prefsSchemaVersion = "1.0.5";
 
 const pref = Services.pref;
 
 if (isDevelopment()) {
+  pref("devtools.debugger.logging", false);
   pref("devtools.debugger.alphabetize-outline", false);
   pref("devtools.debugger.auto-pretty-print", false);
   pref("devtools.source-map.client-service.enabled", true);
   pref("devtools.debugger.pause-on-exceptions", false);
   pref("devtools.debugger.pause-on-caught-exceptions", false);
   pref("devtools.debugger.ignore-caught-exceptions", true);
   pref("devtools.debugger.call-stack-visible", true);
   pref("devtools.debugger.scopes-visible", true);
@@ -45,30 +46,30 @@ if (isDevelopment()) {
   pref("devtools.debugger.skip-pausing", false);
   pref("devtools.debugger.features.workers", true);
   pref("devtools.debugger.features.async-stepping", true);
   pref("devtools.debugger.features.wasm", true);
   pref("devtools.debugger.features.shortcuts", true);
   pref("devtools.debugger.features.root", true);
   pref("devtools.debugger.features.map-scopes", true);
   pref("devtools.debugger.features.remove-command-bar-options", true);
-  pref("devtools.debugger.features.event-listeners", false);
   pref("devtools.debugger.features.code-folding", false);
   pref("devtools.debugger.features.outline", true);
   pref("devtools.debugger.features.column-breakpoints", false);
   pref("devtools.debugger.features.pause-points", true);
   pref("devtools.debugger.features.skip-pausing", true);
   pref("devtools.debugger.features.component-pane", false);
   pref("devtools.debugger.features.autocomplete-expressions", false);
   pref("devtools.debugger.features.map-expression-bindings", true);
   pref("devtools.debugger.features.map-await-expression", true);
   pref("devtools.debugger.features.xhr-breakpoints", true);
 }
 
 export const prefs = new PrefsHelper("devtools", {
+  logging: ["Bool", "debugger.alphabetize-outline"],
   alphabetizeOutline: ["Bool", "debugger.alphabetize-outline"],
   autoPrettyPrint: ["Bool", "debugger.auto-pretty-print"],
   clientSourceMapsEnabled: ["Bool", "source-map.client-service.enabled"],
   pauseOnExceptions: ["Bool", "debugger.pause-on-exceptions"],
   pauseOnCaughtExceptions: ["Bool", "debugger.pause-on-caught-exceptions"],
   ignoreCaughtExceptions: ["Bool", "debugger.ignore-caught-exceptions"],
   callStackVisible: ["Bool", "debugger.call-stack-visible"],
   scopesVisible: ["Bool", "debugger.scopes-visible"],
@@ -99,17 +100,16 @@ export const features = new PrefsHelper(
   asyncStepping: ["Bool", "async-stepping"],
   wasm: ["Bool", "wasm"],
   shortcuts: ["Bool", "shortcuts"],
   root: ["Bool", "root"],
   columnBreakpoints: ["Bool", "column-breakpoints"],
   mapScopes: ["Bool", "map-scopes"],
   removeCommandBarOptions: ["Bool", "remove-command-bar-options"],
   workers: ["Bool", "workers"],
-  eventListeners: ["Bool", "event-listeners"],
   outline: ["Bool", "outline"],
   codeFolding: ["Bool", "code-folding"],
   pausePoints: ["Bool", "pause-points"],
   skipPausing: ["Bool", "skip-pausing"],
   autocompleteExpression: ["Bool", "autocomplete-expressions"],
   mapExpressionBindings: ["Bool", "map-expression-bindings"],
   mapAwaitExpression: ["Bool", "map-await-expression"],
   componentPane: ["Bool", "component-pane"],
--- a/devtools/client/debugger/new/src/utils/quick-open.js
+++ b/devtools/client/debugger/new/src/utils/quick-open.js
@@ -1,16 +1,21 @@
 /* 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/>. */
 
 // @flow
 import classnames from "classnames";
 import { endTruncateStr } from "./utils";
-import { isPretty, getFilename, getSourceClassnames } from "./source";
+import {
+  isPretty,
+  getFilename,
+  getSourceClassnames,
+  getSourceQueryString
+} from "./source";
 
 import type { Location as BabelLocation } from "@babel/types";
 import type { Symbols } from "../reducers/ast";
 import type { QuickOpenType } from "../reducers/quick-open";
 import type { TabList } from "../reducers/tabs";
 import type { Source } from "../types";
 import type { SymbolDeclaration } from "../workers/parser";
 
@@ -48,19 +53,23 @@ export function parseLineColumn(query: s
       line: lineNumber,
       ...(!isNaN(columnNumber) ? { column: columnNumber } : null)
     };
   }
 }
 
 export function formatSourcesForList(source: Source, tabs: TabList) {
   const title = getFilename(source);
-  const subtitle = endTruncateStr(source.relativeUrl, 100);
+  const relativeUrlWithQuery = `${source.relativeUrl}${getSourceQueryString(
+    source
+  )}`;
+  const subtitle = endTruncateStr(relativeUrlWithQuery, 100);
+  const value = relativeUrlWithQuery;
   return {
-    value: source.relativeUrl,
+    value,
     title,
     subtitle,
     icon: tabs.some(tab => tab.url == source.url)
       ? "tab result-item-icon"
       : classnames(getSourceClassnames(source), "result-item-icon"),
     id: source.id,
     url: source.url
   };
@@ -88,23 +97,22 @@ export function formatSymbol(symbol: Sym
     subtitle: `${symbol.location.start.line}`,
     value: symbol.name,
     location: symbol.location
   };
 }
 
 export function formatSymbols(symbols: ?Symbols): FormattedSymbolDeclarations {
   if (!symbols || symbols.loading) {
-    return { variables: [], functions: [] };
+    return { functions: [] };
   }
 
-  const { variables, functions } = symbols;
+  const { functions } = symbols;
 
   return {
-    variables: variables.map(formatSymbol),
     functions: functions.map(formatSymbol)
   };
 }
 
 export function formatShortcutResults(): Array<QuickOpenResult> {
   return [
     {
       value: L10N.getStr("symbolSearch.search.functionsPlaceholder.title"),
--- a/devtools/client/debugger/new/src/utils/source-maps.js
+++ b/devtools/client/debugger/new/src/utils/source-maps.js
@@ -2,25 +2,25 @@
  * 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/>. */
 
 // @flow
 
 import { isOriginalId } from "devtools-source-map";
 import { getSource } from "../selectors";
 
-import type { Location, MappedLocation, Source } from "../types";
+import type { SourceLocation, MappedLocation, Source } from "../types";
 import { isGenerated } from "../utils/source";
 
 export async function getGeneratedLocation(
   state: Object,
   source: Source,
-  location: Location,
+  location: SourceLocation,
   sourceMaps: Object
-): Promise<Location> {
+): Promise<SourceLocation> {
   if (!isOriginalId(location.sourceId)) {
     return location;
   }
 
   const { line, sourceId, column } = await sourceMaps.getGeneratedLocation(
     location,
     source
   );
@@ -36,18 +36,18 @@ export async function getGeneratedLocati
     column: column === 0 ? undefined : column,
     sourceUrl: generatedSource.url
   };
 }
 
 export async function getMappedLocation(
   state: Object,
   sourceMaps: Object,
-  location: Location
-): Promise<Location> {
+  location: SourceLocation
+): Promise<SourceLocation> {
   const source = getSource(state, location.sourceId);
 
   if (!source) {
     return location;
   }
 
   if (isOriginalId(location.sourceId)) {
     return getGeneratedLocation(state, source, location, sourceMaps);
--- a/devtools/client/debugger/new/src/utils/source.js
+++ b/devtools/client/debugger/new/src/utils/source.js
@@ -16,17 +16,17 @@ import { endTruncateStr } from "./utils"
 import { truncateMiddleText } from "../utils/text";
 import { parse as parseURL } from "../utils/url";
 import { renderWasmText } from "./wasm";
 import { toEditorPosition } from "./editor";
 export { isMinified } from "./isMinified";
 import { getURL, getFileExtension } from "./sources-tree";
 import { prefs } from "./prefs";
 
-import type { Source, Location, JsSource } from "../types";
+import type { Source, SourceLocation, JsSource } from "../types";
 import type { SourceMetaDataType } from "../reducers/ast";
 import type { SymbolDeclarations } from "../workers/parser";
 
 type transformUrlCallback = string => string;
 
 export const sourceTypes = {
   coffee: "coffeescript",
   js: "javascript",
@@ -381,17 +381,17 @@ export function getMode(
 export function isLoaded(source: Source) {
   return source.loadedState === "loaded";
 }
 
 export function isLoading(source: Source) {
   return source.loadedState === "loading";
 }
 
-export function getTextAtPosition(source: ?Source, location: Location) {
+export function getTextAtPosition(source: ?Source, location: SourceLocation) {
   if (!source || !source.text) {
     return "";
   }
 
   const line = location.line;
   const column = location.column || 0;
 
   if (source.isWasm) {
--- a/devtools/client/debugger/new/src/workers/parser/getScopes/index.js
+++ b/devtools/client/debugger/new/src/workers/parser/getScopes/index.js
@@ -22,21 +22,21 @@ export type {
   BindingData,
   BindingDeclarationLocation,
   BindingLocation,
   BindingLocationType,
   BindingMetaValue,
   BindingType
 };
 
-import type { Location } from "../../../types";
+import type { SourceLocation } from "../../../types";
 
 let parsedScopesCache = new Map();
 
-export default function getScopes(location: Location): SourceScope[] {
+export default function getScopes(location: SourceLocation): SourceScope[] {
   const { sourceId } = location;
   let parsedScopes = parsedScopesCache.get(sourceId);
   if (!parsedScopes) {
     parsedScopes = parseSourceScopes(sourceId);
     parsedScopesCache.set(sourceId, parsedScopes);
   }
   return parsedScopes ? findScopes(parsedScopes, location) : [];
 }
@@ -45,17 +45,20 @@ export function clearScopes() {
   parsedScopesCache = new Map();
 }
 
 export { buildScopeList };
 
 /**
  * Searches all scopes and their bindings at the specific location.
  */
-function findScopes(scopes: ParsedScope[], location: Location): SourceScope[] {
+function findScopes(
+  scopes: ParsedScope[],
+  location: SourceLocation
+): SourceScope[] {
   // Find inner most in the tree structure.
   let searchInScopes: ?(ParsedScope[]) = scopes;
   const found = [];
   while (searchInScopes) {
     const foundOne = searchInScopes.some(s => {
       if (
         compareLocations(s.start, location) <= 0 &&
         compareLocations(location, s.end) < 0
@@ -77,14 +80,14 @@ function findScopes(scopes: ParsedScope[
       displayName: i.displayName,
       start: i.start,
       end: i.end,
       bindings: i.bindings
     };
   });
 }
 
-function compareLocations(a: Location, b: Location): number {
+function compareLocations(a: SourceLocation, b: SourceLocation): number {
   // According to type of Location.column can be undefined, if will not be the
   // case here, ignoring flow error.
   // $FlowIgnore
   return a.line == b.line ? a.column - b.column : a.line - b.line;
 }
--- a/devtools/client/debugger/new/src/workers/parser/index.js
+++ b/devtools/client/debugger/new/src/workers/parser/index.js
@@ -3,17 +3,17 @@
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
 import { workerUtils } from "devtools-utils";
 const { WorkerDispatcher } = workerUtils;
 
 import type { AstLocation, AstPosition, PausePoints } from "./types";
-import type { Location, Source, SourceId } from "../../types";
+import type { SourceLocation, Source, SourceId } from "../../types";
 import type { SourceScope } from "./getScopes/visitor";
 import type { SymbolDeclarations } from "./getSymbols";
 
 const dispatcher = new WorkerDispatcher();
 export const start = (url: string, win: any = window) =>
   dispatcher.start(url, win);
 export const stop = () => dispatcher.stop();
 
@@ -21,24 +21,25 @@ export const findOutOfScopeLocations = a
   sourceId: string,
   position: AstPosition
 ): Promise<AstLocation[]> =>
   dispatcher.invoke("findOutOfScopeLocations", sourceId, position);
 
 export const getNextStep = async (
   sourceId: SourceId,
   pausedPosition: AstPosition
-): Promise<?Location> =>
+): Promise<?SourceLocation> =>
   dispatcher.invoke("getNextStep", sourceId, pausedPosition);
 
 export const clearASTs = async (): Promise<void> =>
   dispatcher.invoke("clearASTs");
 
-export const getScopes = async (location: Location): Promise<SourceScope[]> =>
-  dispatcher.invoke("getScopes", location);
+export const getScopes = async (
+  location: SourceLocation
+): Promise<SourceScope[]> => dispatcher.invoke("getScopes", location);
 
 export const clearScopes = async (): Promise<void> =>
   dispatcher.invoke("clearScopes");
 
 export const clearSymbols = async (): Promise<void> =>
   dispatcher.invoke("clearSymbols");
 
 export const getSymbols = async (
--- a/devtools/client/debugger/new/test/mochitest/browser.ini
+++ b/devtools/client/debugger/new/test/mochitest/browser.ini
@@ -645,16 +645,17 @@ support-files =
   examples/frames.js
   examples/pause-points.js
   examples/script-mutate.js
   examples/script-switching-02.js
   examples/script-switching-01.js
   examples/times2.js
   examples/doc_rr_basic.html
   examples/doc_rr_continuous.html
+  examples/doc_rr_logs.html
   examples/doc_rr_recovery.html
   examples/doc_rr_error.html
 
 [browser_dbg-asm.js]
 [browser_dbg-async-stepping.js]
 [browser_dbg-sourcemapped-breakpoint-console.js]
 skip-if = (os == "win" && ccov) # Bug 1453549
 [browser_dbg-xhr-breakpoints.js]
@@ -778,8 +779,11 @@ skip-if = true # See bug 1481009
 [browser_dbg_rr_replay-01.js]
 skip-if = os != "mac" || debug || !nightly_build
 [browser_dbg_rr_replay-02.js]
 skip-if = os != "mac" || debug || !nightly_build
 [browser_dbg_rr_replay-03.js]
 skip-if = os != "mac" || debug || !nightly_build
 [browser_dbg_rr_console_warp-01.js]
 skip-if = os != "mac" || debug || !nightly_build
+[browser_dbg_rr_console_warp-02.js]
+skip-if = os != "mac" || debug || !nightly_build
+
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-breakpoints-actions.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-breakpoints-actions.js
@@ -3,25 +3,25 @@
 
 function openFirstBreakpointContextMenu(dbg){
   rightClickElement(dbg, "breakpointItem", 3);
 }
 
 
 // Tests to see if we can trigger a breakpoint action via the context menu
 add_task(async function() {
-  const dbg = await initDebugger("doc-scripts.html");
+  const dbg = await initDebugger("doc-scripts.html", "simple2");
   await selectSource(dbg, "simple2");
   await waitForSelectedSource(dbg, "simple2");
 
   await addBreakpoint(dbg, "simple2", 3);
 
   openFirstBreakpointContextMenu(dbg)
   // select "Remove breakpoint"
-  selectMenuItem(dbg, 1);
+  selectContextMenuItem(dbg, selectors.breakpointContextMenu.remove);
 
   await waitForState(dbg, state => dbg.selectors.getBreakpointCount(state) === 0);
   ok("successfully removed the breakpoint");
 });
 
 // Tests "disable others", "enable others" and "remove others" context actions
 add_task(async function() {
   const dbg = await initDebugger("doc-scripts.html");
@@ -31,48 +31,48 @@ add_task(async function() {
   await addBreakpoint(dbg, "simple1", 1);
   await addBreakpoint(dbg, "simple1", 4);
   await addBreakpoint(dbg, "simple1", 5);
   await addBreakpoint(dbg, "simple1", 6);
 
   openFirstBreakpointContextMenu(dbg);
   // select "Disable Others"
   let dispatched = waitForDispatch(dbg, "DISABLE_BREAKPOINT", 3);
-  selectMenuItem(dbg, 7);
+  selectContextMenuItem(dbg, selectors.breakpointContextMenu.disableOthers);
   await waitForState(dbg, state =>
     dbg.selectors.getBreakpointsList(state)
       .every(bp => (bp.location.line !== 1) === bp.disabled)
   );
   await dispatched;
   ok("breakpoint at 1 is the only enabled breakpoint");
 
   openFirstBreakpointContextMenu(dbg);
   // select "Disable All"
   dispatched = waitForDispatch(dbg, "DISABLE_ALL_BREAKPOINTS");
-  selectMenuItem(dbg, 9);
+  selectContextMenuItem(dbg, selectors.breakpointContextMenu.disableAll);
   await waitForState(dbg, state =>
     dbg.selectors.getBreakpointsList(state).every(bp => bp.disabled)
   );
   await dispatched;
   ok("all breakpoints are disabled");
 
   openFirstBreakpointContextMenu(dbg);
   // select "Enable Others"
   dispatched = waitForDispatch(dbg, "ENABLE_BREAKPOINT", 3);
-  selectMenuItem(dbg, 3);
+  selectContextMenuItem(dbg, selectors.breakpointContextMenu.enableOthers);
   await waitForState(dbg, state =>
     dbg.selectors.getBreakpointsList(state)
       .every(bp => (bp.location.line === 1) === bp.disabled)
   );
   await dispatched;
   ok("all breakpoints except line 1 are enabled");
 
   openFirstBreakpointContextMenu(dbg);
   // select "Remove Others"
   dispatched = waitForDispatch(dbg, "REMOVE_BREAKPOINT", 3);
-  selectMenuItem(dbg, 6);
+  selectContextMenuItem(dbg, selectors.breakpointContextMenu.removeOthers);
   await waitForState(dbg, state =>
     dbg.selectors.getBreakpointsList(state).length === 1 &&
     dbg.selectors.getBreakpointsList(state)[0].location.line === 1
   );
   await dispatched;
   ok("remaining breakpoint should be on line 1");
 });
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-breakpoints-cond.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-breakpoints-cond.js
@@ -33,29 +33,36 @@ function waitForElementFocus(dbg, el) {
 }
 
 async function assertConditionalBreakpointIsFocused(dbg) {
   const input = findElement(dbg, "conditionalPanelInput");
   await waitForElementFocus(dbg, input);
 }
 
 async function setConditionalBreakpoint(dbg, index, condition) {
+  const {
+    addConditionalBreakpoint,
+    editBreakpoint
+  } = selectors.gutterContextMenu;
+  // Make this work with either add or edit menu items
+  const selector = `${addConditionalBreakpoint},${editBreakpoint}`;
+
   rightClickElement(dbg, "gutter", index);
-  selectMenuItem(dbg, 2);
+  selectContextMenuItem(dbg, selector);
   await waitForElement(dbg, "conditionalPanelInput");
   await assertConditionalBreakpointIsFocused(dbg);
 
   // Position cursor reliably at the end of the text.
   pressKey(dbg, "End");
   type(dbg, condition);
   pressKey(dbg, "Enter");
 }
 
 add_task(async function() {
-  const dbg = await initDebugger("doc-scripts.html");
+  const dbg = await initDebugger("doc-scripts.html", "simple2");
   await selectSource(dbg, "simple2");
   await waitForSelectedSource(dbg, "simple2");
 
   await setConditionalBreakpoint(dbg, 5, "1");
   await waitForDispatch(dbg, "ADD_BREAKPOINT");
   let bp = findBreakpoint(dbg, "simple2", 5);
   is(bp.condition, "1", "breakpoint is created with the condition");
   assertEditorBreakpoint(dbg, 5, true);
@@ -81,13 +88,13 @@ add_task(async function() {
   bp = findBreakpoint(dbg, "simple2", 5);
   is(bp.condition, "1", "breakpoint is created with the condition");
   assertEditorBreakpoint(dbg, 5, true);
 
   const bpCondition = waitForDispatch(dbg, "SET_BREAKPOINT_CONDITION");
   //right click breakpoint in breakpoints list
   rightClickElement(dbg, "breakpointItem", 3)
   // select "remove condition";
-  selectMenuItem(dbg, 8);
+  selectContextMenuItem(dbg, selectors.breakpointContextMenu.removeCondition);
   await bpCondition;
   bp = findBreakpoint(dbg, "simple2", 5);
   is(bp.condition, undefined, "breakpoint condition removed");
 });
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-breakpoints-reloading.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-breakpoints-reloading.js
@@ -20,17 +20,17 @@ function addBreakpoint(dbg, line) {
 }
 
 function assertEditorBreakpoint(dbg, line) {
   const exists = !!getLineEl(dbg, line).querySelector(".new-breakpoint");
   ok(exists, `Breakpoint exists on line ${line}`);
 }
 
 add_task(async function() {
-  const dbg = await initDebugger("doc-scripts.html");
+  const dbg = await initDebugger("doc-scripts.html", "simple1.js");
   const {
     selectors: { getBreakpoints, getBreakpoint },
     getState
   } = dbg;
   const source = findSource(dbg, "simple1.js");
 
   await selectSource(dbg, source.url);
   await addBreakpoint(dbg, 5);
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-breakpoints.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-breakpoints.js
@@ -39,18 +39,19 @@ function findBreakpoint(dbg, url, line) 
   const {
     selectors: { getBreakpoint },
     getState
   } = dbg;
   const source = findSource(dbg, url);
   return getBreakpoint(getState(), { sourceId: source.id, line });
 }
 
+// Test enabling and disabling a breakpoint using the check boxes
 add_task(async function() {
-  const dbg = await initDebugger("doc-scripts.html");
+  const dbg = await initDebugger("doc-scripts.html", "simple2");
 
   // Create two breakpoints
   await selectSource(dbg, "simple2");
   await addBreakpoint(dbg, "simple2", 3);
   await addBreakpoint(dbg, "simple2", 5);
 
   // Disable the first one
   await disableBreakpoint(dbg, 0);
@@ -60,8 +61,36 @@ add_task(async function() {
   is(bp2.disabled, false, "second breakpoint is enabled");
 
   // Disable and Re-Enable the second one
   await disableBreakpoint(dbg, 1);
   await enableBreakpoint(dbg, 1);
   bp2 = findBreakpoint(dbg, "simple2", 5);
   is(bp2.disabled, false, "second breakpoint is enabled");
 });
+
+// Test enabling and disabling a breakpoint using the context menu
+add_task(async function() {
+  const dbg = await initDebugger("doc-scripts.html");
+  await selectSource(dbg, "simple2");
+  await addBreakpoint(dbg, "simple2", 3);
+  await addBreakpoint(dbg, "simple2", 5);
+
+  rightClickElement(dbg, "breakpointItem", 3);
+  const disableBreakpointDispatch = waitForDispatch(dbg, "DISABLE_BREAKPOINT");
+  selectContextMenuItem(dbg, selectors.breakpointContextMenu.disableSelf);
+  await disableBreakpointDispatch;
+
+  let bp1 = findBreakpoint(dbg, "simple2", 3);
+  let bp2 = findBreakpoint(dbg, "simple2", 5);
+  is(bp1.disabled, true, "first breakpoint is disabled");
+  is(bp2.disabled, false, "second breakpoint is enabled");
+
+  rightClickElement(dbg, "breakpointItem", 3);
+  const enableBreakpointDispatch = waitForDispatch(dbg, "ENABLE_BREAKPOINT");
+  selectContextMenuItem(dbg, selectors.breakpointContextMenu.enableSelf);
+  await enableBreakpointDispatch;
+
+  bp1 = findBreakpoint(dbg, "simple2", 3);
+  bp2 = findBreakpoint(dbg, "simple2", 5);
+  is(bp1.disabled, false, "first breakpoint is enabled");
+  is(bp2.disabled, false, "second breakpoint is enabled");
+});
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-console-async.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-console-async.js
@@ -35,17 +35,17 @@ async function hasMessage(dbg, msg) {
     msg
   ).length > 0)
 }
 
 add_task(async function() {
   Services.prefs.setBoolPref("devtools.toolbox.splitconsoleEnabled", true);
   Services.prefs.setBoolPref("devtools.debugger.features.map-await-expression", true);
 
-  const dbg = await initDebugger("doc-script-switching.html");
+  const dbg = await initDebugger("doc-script-switching.html", "switching-01");
 
   await selectSource(dbg, "switching-01");
 
   // open the console
   await getSplitConsole(dbg);
   ok(dbg.toolbox.splitConsole, "Split console is shown.");
 
   const webConsole = await dbg.toolbox.getPanel("webconsole")
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-console.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-console.js
@@ -1,34 +1,11 @@
-// Return a promise with a reference to jsterm, opening the split
-// console if necessary.  This cleans up the split console pref so
-// it won't pollute other tests.
-function getSplitConsole(dbg) {
-  const { toolbox, win } = dbg;
-
-  if (!win) {
-    win = toolbox.win;
-  }
-
-  if (!toolbox.splitConsole) {
-    pressKey(dbg, "Escape");
-  }
-
-  return new Promise(resolve => {
-    toolbox.getPanelWhenReady("webconsole").then(() => {
-      ok(toolbox.splitConsole, "Split console is shown.");
-      let jsterm = toolbox.getPanel("webconsole").hud.jsterm;
-      resolve(jsterm);
-    });
-  });
-}
-
 add_task(async function() {
   Services.prefs.setBoolPref("devtools.toolbox.splitconsoleEnabled", true);
-  const dbg = await initDebugger("doc-script-switching.html");
+  const dbg = await initDebugger("doc-script-switching.html", "switching-01");
 
   await selectSource(dbg, "switching-01");
 
   // open the console
   await getSplitConsole(dbg);
   ok(dbg.toolbox.splitConsole, "Split console is shown.");
 
   // close the console
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-editor-gutter.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-editor-gutter.js
@@ -21,17 +21,17 @@ function assertEditorBreakpoint(dbg, lin
     "Breakpoint " +
       (shouldExist ? "exists" : "does not exist") +
       " on line " +
       line
   );
 }
 
 add_task(async function() {
-  const dbg = await initDebugger("doc-scripts.html");
+  const dbg = await initDebugger("doc-scripts.html", "simple1.js");
   const {
     selectors: { getBreakpoint, getBreakpointCount },
     getState
   } = dbg;
   const source = findSource(dbg, "simple1.js");
 
   await selectSource(dbg, source.url);
 
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-editor-highlight.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-editor-highlight.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests that the editor will always highight the right line, no
 // matter if the source text doesn't exist yet or even if the source
 // doesn't exist.
 
 add_task(async function() {
-  const dbg = await initDebugger("doc-scripts.html");
+  const dbg = await initDebugger("doc-scripts.html", "long.js");
   const {
     selectors: { getSource },
     getState
   } = dbg;
   const sourceUrl = EXAMPLE_URL + "long.js";
 
   // The source itself doesn't even exist yet, and using
   // `selectSourceURL` will set a pending request to load this source
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-editor-mode.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-editor-mode.js
@@ -1,14 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests that the editor sets the correct mode for different file
 // types
 add_task(async function() {
-  const dbg = await initDebugger("doc-scripts.html");
+  const dbg = await initDebugger("doc-scripts.html", "simple1.js");
 
   await selectSource(dbg, "simple1.js");
   is(dbg.win.cm.getOption("mode").name, "javascript", "Mode is correct");
 
   await selectSource(dbg, "doc-scripts.html");
   is(dbg.win.cm.getOption("mode").name, "htmlmixed", "Mode is correct");
 });
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-keyboard-navigation.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-keyboard-navigation.js
@@ -1,15 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests that keyboard navigation into and out of debugger code editor
 
 add_task(async function() {
-  const dbg = await initDebugger("doc-scripts.html");
+  const dbg = await initDebugger("doc-scripts.html", "simple2");
   let doc = dbg.win.document;
 
   await selectSource(dbg, "simple2");
 
   await waitForElementWithSelector(dbg, ".CodeMirror");
   findElementWithSelector(dbg, ".CodeMirror").focus();
 
   // Enter code editor
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-minified.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-minified.js
@@ -9,19 +9,17 @@ function getScopeNodeLabel(dbg, index) {
 
 function getScopeNodeValue(dbg, index) {
   return findElement(dbg, "scopeValue", index).innerText;
 }
 
 add_task(async function() {
   await pushPref("devtools.debugger.features.map-scopes", true);
 
-  const dbg = await initDebugger("doc-minified2.html");
-
-  await waitForSources(dbg, "sum.js");
+  const dbg = await initDebugger("doc-minified2.html", "sum.js");
 
   await selectSource(dbg, "sum.js");
   await addBreakpoint(dbg, "sum.js", 2);
 
   invokeInTab("test");
   await waitForPaused(dbg);
 
   is(getScopeNodeLabel(dbg, 1), "sum", "check scope label");
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-outline-filter.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-outline-filter.js
@@ -6,17 +6,17 @@ function getItems(dbg) {
 }
 
 function getNthItem(dbg, index) {
   return findElement(dbg, "outlineItem", index);
 }
 
 // Tests the outline pane fuzzy filtering of outline items
 add_task(async function() {
-  const dbg = await initDebugger("doc-scripts.html");
+  const dbg = await initDebugger("doc-scripts.html", "long");
   await selectSource(dbg, "long", 1);
   findElementWithSelector(dbg, ".outline-tab").click();
 
   // turn off alphetical sort if active
   const alphabetizeButton = findElementWithSelector(
     dbg,
     ".outline-footer button"
   );
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-outline-pretty.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-outline-pretty.js
@@ -6,17 +6,17 @@ function getItems(dbg) {
 }
 
 function getNthItem(dbg, index) {
   return findElement(dbg, "outlineItem", index);
 }
 
 // Tests that the length of outline functions for original and pretty printed source matches
 add_task(async function () {
-  const dbg = await initDebugger("doc-scripts.html");
+  const dbg = await initDebugger("doc-scripts.html", "simple1");
   const {
     selectors: { getSelectedSource },
     getState
   } = dbg;
 
   await selectSource(dbg, "simple1");
   findElementWithSelector(dbg, ".outline-tab").click();
   const originalSource = getItems(dbg);
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-outline.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-outline.js
@@ -6,17 +6,17 @@ function getItems(dbg) {
 }
 
 function getNthItem(dbg, index) {
   return findElement(dbg, "outlineItem", index);
 }
 // Tests that the editor highlights the correct location when the
 // debugger pauses
 add_task(async function() {
-  const dbg = await initDebugger("doc-scripts.html");
+  const dbg = await initDebugger("doc-scripts.html", "simple1");
   const {
     selectors: { getSelectedSource },
     getState
   } = dbg;
 
   await selectSource(dbg, "simple1", 1);
 
   findElementWithSelector(dbg, ".outline-tab").click();
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-pause-points.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-pause-points.js
@@ -24,17 +24,17 @@ async function testCase(dbg, { name, cou
     locations.push([line, column]);
   });
 
   is(formatSteps(locations), formatSteps(steps), name);
   await resume(dbg);
 }
 
 add_task(async function test() {
-  const dbg = await initDebugger("doc-pause-points.html");
+  const dbg = await initDebugger("doc-pause-points.html", "pause-points.js");
 
   await selectSource(dbg, "pause-points.js")
   await testCase(dbg, {
     name: "statements",
     count: 7,
     steps: [[9,2], [10,4], [10,13], [11,2], [11,21], [12,2], [12,12], [13,0]]
   });
 
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-pretty-print-paused.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-pretty-print-paused.js
@@ -1,15 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests pretty-printing a source that is currently paused.
 
 add_task(async function() {
-  const dbg = await initDebugger("doc-minified.html");
+  const dbg = await initDebugger("doc-minified.html", "math.min.js");
 
   await selectSource(dbg, "math.min.js");
   await addBreakpoint(dbg, "math.min.js", 2);
 
   invokeInTab("arithmetic");
   await waitForPaused(dbg);
   assertPausedLocation(dbg);
 
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-pretty-print.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-pretty-print.js
@@ -1,15 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests basic pretty-printing functionality.
 
 add_task(async function() {
-  const dbg = await initDebugger("doc-minified.html");
+  const dbg = await initDebugger("doc-minified.html", "math.min.js");
 
   await selectSource(dbg, "math.min.js", 2);
   clickElement(dbg, "prettyPrintButton");
 
   await waitForSelectedSource(dbg, "math.min.js:formatted");
   const ppSrc = findSource(dbg, "math.min.js:formatted");
 
   ok(ppSrc, "Pretty-printed source exists");
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-preview-source-maps.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-preview-source-maps.js
@@ -28,23 +28,22 @@ function assertPreviewPopup(dbg, { field
     value,
     "Preview.result"
   );
   is(preview.updating, false, "Preview.updating");
   is(preview.expression, expression, "Preview.expression");
 }
 
 add_task(async function() {
-  const dbg = await initDebugger("doc-sourcemaps.html");
+  const dbg = await initDebugger("doc-sourcemaps.html", "entry.js", "output.js", "times2.js", "opts.js");
   const {
     selectors: { getSelectedSource },
     getState
   } = dbg;
 
-  await waitForSources(dbg, "entry.js", "output.js", "times2.js", "opts.js");
   await selectSource(dbg, "times2");
   await addBreakpoint(dbg, "times2", 2);
 
   invokeInTab("keepMeAlive");
   await waitForPaused(dbg);
   await waitForSelectedSource(dbg, "times2");
 
   info(`Test previewing in the original location`);
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-preview.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-preview.js
@@ -9,17 +9,17 @@ async function previews(dbg, fnName, pre
   await resume(dbg);
 
   info(`Ran tests for ${fnName}`);
 }
 
 // Test hovering on an object, which will show a popup and on a
 // simple value, which will show a tooltip.
 add_task(async function() {
-  const dbg = await initDebugger("doc-preview.html");
+  const dbg = await initDebugger("doc-preview.html", "preview.js");
   await selectSource(dbg, "preview.js");
 
   await previews(dbg, "empties", [
     // { line: 2, column: 9, expression: "a", result: '""' },
     // { line: 3, column: 9, expression: "b", result: "false" },
     { line: 4, column: 9, expression: "c", result: "undefined" },
     { line: 5, column: 9, expression: "d", result: "null" }
   ]);
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-quick-open.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-quick-open.js
@@ -105,26 +105,16 @@ add_task(async function() {
   is(resultCount(dbg), 2, "two function results");
 
   type(dbg, "@x");
   is(resultCount(dbg), 0, "no functions with 'x' in name");
 
   pressKey(dbg, "Escape");
   assertDisabled(dbg);
 
-  info("Testing variable search");
-  quickOpen(dbg, "sw2");
-  pressKey(dbg, "Enter");
-
-  quickOpen(dbg, "#");
-  is(resultCount(dbg), 1, "one variable result");
-  const results = findAllElements(dbg, "resultItems");
-  results.forEach(result => is(result.textContent, "x13"));
-  await waitToClose(dbg);
-
   info("Testing goto line:column");
   assertLine(dbg, 0);
   assertColumn(dbg, null);
   quickOpen(dbg, ":7:12");
   pressKey(dbg, "Enter");
   assertLine(dbg, 7);
   assertColumn(dbg, 12);
 
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-react-app.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-react-app.js
@@ -1,15 +1,13 @@
 add_task(async function() {
   const dbg = await initDebugger("doc-react.html", "App.js");
 
-  await waitForSource(dbg, "App.js");
   await addBreakpoint(dbg, "App.js", 11);
 
-
   info('Test previewing an immutable Map inside of a react component')
   invokeInTab("clickButton");
   await waitForPaused(dbg);
   await waitForState(
     dbg,
     state => dbg.selectors.getSelectedScopeMappings(state)
   );
 
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-reloading.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-reloading.js
@@ -13,19 +13,18 @@ async function waitForBreakpoint(dbg, lo
     state => {
       return dbg.selectors.getBreakpoint(dbg.getState(), location);
     },
     "Waiting for breakpoint"
   );
 }
 
 add_task(async function() {
-  const dbg = await initDebugger("ember/quickstart/dist/");
+  const dbg = await initDebugger("ember/quickstart/dist/", "ember-application/index.js");
 
-  await waitForSource(dbg, "ember-application/index.js");
   await selectSource(dbg, "ember-application/index.js");
 
   info("1. reload and hit breakpoint")
   await addBreakpoint(dbg, "ember-application/index.js", 4);
   reload(dbg, "ember/quickstart/dist/");
 
   info("2. Wait for sources to appear and then reload")
   await waitForDispatch(dbg, "ADD_SOURCES")
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-search-file.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-search-file.js
@@ -8,17 +8,17 @@ function waitForSearchState(dbg) {
 }
 
 function getFocusedEl(dbg) {
   let doc = dbg.win.document;
   return doc.activeElement;
 }
 
 add_task(async function() {
-  const dbg = await initDebugger("doc-scripts.html");
+  const dbg = await initDebugger("doc-scripts.html", "simple1.js");
   const {
     selectors: { getBreakpoints, getBreakpoint, getActiveSearch },
     getState
   } = dbg;
   const source = findSource(dbg, "simple1.js");
 
   await selectSource(dbg, source.url);
 
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-sourcemaps.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-sourcemaps.js
@@ -34,23 +34,22 @@ function getLineEl(dbg, line) {
 }
 
 function clickGutter(dbg, line) {
   clickElement(dbg, "gutter", line);
 }
 
 add_task(async function() {
   // NOTE: the CORS call makes the test run times inconsistent
-  const dbg = await initDebugger("doc-sourcemaps.html");
+  const dbg = await initDebugger("doc-sourcemaps.html", "entry.js", "output.js", "times2.js", "opts.js");
   const {
     selectors: { getBreakpoint, getBreakpointCount },
     getState
   } = dbg;
 
-  await waitForSources(dbg, "entry.js", "output.js", "times2.js", "opts.js");
   ok(true, "Original sources exist");
   const bundleSrc = findSource(dbg, "bundle.js");
 
   await selectSource(dbg, bundleSrc);
 
   await clickGutter(dbg, 13);
   await waitForDispatch(dbg, "ADD_BREAKPOINT");
   assertEditorBreakpoint(dbg, 13, true);
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-sourcemaps2.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-sourcemaps2.js
@@ -13,24 +13,22 @@ function assertBpInGutter(dbg, lineNumbe
 }
 
 // Tests loading sourcemapped sources, setting breakpoints, and
 // stepping in them.
 
 // This source map does not have source contents, so it's fetched separately
 add_task(async function() {
   // NOTE: the CORS call makes the test run times inconsistent
-  const dbg = await initDebugger("doc-sourcemaps2.html");
+  const dbg = await initDebugger("doc-sourcemaps2.html", "main.js", "main.min.js");
   const {
     selectors: { getBreakpoint, getBreakpointCount },
     getState
   } = dbg;
 
-  await waitForSources(dbg, "main.js", "main.min.js");
-
   ok(true, "Original sources exist");
   const mainSrc = findSource(dbg, "main.js");
 
   await selectSource(dbg, mainSrc);
 
   // Test that breakpoint is not off by a line.
   await addBreakpoint(dbg, mainSrc, 4);
   is(getBreakpointCount(getState()), 1, "One breakpoint exists");
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-sourcemaps3.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-sourcemaps3.js
@@ -5,24 +5,22 @@
 // inspecting restored scopes.
 requestLongerTimeout(2);
 
 // This source map does not have source contents, so it's fetched separately
 add_task(async function() {
   // NOTE: the CORS call makes the test run times inconsistent
   await pushPref("devtools.debugger.features.map-scopes", true);
 
-  const dbg = await initDebugger("doc-sourcemaps3.html");
+  const dbg = await initDebugger("doc-sourcemaps3.html", "bundle.js", "sorted.js", "test.js");
   const {
     selectors: { getBreakpoint, getBreakpointCount },
     getState
   } = dbg;
 
-  await waitForSources(dbg, "bundle.js", "sorted.js", "test.js");
-
   ok(true, "Original sources exist");
   const sortedSrc = findSource(dbg, "sorted.js");
 
   await selectSource(dbg, sortedSrc);
 
   // Test that breakpoint is not off by a line.
   await addBreakpoint(dbg, sortedSrc, 9);
   is(getBreakpointCount(getState()), 1, "One breakpoint exists");
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-sources-arrow-keys.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-sources-arrow-keys.js
@@ -1,15 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Test keyboard arrow behaviour
 add_task(async function() {
-  const dbg = await initDebugger("doc-sources.html");
-  await waitForSources(dbg, "simple1", "simple2", "nested-source", "long.js");
+  const dbg = await initDebugger("doc-sources.html", "simple1", "simple2", "nested-source", "long.js");
 
   await clickElement(dbg, "sourceDirectoryLabel", 2);
   await assertSourceCount(dbg, 7);
 
   // Right key on open dir
   await pressKey(dbg, "Right");
   await assertNodeIsFocused(dbg, 3);
 
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-sources-named-eval.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-sources-named-eval.js
@@ -13,26 +13,23 @@ async function waitForSourceCount(dbg, i
 
 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 dbg = await initDebugger("doc-sources.html", "simple1", "simple2", "nested-source", "long.js");
   const {
     selectors: { getSelectedSource },
     getState
   } = dbg;
 
-  await waitForSources(dbg, "simple1", "simple2", "nested-source", "long.js");
-
-
-  dump(`>>> contentTask: evaluate evaled.js\n`)
+  info(`>>> contentTask: evaluate evaled.js\n`)
   ContentTask.spawn(gBrowser.selectedBrowser, null, function() {
     content.eval("window.evaledFunc = function() {} //# sourceURL=evaled.js");
   });
 
   await waitForSourceCount(dbg, 3);
   // is(getLabel(dbg, 3), "evaled.js", "evaled exists");
   ok(true)
 });
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-sources-querystring.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-sources-querystring.js
@@ -5,24 +5,22 @@
 
 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-querystring.html");
+  const dbg = await initDebugger("doc-sources-querystring.html", "simple1.js?x=1", "simple1.js?x=2");
   const {
     selectors: { getSelectedSource },
     getState
   } = dbg;
 
-  await waitForSources(dbg, "simple1.js?x=1", "simple1.js?x=2");
-
   // Expand nodes and make sure more sources appear.
   await assertSourceCount(dbg, 2);
   await clickElement(dbg, "sourceDirectoryLabel", 2);
 
   const labels = [getLabel(dbg, 4), getLabel(dbg, 3)];
   is(
     labels.includes("simple1.js?x=1") && labels.includes("simple1.js?x=2"),
     true,
@@ -35,9 +33,13 @@ add_task(async function() {
   is(tab.innerText, "simple1.js?x=1", "Tab label is simple1.js?x=1");
   await addBreakpoint(dbg, "simple1.js?x=1", 6);
   const breakpointHeading = findElement(dbg, "breakpointItem", 2).innerText;
   is(
     breakpointHeading,
     "simple1.js?x=1",
     "Breakpoint heading is simple1.js?x=1"
   );
+
+  pressKey(dbg, "quickOpen");
+  type(dbg, "simple1.js?x");
+  ok(findElement(dbg, "resultItems")[0].innerText.includes("simple.js?x=1"));
 });
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-sources.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-sources.js
@@ -5,24 +5,22 @@
 
 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 dbg = await initDebugger("doc-sources.html", "simple1", "simple2", "nested-source", "long.js");
   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);
 
   await assertSourceCount(dbg, 7);
   await clickElement(dbg, "sourceDirectoryLabel", 3);
   await assertSourceCount(dbg, 8);
 
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-tabs-pretty-print.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-tabs-pretty-print.js
@@ -1,24 +1,24 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests re-opening pretty printed tabs on load
 
 add_task(async function() {
-  const dbg = await initDebugger("doc-minified.html");
+  const dbg = await initDebugger("doc-minified.html", "math.min.js");
 
   await selectSource(dbg, "math.min.js");
   clickElement(dbg, "prettyPrintButton");
   await waitForSource(dbg, "math.min.js:formatted");
   // Test reloading the debugger
   await waitForSelectedSource(dbg, "math.min.js:formatted");
   await reload(dbg);
 
   await waitForSelectedSource(dbg, "math.min.js:formatted");
   ok(true, "Pretty printed source is selected on reload");
-  
+
   await selectSource(dbg, "math.min.js:formatted");
   const source = findSource(dbg, "math.min.js:formatted");
   dbg.actions.showSource(source.id);
   const focusedTreeElement = findElementWithSelector(dbg, ".sources-list .focused .label");
   is(focusedTreeElement.textContent.trim(), "math.min.js", "Pretty printed source is selected in tree");
 });
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-tabs.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-tabs.js
@@ -3,17 +3,17 @@
 
 // Tests adding and removing tabs
 
 function countTabs(dbg) {
   return findElement(dbg, "sourceTabs").children.length;
 }
 
 add_task(async function() {
-  const dbg = await initDebugger("doc-scripts.html");
+  const dbg = await initDebugger("doc-scripts.html", "simple1", "simple2");
 
   await selectSource(dbg, "simple1");
   await selectSource(dbg, "simple2");
   is(countTabs(dbg), 2);
 
   // Test reloading the debugger
   await reload(dbg, "simple1", "simple2");
   is(countTabs(dbg), 2);
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-toggling-tools.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-toggling-tools.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Tests that you can switch tools, without losing your editor position
 
 add_task(async function() {
-  const dbg = await initDebugger("doc-scripts.html");
+  const dbg = await initDebugger("doc-scripts.html", "long");
 
   await selectSource(dbg, "long");
   getCM(dbg).scrollTo(0, 284);
 
   pressKey(dbg, "inspector");
   pressKey(dbg, "debugger");
 
   is(getCM(dbg).getScrollInfo().top, 284);
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-xhr-breakpoints.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-xhr-breakpoints.js
@@ -41,18 +41,17 @@ function getXHRBreakpointCheckbox(dbg) {
 
 async function clickPauseOnAny(dbg, expectedEvent) {
   getXHRBreakpointCheckbox(dbg).click();
   await waitForDispatch(dbg, expectedEvent);
 }
 
 // Tests that a basic XHR breakpoint works for get and POST is ignored
 add_task(async function() {
-  const dbg = await initDebugger("doc-xhr.html");
-  await waitForSources(dbg, "fetch.js");
+  const dbg = await initDebugger("doc-xhr.html", "fetch.js");
   await dbg.actions.setXHRBreakpoint("doc", "GET");
   invokeInTab("main", "doc-xhr.html");
   await waitForPaused(dbg);
   assertPausedLocation(dbg);
   resume(dbg);
 
   await dbg.actions.removeXHRBreakpoint(0);
   invokeInTab("main", "doc-xhr.html");
@@ -60,20 +59,17 @@ add_task(async function() {
 
   await dbg.actions.setXHRBreakpoint("doc-xhr.html", "POST");
   invokeInTab("main", "doc");
   assertNotPaused(dbg);
 });
 
 // Tests the "pause on any URL" checkbox works properly
 add_task(async function() {
-  const dbg = await initDebugger("doc-xhr.html");
-  await waitForSources(dbg, "fetch.js");
-
-  info("HERE 1");
+  const dbg = await initDebugger("doc-xhr.html", "fetch.js");
 
   // Enable pause on any URL
   await clickPauseOnAny(dbg, "SET_XHR_BREAKPOINT");
 
   invokeInTab("main", "doc-xhr.html");
   await waitForPaused(dbg);
   await resume(dbg);
 
@@ -82,17 +78,16 @@ add_task(async function() {
   await resume(dbg);
 
   invokeInTab("main", "README.md");
   await waitForPaused(dbg);
   await resume(dbg);
 
   // Disable pause on any URL
   await clickPauseOnAny(dbg, "DISABLE_XHR_BREAKPOINT");
-  info("HERE 4");
   invokeInTab("main", "README.md");
   assertNotPaused(dbg);
 
   // Turn off the checkbox
   await dbg.actions.removeXHRBreakpoint(0);
 });
 
 // Tests removal works properly
@@ -108,18 +103,18 @@ add_task(async function() {
   await addXHRBreakpoint(dbg, "4");
 
   // Remove "2"
   await removeXHRBreakpoint(dbg, 1);
 
   const listItems = getXHRBreakpointsElements(dbg);
   is(listItems.length, 3, "3 XHR breakpoints display in list");
   is(
-    pauseOnAnyCheckbox.checked, true, 
+    pauseOnAnyCheckbox.checked, true,
     "The pause on any is still checked"
   );
   is(
     getXHRBreakpointLabels(listItems).join(""),
     "134",
     "Only the desired breakpoint was removed"
   );
 
-});
\ No newline at end of file
+});
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-01.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-01.js
@@ -2,42 +2,41 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Test basic breakpoint functionality in web replay.
 async function test() {
   waitForExplicitFinish();
 
-  let tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" });
-  gBrowser.selectedTab = tab;
-  openTrustedLinkIn(EXAMPLE_URL + "doc_rr_basic.html", "current");
-  await once(Services.ppmm, "RecordingFinished");
+  const dbg = await attatchRecordingDebugger(
+    "doc_rr_basic.html",
+    { waitForRecording: true }
+  );
+  const {threadClient, tab, toolbox} = dbg;
 
-  let toolbox = await attachDebugger(tab), client = toolbox.threadClient;
-  await client.interrupt();
-  await setBreakpoint(client, "doc_rr_basic.html", 21);
+  await setBreakpoint(threadClient, "doc_rr_basic.html", 21);
 
   // Visit a lot of breakpoints so that we are sure we have crossed major
   // checkpoint boundaries.
-  await rewindToLine(client, 21);
-  await checkEvaluateInTopFrame(client, "number", 10);
-  await rewindToLine(client, 21);
-  await checkEvaluateInTopFrame(client, "number", 9);
-  await rewindToLine(client, 21);
-  await checkEvaluateInTopFrame(client, "number", 8);
-  await rewindToLine(client, 21);
-  await checkEvaluateInTopFrame(client, "number", 7);
-  await rewindToLine(client, 21);
-  await checkEvaluateInTopFrame(client, "number", 6);
-  await resumeToLine(client, 21);
-  await checkEvaluateInTopFrame(client, "number", 7);
-  await resumeToLine(client, 21);
-  await checkEvaluateInTopFrame(client, "number", 8);
-  await resumeToLine(client, 21);
-  await checkEvaluateInTopFrame(client, "number", 9);
-  await resumeToLine(client, 21);
-  await checkEvaluateInTopFrame(client, "number", 10);
+  await rewindToLine(threadClient, 21);
+  await checkEvaluateInTopFrame(threadClient, "number", 10);
+  await rewindToLine(threadClient, 21);
+  await checkEvaluateInTopFrame(threadClient, "number", 9);
+  await rewindToLine(threadClient, 21);
+  await checkEvaluateInTopFrame(threadClient, "number", 8);
+  await rewindToLine(threadClient, 21);
+  await checkEvaluateInTopFrame(threadClient, "number", 7);
+  await rewindToLine(threadClient, 21);
+  await checkEvaluateInTopFrame(threadClient, "number", 6);
+  await resumeToLine(threadClient, 21);
+  await checkEvaluateInTopFrame(threadClient, "number", 7);
+  await resumeToLine(threadClient, 21);
+  await checkEvaluateInTopFrame(threadClient, "number", 8);
+  await resumeToLine(threadClient, 21);
+  await checkEvaluateInTopFrame(threadClient, "number", 9);
+  await resumeToLine(threadClient, 21);
+  await checkEvaluateInTopFrame(threadClient, "number", 10);
 
   await toolbox.closeToolbox();
   await gBrowser.removeTab(tab);
   finish();
 }
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-02.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-02.js
@@ -2,28 +2,24 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Test unhandled divergence while evaluating at a breakpoint with Web Replay.
 async function test() {
   waitForExplicitFinish();
 
-  let tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" });
-  gBrowser.selectedTab = tab;
-  openTrustedLinkIn(EXAMPLE_URL + "doc_rr_basic.html", "current");
-  await once(Services.ppmm, "RecordingFinished");
+  const dbg = await attatchRecordingDebugger("doc_rr_basic.html", { waitForRecording: true });
+  const {threadClient, tab, toolbox} = dbg;
 
-  let toolbox = await attachDebugger(tab), client = toolbox.threadClient;
-  await client.interrupt();
-  await setBreakpoint(client, "doc_rr_basic.html", 21);
-  await rewindToLine(client, 21);
-  await checkEvaluateInTopFrame(client, "number", 10);
-  await checkEvaluateInTopFrameThrows(client, "window.alert(3)");
-  await checkEvaluateInTopFrame(client, "number", 10);
-  await checkEvaluateInTopFrameThrows(client, "window.alert(3)");
-  await checkEvaluateInTopFrame(client, "number", 10);
-  await checkEvaluateInTopFrame(client, "testStepping2()", undefined);
+  await setBreakpoint(threadClient, "doc_rr_basic.html", 21);
+  await rewindToLine(threadClient, 21);
+  await checkEvaluateInTopFrame(threadClient, "number", 10);
+  await checkEvaluateInTopFrameThrows(threadClient, "window.alert(3)");
+  await checkEvaluateInTopFrame(threadClient, "number", 10);
+  await checkEvaluateInTopFrameThrows(threadClient, "window.alert(3)");
+  await checkEvaluateInTopFrame(threadClient, "number", 10);
+  await checkEvaluateInTopFrame(threadClient, "testStepping2()", undefined);
 
   await toolbox.destroy();
   await gBrowser.removeTab(tab);
   finish();
 }
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-03.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-03.js
@@ -2,28 +2,26 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Test some issues when stepping around after hitting a breakpoint while recording.
 async function test() {
   waitForExplicitFinish();
 
-  let tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" });
-  gBrowser.selectedTab = tab;
-  openTrustedLinkIn(EXAMPLE_URL + "doc_rr_continuous.html", "current");
+  const dbg = await attatchRecordingDebugger("doc_rr_continuous.html");
+  const {threadClient, tab, toolbox} = dbg;
 
-  let toolbox = await attachDebugger(tab), client = toolbox.threadClient;
-  await client.interrupt();
-  await setBreakpoint(client, "doc_rr_continuous.html", 19);
-  await resumeToLine(client, 19);
-  await reverseStepOverToLine(client, 18);
-  await checkEvaluateInTopFrame(client, "SpecialPowers.Cu.recordReplayDirective(/* AlwaysTakeTemporarySnapshots */ 3)", undefined);
-  await stepInToLine(client, 22);
-  await setBreakpoint(client, "doc_rr_continuous.html", 24);
-  await resumeToLine(client, 24);
-  await setBreakpoint(client, "doc_rr_continuous.html", 22);
-  await rewindToLine(client, 22);
+  await threadClient.interrupt();
+  await setBreakpoint(threadClient, "doc_rr_continuous.html", 19);
+  await resumeToLine(threadClient, 19);
+  await reverseStepOverToLine(threadClient, 18);
+  await checkEvaluateInTopFrame(threadClient, "SpecialPowers.Cu.recordReplayDirective(/* AlwaysTakeTemporarySnapshots */ 3)", undefined);
+  await stepInToLine(threadClient, 22);
+  await setBreakpoint(threadClient, "doc_rr_continuous.html", 24);
+  await resumeToLine(threadClient, 24);
+  await setBreakpoint(threadClient, "doc_rr_continuous.html", 22);
+  await rewindToLine(threadClient, 22);
 
   await toolbox.destroy();
   await gBrowser.removeTab(tab);
   finish();
 }
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-04.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-04.js
@@ -3,34 +3,31 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Test navigating back to earlier breakpoints while recording, then resuming
 // recording.
 async function test() {
   waitForExplicitFinish();
 
-  let tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" });
-  gBrowser.selectedTab = tab;
-  openTrustedLinkIn(EXAMPLE_URL + "doc_rr_continuous.html", "current");
+  const dbg = await attatchRecordingDebugger("doc_rr_continuous.html");
+  const {threadClient, tab, toolbox} = dbg;
 
-  let toolbox = await attachDebugger(tab), client = toolbox.threadClient;
-  await client.interrupt();
-  await setBreakpoint(client, "doc_rr_continuous.html", 14);
-  await resumeToLine(client, 14);
-  let value = await evaluateInTopFrame(client, "number");
-  await resumeToLine(client, 14);
-  await checkEvaluateInTopFrame(client, "number", value + 1);
-  await rewindToLine(client, 14);
-  await checkEvaluateInTopFrame(client, "number", value);
-  await resumeToLine(client, 14);
-  await checkEvaluateInTopFrame(client, "number", value + 1);
-  await resumeToLine(client, 14);
-  await checkEvaluateInTopFrame(client, "number", value + 2);
-  await resumeToLine(client, 14);
-  await checkEvaluateInTopFrame(client, "number", value + 3);
-  await rewindToLine(client, 14);
-  await checkEvaluateInTopFrame(client, "number", value + 2);
+  await setBreakpoint(threadClient, "doc_rr_continuous.html", 14);
+  await resumeToLine(threadClient, 14);
+  let value = await evaluateInTopFrame(threadClient, "number");
+  await resumeToLine(threadClient, 14);
+  await checkEvaluateInTopFrame(threadClient, "number", value + 1);
+  await rewindToLine(threadClient, 14);
+  await checkEvaluateInTopFrame(threadClient, "number", value);
+  await resumeToLine(threadClient, 14);
+  await checkEvaluateInTopFrame(threadClient, "number", value + 1);
+  await resumeToLine(threadClient, 14);
+  await checkEvaluateInTopFrame(threadClient, "number", value + 2);
+  await resumeToLine(threadClient, 14);
+  await checkEvaluateInTopFrame(threadClient, "number", value + 3);
+  await rewindToLine(threadClient, 14);
+  await checkEvaluateInTopFrame(threadClient, "number", value + 2);
 
   await toolbox.destroy();
   await gBrowser.removeTab(tab);
   finish();
 }
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-05.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-05.js
@@ -3,29 +3,28 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Test hitting breakpoints when rewinding past the point where the breakpoint
 // script was created.
 async function test() {
   waitForExplicitFinish();
 
-  let tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" });
-  gBrowser.selectedTab = tab;
-  openTrustedLinkIn(EXAMPLE_URL + "doc_rr_basic.html", "current");
-  await once(Services.ppmm, "RecordingFinished");
+  const dbg = await attatchRecordingDebugger(
+    "doc_rr_basic.html", 
+    { waitForRecording: true }
+  );
 
-  let toolbox = await attachDebugger(tab), client = toolbox.threadClient;
-  await client.interrupt();
+  const {threadClient, tab, toolbox} = dbg;
 
   // Rewind to the beginning of the recording.
-  await rewindToLine(client, undefined);
+  await rewindToLine(threadClient, undefined);
 
-  await setBreakpoint(client, "doc_rr_basic.html", 21);
-  await resumeToLine(client, 21);
-  await checkEvaluateInTopFrame(client, "number", 1);
-  await resumeToLine(client, 21);
-  await checkEvaluateInTopFrame(client, "number", 2);
+  await setBreakpoint(threadClient, "doc_rr_basic.html", 21);
+  await resumeToLine(threadClient, 21);
+  await checkEvaluateInTopFrame(threadClient, "number", 1);
+  await resumeToLine(threadClient, 21);
+  await checkEvaluateInTopFrame(threadClient, "number", 2);
 
   await toolbox.destroy();
   await gBrowser.removeTab(tab);
   finish();
 }
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_console_warp-01.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_console_warp-01.js
@@ -1,100 +1,53 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
-var { HUDService } = require("devtools/client/webconsole/hudservice");
-
 // This functionality was copied from devtools/client/webconsole/test/mochitest/head.js,
 // since this test straddles both the web console and the debugger. I couldn't
 // figure out how to load that script directly here.
-
-function findMessages(hud, text, selector = ".message") {
-  const messages = hud.ui.outputNode.querySelectorAll(selector);
-  const elements = Array.prototype.filter.call(
-    messages,
-    (el) => el.textContent.includes(text)
-  );
-  return elements;
-}
-
-function waitForThreadEvents(console, eventName) {
+function waitForThreadEvents(threadClient, eventName) {
   info(`Waiting for thread event '${eventName}' to fire.`);
-  const thread = console.threadClient;
 
   return new Promise(function(resolve, reject) {
-    thread.addListener(eventName, function onEvent(eventName, ...args) {
+    threadClient.addListener(eventName, function onEvent(eventName, ...args) {
       info(`Thread event '${eventName}' fired.`);
-      thread.removeListener(eventName, onEvent);
+      threadClient.removeListener(eventName, onEvent);
       resolve.apply(resolve, args);
     });
   });
 }
 
-async function openContextMenu(hud, element) {
-  const onConsoleMenuOpened = hud.ui.consoleOutput.once("menu-open");
-  synthesizeContextMenuEvent(element);
-  await onConsoleMenuOpened;
-  const doc = hud.ui.consoleOutput.owner.chromeWindow.document;
-  return doc.getElementById("webconsole-menu");
-}
-
-function hideContextMenu(hud) {
-  const doc = hud.ui.consoleOutput.owner.chromeWindow.document;
-  const popup = doc.getElementById("webconsole-menu");
-  if (!popup) {
-    return Promise.resolve();
-  }
-
-  const onPopupHidden = once(popup, "popuphidden");
-  popup.hidePopup();
-  return onPopupHidden;
-}
 
 // Test basic console time warping functionality in web replay.
 async function test() {
   waitForExplicitFinish();
 
-  let tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" });
-  gBrowser.selectedTab = tab;
-  openTrustedLinkIn(EXAMPLE_URL + "doc_rr_error.html", "current");
-  await once(Services.ppmm, "RecordingFinished");
-
-  let console = await openToolboxForTab(tab, "webconsole");
-  let hud = console.getCurrentPanel().hud;
-  let messages = findMessages(hud, "Number 5");
-  ok(messages.length == 1, "Found one message");
-  let message = messages.pop();
+  const dbg = await attatchRecordingDebugger(
+    "doc_rr_error.html", 
+    { waitForRecording: true }
+  );
 
-  let menuPopup = await openContextMenu(hud, message);
-  let timeWarpItem = menuPopup.querySelector("#console-menu-time-warp");
-  ok(timeWarpItem, "Time warp menu item is available");
-  timeWarpItem.click();
-  await hideContextMenu(hud);
-
-  await once(Services.ppmm, "TimeWarpFinished");
+  const {tab, toolbox, threadClient} = dbg;
+  const console = await getSplitConsole(dbg);
+  const hud = console.hud;
 
-  await waitForThreadEvents(console, 'paused')
-  messages = findMessages(hud, "", ".paused");
-  ok(messages.length == 1, "Found one paused message");
+  await warpToMessage(hud, threadClient, "Number 5");
+  await threadClient.interrupt();
 
-  let toolbox = await attachDebugger(tab), client = toolbox.threadClient;
-  await client.interrupt();
-
-  await checkEvaluateInTopFrame(client, "number", 5);
+  await checkEvaluateInTopFrame(threadClient, "number", 5);
 
   // Initially we are paused inside the 'new Error()' call on line 19. The
   // first reverse step takes us to the start of that line.
-  await reverseStepOverToLine(client, 19);
-
-  await reverseStepOverToLine(client, 18);
-  await setBreakpoint(client, "doc_rr_error.html", 12);
-  await rewindToLine(client, 12);
-  await checkEvaluateInTopFrame(client, "number", 4);
-  await resumeToLine(client, 12);
-  await checkEvaluateInTopFrame(client, "number", 5);
+  await reverseStepOverToLine(threadClient, 19);
+  await reverseStepOverToLine(threadClient, 18);
+  await setBreakpoint(threadClient, "doc_rr_error.html", 12);
+  await rewindToLine(threadClient, 12);
+  await checkEvaluateInTopFrame(threadClient, "number", 4);
+  await resumeToLine(threadClient, 12);
+  await checkEvaluateInTopFrame(threadClient, "number", 5);
 
   await toolbox.destroy();
   await gBrowser.removeTab(tab);
   finish();
 }
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_console_warp-02.js
@@ -0,0 +1,48 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+
+// This functionality was copied from devtools/client/webconsole/test/mochitest/head.js,
+// since this test straddles both the web console and the debugger. I couldn't
+// figure out how to load that script directly here.
+function waitForThreadEvents(threadClient, eventName) {
+  info(`Waiting for thread event '${eventName}' to fire.`);
+
+  return new Promise(function(resolve, reject) {
+    threadClient.addListener(eventName, function onEvent(eventName, ...args) {
+      info(`Thread event '${eventName}' fired.`);
+      threadClient.removeListener(eventName, onEvent);
+      resolve.apply(resolve, args);
+    });
+  });
+}
+
+
+// Test basic console time warping functionality in web replay.
+async function test() {
+  waitForExplicitFinish();
+
+  const dbg = await attatchRecordingDebugger(
+    "doc_rr_logs.html", 
+    { waitForRecording: true }
+  );
+
+  const {tab, toolbox, threadClient} = dbg;
+  const console = await getSplitConsole(dbg);
+  const hud = console.hud;
+
+  let message = await warpToMessage(hud, threadClient, "number: 1");
+  ok(!message.classList.contains("paused-before"), "paused before message is not shown");
+
+  await stepOverToLine(threadClient, 18);
+  await reverseStepOverToLine(threadClient, 17);
+
+  message = findMessage(hud, "number: 1")
+  ok(message.classList.contains("paused-before"), "paused before message is shown");
+
+  await toolbox.destroy();
+  await gBrowser.removeTab(tab);
+  finish();
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/test/mochitest/examples/doc_rr_logs.html
@@ -0,0 +1,28 @@
+<html lang="en" dir="ltr">
+<head>
+  <meta charset="utf-8"/>
+</head>
+
+<body>
+<div id="maindiv" style="padding-top:50px">Hello World!</div>
+</body>
+<script>
+const cpmm = SpecialPowers.Services.cpmm;
+function recordingFinished() {
+  cpmm.sendAsyncMessage("RecordingFinished");
+}
+var number = 0;
+function f() {
+  number++;
+  console.log({ number }); 
+  number++;
+  console.log({ number }); 
+  number++;
+  console.log({ number }); 
+  window.setTimeout(recordingFinished);
+}
+window.setTimeout(f, 1);
+// Simulate a longer recording by marking major checkpoints whenever possible.
+SpecialPowers.Cu.recordReplayDirective(/* AlwaysMarkMajorCheckpoints */ 4);
+</script>
+</html>
--- a/devtools/client/debugger/new/test/mochitest/head.js
+++ b/devtools/client/debugger/new/test/mochitest/head.js
@@ -41,16 +41,27 @@ Services.scriptloader.loadSubScript(
 Services.scriptloader.loadSubScript(
   "chrome://mochitests/content/browser/devtools/client/debugger/new/test/mochitest/helpers.js",
   this
 );
 
 const EXAMPLE_URL =
   "http://example.com/browser/devtools/client/debugger/new/test/mochitest/examples/";
 
+
+async function waitUntilPredicate(predicate) {
+  let result;
+  await waitUntil(() => {
+    result = predicate();
+    return result;
+  })
+
+  return result;
+}
+
 // NOTE: still experimental, the screenshots might not be exactly correct
 async function takeScreenshot(dbg) {
   let canvas = dbg.win.document.createElementNS(
     "http://www.w3.org/1999/xhtml",
     "html:canvas"
   );
   let context = canvas.getContext("2d");
   canvas.width = dbg.win.innerWidth;
@@ -63,16 +74,52 @@ async function takeScreenshot(dbg) {
 // Attach a debugger to a tab, returning a promise that resolves with the
 // debugger's toolbox.
 async function attachDebugger(tab) {
   let target = await TargetFactory.forTab(tab);
   let toolbox = await gDevTools.showToolbox(target, "jsdebugger");
   return toolbox;
 }
 
+async function attatchRecordingDebugger(url, { waitForRecording } = { waitForRecording: false }) {
+  let tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" });
+  gBrowser.selectedTab = tab;
+  openTrustedLinkIn(EXAMPLE_URL + url, "current");
+  
+  if (waitForRecording) {
+    await once(Services.ppmm, "RecordingFinished");
+  }
+  const toolbox = await attachDebugger(tab);
+  const dbg = createDebuggerContext(toolbox)
+  const threadClient = dbg.toolbox.threadClient;
+
+  await threadClient.interrupt();
+  return {...dbg, tab, threadClient};
+}
+
+
+// Return a promise with a reference to jsterm, opening the split
+// console if necessary.  This cleans up the split console pref so
+// it won't pollute other tests.
+async function getSplitConsole(dbg) {
+  const { toolbox, win } = dbg;
+
+  if (!win) {
+    win = toolbox.win;
+  }
+
+  if (!toolbox.splitConsole) {
+    pressKey(dbg, "Escape");
+  }
+
+  await toolbox.openSplitConsole();
+  return toolbox.getPanel("webconsole");
+}
+
+
 // Return a promise that resolves when a breakpoint has been set.
 async function setBreakpoint(threadClient, expectedFile, lineno) {
   let {sources} = await threadClient.getSources();
   ok(sources.length == 1, "Got one source");
   ok(RegExp(expectedFile).test(sources[0].url), "Source is " + expectedFile);
   let sourceClient = threadClient.source(sources[0]);
   await sourceClient.setBreakpoint({ line: lineno });
 }
@@ -138,8 +185,78 @@ async function checkEvaluateInTopFrameTh
 }
 
 // Return a pathname that can be used for a new recording file.
 function newRecordingFile() {
   ChromeUtils.import("resource://gre/modules/osfile.jsm", this);
   return OS.Path.join(OS.Constants.Path.tmpDir,
                       "MochitestRecording" + Math.round(Math.random() * 1000000000));
 }
+
+
+async function warpToMessage(hud, threadClient, text) {
+  let messages = await waitForMessages(hud, text);
+  ok(messages.length == 1, "Found one message");
+  let message = messages.pop();
+
+  let menuPopup = await openConsoleContextMenu(hud, message);
+  console.log(`.>> menu`, menuPopup);
+
+
+  let timeWarpItem = menuPopup.querySelector("#console-menu-time-warp");
+  ok(timeWarpItem, "Time warp menu item is available");
+
+  timeWarpItem.click();
+
+  await Promise.all([
+    hideConsoleContextMenu(hud),
+    once(Services.ppmm, "TimeWarpFinished"),
+    waitForThreadEvents(threadClient, 'paused')
+  ]);
+
+  messages = findMessages(hud, "", ".paused");
+  ok(messages.length == 1, "Found one paused message");
+
+  return message;
+}
+
+
+function findMessage(hud, text, selector = ".message") {
+  return findMessages(hud, text, selector)[0]
+}
+
+function findMessages(hud, text, selector = ".message") {
+  const messages = hud.ui.outputNode.querySelectorAll(selector);
+  const elements = Array.prototype.filter.call(
+    messages,
+    (el) => el.textContent.includes(text)
+  );
+
+  if (elements.length == 0) {
+    return null;
+  }
+
+  return elements;
+}
+
+function waitForMessages(hud, text, selector = ".message") {
+  return waitUntilPredicate(() => findMessages(hud, text, selector))
+}
+
+async function openConsoleContextMenu(hud, element) {
+  const onConsoleMenuOpened = hud.ui.consoleOutput.once("menu-open");
+  synthesizeContextMenuEvent(element);
+  await onConsoleMenuOpened;
+  const doc = hud.ui.consoleOutput.owner.chromeWindow.document;
+  return doc.getElementById("webconsole-menu");
+}
+
+function hideConsoleContextMenu(hud) {
+  const doc = hud.ui.consoleOutput.owner.chromeWindow.document;
+  const popup = doc.getElementById("webconsole-menu");
+  if (!popup) {
+    return Promise.resolve();
+  }
+
+  const onPopupHidden = once(popup, "popuphidden");
+  popup.hidePopup();
+  return onPopupHidden;
+}
--- a/devtools/client/debugger/new/test/mochitest/helpers.js
+++ b/devtools/client/debugger/new/test/mochitest/helpers.js
@@ -491,20 +491,22 @@ function clearDebuggerPreferences() {
 /**
  * Intilializes the debugger.
  *
  * @memberof mochitest
  * @param {String} url
  * @return {Promise} dbg
  * @static
  */
-async function initDebugger(url) {
+async function initDebugger(url, ...sources) {
   clearDebuggerPreferences();
   const toolbox = await openNewTabAndToolbox(EXAMPLE_URL + url, "jsdebugger");
-  return createDebuggerContext(toolbox);
+  const dbg = createDebuggerContext(toolbox);
+  await waitForSources(dbg, ...sources)
+  return dbg;
 }
 
 async function initPane(url, pane) {
   clearDebuggerPreferences();
   return openNewTabAndToolbox(EXAMPLE_URL + url, pane);
 }
 
 window.resumeTest = undefined;
@@ -1023,23 +1025,37 @@ const selectors = {
     `.expressions-list .expression-container:nth-child(${i}) .object-delimiter + *`,
   expressionClose: i =>
     `.expressions-list .expression-container:nth-child(${i}) .close`,
   expressionInput: ".expressions-list  input.input-expression",
   expressionNodes: ".expressions-list .tree-node",
   scopesHeader: ".scopes-pane ._header",
   breakpointItem: i => `.breakpoints-list div:nth-of-type(${i})`,
   breakpointItems: `.breakpoints-list .breakpoint`,
+  breakpointContextMenu: {
+    disableSelf: "#node-menu-disable-self",
+    disableAll: "#node-menu-disable-all",
+    disableOthers: "#node-menu-disable-others",
+    enableSelf: "#node-menu-enable-self",
+    enableOthers: "#node-menu-enable-others",
+    remove: "#node-menu-delete-self",
+    removeOthers: "#node-menu-delete-other",
+    removeCondition: "#node-menu-remove-condition"
+  },
   scopes: ".scopes-list",
   scopeNode: i => `.scopes-list .tree-node:nth-child(${i}) .object-label`,
   scopeValue: i =>
     `.scopes-list .tree-node:nth-child(${i}) .object-delimiter + *`,
   frame: i => `.frames ul li:nth-child(${i})`,
   frames: ".frames ul li",
   gutter: i => `.CodeMirror-code *:nth-child(${i}) .CodeMirror-linenumber`,
+  gutterContextMenu: {
+    addConditionalBreakpoint: "#node-menu-add-conditional-breakpoint",
+    editBreakpoint: "#node-menu-edit-conditional-breakpoint"
+  },
   menuitem: i => `menupopup menuitem:nth-child(${i})`,
   pauseOnExceptions: ".pause-exceptions",
   breakpoint: ".CodeMirror-code > .new-breakpoint",
   highlightLine: ".CodeMirror-code > .highlight-line",
   debugLine: ".new-debug-line",
   debugErrorLine: ".new-debug-line-error",
   codeMirror: ".CodeMirror",
   resume: ".resume.active",
@@ -1140,24 +1156,24 @@ function rightClickElement(dbg, elementN
   const doc = dbg.win.document;
   return EventUtils.synthesizeMouseAtCenter(
     doc.querySelector(selector),
     { type: "contextmenu" },
     dbg.win
   );
 }
 
-function selectMenuItem(dbg, index) {
+function selectContextMenuItem(dbg, selector) {
   // the context menu is in the toolbox window
   const doc = dbg.toolbox.win.document;
 
   // there are several context menus, we want the one with the menu-api
   const popup = doc.querySelector('menupopup[menu-api="true"]');
 
-  const item = popup.querySelector(`menuitem:nth-child(${index})`);
+  const item = popup.querySelector(selector);
   return EventUtils.synthesizeMouseAtCenter(item, {}, dbg.toolbox.win);
 }
 
 /**
  * Toggles the debugger call stack accordian.
  *
  * @memberof mochitest/actions
  * @param {Object} dbg
--- a/devtools/client/inspector/flexbox/flexbox.js
+++ b/devtools/client/inspector/flexbox/flexbox.js
@@ -489,17 +489,17 @@ class FlexboxInspector {
         return;
       }
 
       if (!flexItemContainer && flexContainer.nodeFront === this.selection.nodeFront) {
         flexItemContainer = await this.getFlexContainerProps(this.selection.nodeFront,
           true);
       }
 
-      const highlighted = this._highlighters &&
+      const highlighted = this.inspector.isHighlighterReady &&
         flexContainer.nodeFront === this.highlighters.flexboxHighlighterShown;
       const color = await this.getOverlayColor();
 
       this.store.dispatch(updateFlexbox({
         color,
         flexContainer,
         flexItemContainer,
         highlighted,
--- a/devtools/client/inspector/inspector.js
+++ b/devtools/client/inspector/inspector.js
@@ -193,16 +193,20 @@ Inspector.prototype = {
   get highlighters() {
     if (!this._highlighters) {
       this._highlighters = new HighlightersOverlay(this);
     }
 
     return this._highlighters;
   },
 
+  get isHighlighterReady() {
+    return !!this._highlighters;
+  },
+
   get is3PaneModeEnabled() {
     if (this.target.chrome) {
       if (!this._is3PaneModeChromeEnabled) {
         this._is3PaneModeChromeEnabled = Services.prefs.getBoolPref(
           THREE_PANE_CHROME_ENABLED_PREF);
       }
 
       return this._is3PaneModeChromeEnabled;
--- a/devtools/client/locales/en-US/debugger.properties
+++ b/devtools/client/locales/en-US/debugger.properties
@@ -593,16 +593,20 @@ sourceFooter.mappedSourceTooltip=(Source
 # LOCALIZATION NOTE (sourceFooter.mappedSuffix): Text associated
 # with a mapped source.  Displays next to URLs in tree and tabs.
 sourceFooter.mappedSuffix=(mapped)
 
 # LOCALIZATION NOTE (sourceFooter.codeCoverage): Text associated
 # with a code coverage button
 sourceFooter.codeCoverage=Code coverage
 
+# LOCALIZATION NOTE (sourceFooter.currentCursorPosition): Text associated
+# with the current cursor line and column
+sourceFooter.currentCursorPosition=(%S, %S)
+
 # LOCALIZATION NOTE (sourceTabs.closeTabButtonTooltip): The tooltip that is displayed
 # for close tab button in source tabs.
 sourceTabs.closeTabButtonTooltip=Close tab
 
 # LOCALIZATION NOTE (scopes.header): Scopes right sidebar pane header.
 scopes.header=Scopes
 
 # LOCALIZATION NOTE (scopes.notAvailable): Scopes right sidebar pane message
--- a/devtools/client/preferences/debugger.js
+++ b/devtools/client/preferences/debugger.js
@@ -42,27 +42,27 @@ pref("devtools.debugger.tabsBlackBoxed",
 pref("devtools.debugger.pending-selected-location", "{}");
 pref("devtools.debugger.pending-breakpoints", "{}");
 pref("devtools.debugger.expressions", "[]");
 pref("devtools.debugger.file-search-case-sensitive", false);
 pref("devtools.debugger.file-search-whole-word", false);
 pref("devtools.debugger.file-search-regex-match", false);
 pref("devtools.debugger.project-directory-root", "");
 pref("devtools.debugger.skip-pausing", false);
+pref("devtools.debugger.logging", false);
 
 pref("devtools.debugger.features.wasm", true);
 pref("devtools.debugger.features.shortcuts", true);
 pref("devtools.debugger.features.root", true);
 pref("devtools.debugger.features.column-breakpoints", false);
 pref("devtools.debugger.features.chrome-scopes", false);
 pref("devtools.debugger.features.map-scopes", true);
 pref("devtools.debugger.features.remove-command-bar-options", false);
 pref("devtools.debugger.features.workers", true);
 pref("devtools.debugger.features.code-coverage", false);
-pref("devtools.debugger.features.event-listeners", false);
 pref("devtools.debugger.features.code-folding", false);
 pref("devtools.debugger.features.outline", true);
 pref("devtools.debugger.features.pause-points", true);
 pref("devtools.debugger.features.component-pane", false);
 pref("devtools.debugger.features.async-stepping", true);
 pref("devtools.debugger.features.skip-pausing", true);
 pref("devtools.debugger.features.autocomplete-expressions", false);
 pref("devtools.debugger.features.map-expression-bindings", true);
--- a/devtools/client/shared/components/reps/reps.js
+++ b/devtools/client/shared/components/reps/reps.js
@@ -1,18 +1,18 @@
 (function webpackUniversalModuleDefinition(root, factory) {
 	if(typeof exports === 'object' && typeof module === 'object')
-		module.exports = factory(require("devtools/client/shared/vendor/react"), require("Services"), require("devtools/client/shared/vendor/react-prop-types"), require("devtools/client/shared/vendor/react-dom-factories"), require("devtools/client/shared/vendor/react-redux"));
+		module.exports = factory(require("devtools/client/shared/vendor/react"), require("Services"), require("devtools/client/shared/vendor/react-redux"), require("devtools/client/shared/vendor/react-prop-types"), require("devtools/client/shared/vendor/react-dom-factories"));
 	else if(typeof define === 'function' && define.amd)
-		define(["devtools/client/shared/vendor/react", "Services", "devtools/client/shared/vendor/react-prop-types", "devtools/client/shared/vendor/react-dom-factories", "devtools/client/shared/vendor/react-redux"], factory);
+		define(["devtools/client/shared/vendor/react", "Services", "devtools/client/shared/vendor/react-redux", "devtools/client/shared/vendor/react-prop-types", "devtools/client/shared/vendor/react-dom-factories"], factory);
 	else {
-		var a = typeof exports === 'object' ? factory(require("devtools/client/shared/vendor/react"), require("Services"), require("devtools/client/shared/vendor/react-prop-types"), require("devtools/client/shared/vendor/react-dom-factories"), require("devtools/client/shared/vendor/react-redux")) : factory(root["devtools/client/shared/vendor/react"], root["Services"], root["devtools/client/shared/vendor/react-prop-types"], root["devtools/client/shared/vendor/react-dom-factories"], root["devtools/client/shared/vendor/react-redux"]);
+		var a = typeof exports === 'object' ? factory(require("devtools/client/shared/vendor/react"), require("Services"), require("devtools/client/shared/vendor/react-redux"), require("devtools/client/shared/vendor/react-prop-types"), require("devtools/client/shared/vendor/react-dom-factories")) : factory(root["devtools/client/shared/vendor/react"], root["Services"], root["devtools/client/shared/vendor/react-redux"], root["devtools/client/shared/vendor/react-prop-types"], root["devtools/client/shared/vendor/react-dom-factories"]);
 		for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
 	}
-})(typeof self !== 'undefined' ? self : this, function(__WEBPACK_EXTERNAL_MODULE_0__, __WEBPACK_EXTERNAL_MODULE_22__, __WEBPACK_EXTERNAL_MODULE_1758__, __WEBPACK_EXTERNAL_MODULE_1759__, __WEBPACK_EXTERNAL_MODULE_1763__) {
+})(typeof self !== 'undefined' ? self : this, function(__WEBPACK_EXTERNAL_MODULE_0__, __WEBPACK_EXTERNAL_MODULE_22__, __WEBPACK_EXTERNAL_MODULE_3592__, __WEBPACK_EXTERNAL_MODULE_3642__, __WEBPACK_EXTERNAL_MODULE_3643__) {
 return /******/ (function(modules) { // webpackBootstrap
 /******/ 	// The module cache
 /******/ 	var installedModules = {};
 /******/
 /******/ 	// The require function
 /******/ 	function __webpack_require__(moduleId) {
 /******/
 /******/ 		// Check if module is in cache
@@ -65,17 +65,17 @@ return /******/ (function(modules) { // 
 /******/
 /******/ 	// Object.prototype.hasOwnProperty.call
 /******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
 /******/
 /******/ 	// __webpack_public_path__
 /******/ 	__webpack_require__.p = "/assets/build";
 /******/
 /******/ 	// Load entry module and return exports
-/******/ 	return __webpack_require__(__webpack_require__.s = 2105);
+/******/ 	return __webpack_require__(__webpack_require__.s = 3730);
 /******/ })
 /************************************************************************/
 /******/ ({
 
 /***/ 0:
 /***/ (function(module, exports) {
 
 module.exports = __WEBPACK_EXTERNAL_MODULE_0__;
@@ -137,45 +137,59 @@ var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBP
 	} else {
 		window.classNames = classNames;
 	}
 }());
 
 
 /***/ }),
 
-/***/ 1758:
+/***/ 22:
 /***/ (function(module, exports) {
 
-module.exports = __WEBPACK_EXTERNAL_MODULE_1758__;
+module.exports = __WEBPACK_EXTERNAL_MODULE_22__;
+
+/***/ }),
+
+/***/ 3592:
+/***/ (function(module, exports) {
+
+module.exports = __WEBPACK_EXTERNAL_MODULE_3592__;
 
 /***/ }),
 
-/***/ 1759:
+/***/ 3642:
 /***/ (function(module, exports) {
 
-module.exports = __WEBPACK_EXTERNAL_MODULE_1759__;
+module.exports = __WEBPACK_EXTERNAL_MODULE_3642__;
 
 /***/ }),
 
-/***/ 1760:
+/***/ 3643:
+/***/ (function(module, exports) {
+
+module.exports = __WEBPACK_EXTERNAL_MODULE_3643__;
+
+/***/ }),
+
+/***/ 3644:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 /* 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/>. */
 
 // Dependencies
 const validProtocols = /(http|https|ftp|data|resource|chrome):/i;
 const tokenSplitRegex = /(\s|\'|\"|\\)+/;
 const ELLIPSIS = "\u2026";
-const dom = __webpack_require__(1759);
+const dom = __webpack_require__(3643);
 const { span } = dom;
 
 /**
  * Returns true if the given object is a grip (see RDP protocol)
  */
 function isGrip(object) {
   return object && object.actor;
 }
@@ -584,17 +598,17 @@ module.exports = {
   getGripType,
   tokenSplitRegex,
   ellipsisElement,
   ELLIPSIS
 };
 
 /***/ }),
 
-/***/ 1762:
+/***/ 3645:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 /* 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/>. */
@@ -604,69 +618,62 @@ module.exports = {
     TINY: Symbol("TINY"),
     SHORT: Symbol("SHORT"),
     LONG: Symbol("LONG")
   }
 };
 
 /***/ }),
 
-/***/ 1763:
-/***/ (function(module, exports) {
-
-module.exports = __WEBPACK_EXTERNAL_MODULE_1763__;
-
-/***/ }),
-
-/***/ 1767:
+/***/ 3647:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 /* 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/>. */
 
-__webpack_require__(1831);
+__webpack_require__(3672);
 
 // Load all existing rep templates
-const Undefined = __webpack_require__(1832);
-const Null = __webpack_require__(1833);
-const StringRep = __webpack_require__(1770);
-const Number = __webpack_require__(1834);
-const ArrayRep = __webpack_require__(1774);
-const Obj = __webpack_require__(1835);
-const SymbolRep = __webpack_require__(1836);
-const InfinityRep = __webpack_require__(1837);
-const NaNRep = __webpack_require__(1838);
-const Accessor = __webpack_require__(1839);
+const Undefined = __webpack_require__(3673);
+const Null = __webpack_require__(3674);
+const StringRep = __webpack_require__(3648);
+const Number = __webpack_require__(3675);
+const ArrayRep = __webpack_require__(3649);
+const Obj = __webpack_require__(3676);
+const SymbolRep = __webpack_require__(3677);
+const InfinityRep = __webpack_require__(3678);
+const NaNRep = __webpack_require__(3679);
+const Accessor = __webpack_require__(3680);
 
 // DOM types (grips)
-const Accessible = __webpack_require__(1840);
-const Attribute = __webpack_require__(1841);
-const DateTime = __webpack_require__(1842);
-const Document = __webpack_require__(1843);
-const DocumentType = __webpack_require__(1844);
-const Event = __webpack_require__(1845);
-const Func = __webpack_require__(1795);
-const PromiseRep = __webpack_require__(1846);
-const RegExp = __webpack_require__(1847);
-const StyleSheet = __webpack_require__(1848);
-const CommentNode = __webpack_require__(1849);
-const ElementNode = __webpack_require__(1850);
-const TextNode = __webpack_require__(1851);
-const ErrorRep = __webpack_require__(1797);
-const Window = __webpack_require__(1852);
-const ObjectWithText = __webpack_require__(1853);
-const ObjectWithURL = __webpack_require__(1854);
-const GripArray = __webpack_require__(1798);
-const GripMap = __webpack_require__(1800);
-const GripMapEntry = __webpack_require__(1801);
-const Grip = __webpack_require__(1784);
+const Accessible = __webpack_require__(3787);
+const Attribute = __webpack_require__(3681);
+const DateTime = __webpack_require__(3682);
+const Document = __webpack_require__(3683);
+const DocumentType = __webpack_require__(3684);
+const Event = __webpack_require__(3685);
+const Func = __webpack_require__(3658);
+const PromiseRep = __webpack_require__(3686);
+const RegExp = __webpack_require__(3687);
+const StyleSheet = __webpack_require__(3688);
+const CommentNode = __webpack_require__(3689);
+const ElementNode = __webpack_require__(3690);
+const TextNode = __webpack_require__(3691);
+const ErrorRep = __webpack_require__(3660);
+const Window = __webpack_require__(3692);
+const ObjectWithText = __webpack_require__(3693);
+const ObjectWithURL = __webpack_require__(3694);
+const GripArray = __webpack_require__(3661);
+const GripMap = __webpack_require__(3663);
+const GripMapEntry = __webpack_require__(3664);
+const Grip = __webpack_require__(3656);
 
 // List of all registered template.
 // XXX there should be a way for extensions to register a new
 // or modify an existing rep.
 const reps = [RegExp, StyleSheet, Event, DateTime, CommentNode, Accessible, ElementNode, TextNode, Attribute, Func, PromiseRep, ArrayRep, Document, DocumentType, Window, ObjectWithText, ObjectWithURL, ErrorRep, GripArray, GripMap, GripMapEntry, Grip, Undefined, Null, StringRep, Number, SymbolRep, InfinityRep, NaNRep, Accessor, Obj];
 
 /**
  * Generic rep that is used for rendering native JS types or an object.
@@ -751,43 +758,43 @@ module.exports = {
     Window
   },
   // Exporting for tests
   getRep
 };
 
 /***/ }),
 
-/***/ 1770:
+/***/ 3648:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 /* 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/>. */
 
 // Dependencies
-const PropTypes = __webpack_require__(1758);
+const PropTypes = __webpack_require__(3642);
 
 const {
   containsURL,
   isURL,
   escapeString,
   getGripType,
   rawCropString,
   sanitizeString,
   wrapRender,
   isGrip,
   tokenSplitRegex,
   ELLIPSIS
-} = __webpack_require__(1760);
-
-const dom = __webpack_require__(1759);
+} = __webpack_require__(3644);
+
+const dom = __webpack_require__(3643);
 const { a, span } = dom;
 
 /**
  * Renders a string. String value is enclosed within quotes.
  */
 StringRep.propTypes = {
   useQuotes: PropTypes.bool,
   escapeWhitespace: PropTypes.bool,
@@ -1025,33 +1032,33 @@ function supportsObject(object, noGrip =
 module.exports = {
   rep: wrapRender(StringRep),
   supportsObject,
   isLongString
 };
 
 /***/ }),
 
-/***/ 1774:
+/***/ 3649:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 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; };
 
 /* 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/>. */
 
 // Dependencies
-const dom = __webpack_require__(1759);
-const PropTypes = __webpack_require__(1758);
-const { wrapRender } = __webpack_require__(1760);
-const { MODE } = __webpack_require__(1762);
+const dom = __webpack_require__(3643);
+const PropTypes = __webpack_require__(3642);
+const { wrapRender } = __webpack_require__(3644);
+const { MODE } = __webpack_require__(3645);
 const { span } = dom;
 
 const ModePropType = PropTypes.oneOf(
 // @TODO Change this to Object.values when supported in Node's version of V8
 Object.keys(MODE).map(key => MODE[key]));
 
 /**
  * Renders an array. The array is enclosed by left and right bracket
@@ -1133,17 +1140,17 @@ function arrayIterator(props, array, max
  */
 ItemRep.propTypes = {
   object: PropTypes.any.isRequired,
   delim: PropTypes.string.isRequired,
   mode: ModePropType
 };
 
 function ItemRep(props) {
-  const { Rep } = __webpack_require__(1767);
+  const { Rep } = __webpack_require__(3647);
 
   const { object, delim, mode } = props;
   return span({}, Rep(_extends({}, props, {
     object: object,
     mode: mode
   })), delim);
 }
 
@@ -1165,34 +1172,34 @@ module.exports = {
   supportsObject,
   maxLengthMap,
   getLength,
   ModePropType
 };
 
 /***/ }),
 
-/***/ 1775:
+/***/ 3650:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 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; };
 
 /* 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/>. */
 
 // Dependencies
-const PropTypes = __webpack_require__(1758);
-const { maybeEscapePropertyName, wrapRender } = __webpack_require__(1760);
-const { MODE } = __webpack_require__(1762);
-
-const { span } = __webpack_require__(1759);
+const PropTypes = __webpack_require__(3642);
+const { maybeEscapePropertyName, wrapRender } = __webpack_require__(3644);
+const { MODE } = __webpack_require__(3645);
+
+const { span } = __webpack_require__(3643);
 
 /**
  * Property for Obj (local JS objects), Grip (remote JS objects)
  * and GripMap (remote JS maps and weakmaps) reps.
  * It's used to render object properties.
  */
 PropRep.propTypes = {
   // Property name.
@@ -1213,18 +1220,18 @@ PropRep.propTypes = {
 /**
  * Function that given a name, a delimiter and an object returns an array
  * of React elements representing an object property (e.g. `name: value`)
  *
  * @param {Object} props
  * @return {Array} Array of React elements.
  */
 function PropRep(props) {
-  const Grip = __webpack_require__(1784);
-  const { Rep } = __webpack_require__(1767);
+  const Grip = __webpack_require__(3656);
+  const { Rep } = __webpack_require__(3647);
 
   let { name, mode, equal, suppressQuotes } = props;
 
   let key;
   // The key can be a simple string, for plain objects,
   // or another object for maps and weakmaps.
   if (typeof name === "string") {
     if (!suppressQuotes) {
@@ -1245,71 +1252,71 @@ function PropRep(props) {
   }, equal), Rep(_extends({}, props))];
 }
 
 // Exports from this module
 module.exports = wrapRender(PropRep);
 
 /***/ }),
 
-/***/ 1780:
+/***/ 3655:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 /* 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 { MODE } = __webpack_require__(1762);
-const { REPS, getRep } = __webpack_require__(1767);
-const objectInspector = __webpack_require__(1855);
+const { MODE } = __webpack_require__(3645);
+const { REPS, getRep } = __webpack_require__(3647);
+const objectInspector = __webpack_require__(3695);
 
 const {
   parseURLEncodedText,
   parseURLParams,
   maybeEscapePropertyName,
   getGripPreviewItems
-} = __webpack_require__(1760);
+} = __webpack_require__(3644);
 
 module.exports = {
   REPS,
   getRep,
   MODE,
   maybeEscapePropertyName,
   parseURLEncodedText,
   parseURLParams,
   getGripPreviewItems,
   objectInspector
 };
 
 /***/ }),
 
-/***/ 1784:
+/***/ 3656:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 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; };
 
 /* 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/>. */
 
 // ReactJS
-const PropTypes = __webpack_require__(1758);
+const PropTypes = __webpack_require__(3642);
 
 // Dependencies
-const { interleave, isGrip, wrapRender } = __webpack_require__(1760);
-const PropRep = __webpack_require__(1775);
-const { MODE } = __webpack_require__(1762);
-
-const dom = __webpack_require__(1759);
+const { interleave, isGrip, wrapRender } = __webpack_require__(3644);
+const PropRep = __webpack_require__(3650);
+const { MODE } = __webpack_require__(3645);
+
+const dom = __webpack_require__(3643);
 const { span } = dom;
 
 /**
  * Renders generic grip. Grip is client representation
  * of remote JS object and is used as an input object
  * for this rep component.
  */
 GripRep.propTypes = {
@@ -1395,17 +1402,17 @@ function safePropIterator(props, object,
   } catch (err) {
     console.error(err);
   }
   return [];
 }
 
 function propIterator(props, object, max) {
   if (object.preview && Object.keys(object.preview).includes("wrappedValue")) {
-    const { Rep } = __webpack_require__(1767);
+    const { Rep } = __webpack_require__(3647);
 
     return [Rep({
       object: object.preview.wrappedValue,
       mode: props.mode || MODE.TINY,
       defaultRep: Grip
     })];
   }
 
@@ -1584,35 +1591,1337 @@ const Grip = {
   maxLengthMap
 };
 
 // Exports from this module
 module.exports = Grip;
 
 /***/ }),
 
-/***/ 1785:
+/***/ 3657:
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+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; };
+
+/* 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__(3665);
+const loadProperties = __webpack_require__(3666);
+const node = __webpack_require__(3667);
+const { nodeIsError, nodeIsPrimitive } = node;
+const selection = __webpack_require__(3698);
+
+const { MODE } = __webpack_require__(3645);
+const {
+  REPS: { Rep, Grip }
+} = __webpack_require__(3647);
+
+
+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(_extends({}, props, {
+    object: node.getValue(item),
+    mode: props.mode || MODE.TINY,
+    defaultRep: Grip
+  }));
+}
+
+module.exports = {
+  client,
+  loadProperties,
+  node,
+  renderRep,
+  selection,
+  shouldRenderRootsInReps
+};
+
+/***/ }),
+
+/***/ 3658:
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+/* 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/>. */
+
+// ReactJS
+const PropTypes = __webpack_require__(3642);
+
+// Reps
+const { getGripType, isGrip, cropString, wrapRender } = __webpack_require__(3644);
+const { MODE } = __webpack_require__(3645);
+
+const dom = __webpack_require__(3643);
+const { span } = dom;
+
+const IGNORED_SOURCE_URLS = ["debugger eval code"];
+
+/**
+ * This component represents a template for Function objects.
+ */
+FunctionRep.propTypes = {
+  object: PropTypes.object.isRequired,
+  parameterNames: PropTypes.array,
+  onViewSourceInDebugger: PropTypes.func
+};
+
+function FunctionRep(props) {
+  const { object: grip, onViewSourceInDebugger, recordTelemetryEvent } = props;
+
+  let jumpToDefinitionButton;
+  if (onViewSourceInDebugger && grip.location && grip.location.url && !IGNORED_SOURCE_URLS.includes(grip.location.url)) {
+    jumpToDefinitionButton = dom.button({
+      className: "jump-definition",
+      draggable: false,
+      title: "Jump to definition",
+      onClick: e => {
+        // Stop the event propagation so we don't trigger ObjectInspector
+        // expand/collapse.
+        e.stopPropagation();
+        if (recordTelemetryEvent) {
+          recordTelemetryEvent("jump_to_definition");
+        }
+        onViewSourceInDebugger(grip.location);
+      }
+    });
+  }
+
+  return span({
+    "data-link-actor-id": grip.actor,
+    className: "objectBox objectBox-function",
+    // Set dir="ltr" to prevent function parentheses from
+    // appearing in the wrong direction
+    dir: "ltr"
+  }, getTitle(grip, props), getFunctionName(grip, props), "(", ...renderParams(props), ")", jumpToDefinitionButton);
+}
+
+function getTitle(grip, props) {
+  const { mode } = props;
+
+  if (mode === MODE.TINY && !grip.isGenerator && !grip.isAsync) {
+    return null;
+  }
+
+  let title = mode === MODE.TINY ? "" : "function ";
+
+  if (grip.isGenerator) {
+    title = mode === MODE.TINY ? "* " : "function* ";
+  }
+
+  if (grip.isAsync) {
+    title = `${"async" + " "}${title}`;
+  }
+
+  return span({
+    className: "objectTitle"
+  }, title);
+}
+
+/**
+ * Returns a ReactElement representing the function name.
+ *
+ * @param {Object} grip : Function grip
+ * @param {Object} props: Function rep props
+ */
+function getFunctionName(grip, props = {}) {
+  let { functionName } = props;
+  let name;
+
+  if (functionName) {
+    const end = functionName.length - 1;
+    functionName = functionName.startsWith('"') && functionName.endsWith('"') ? functionName.substring(1, end) : functionName;
+  }
+
+  if (grip.displayName != undefined && functionName != undefined && grip.displayName != functionName) {
+    name = `${functionName}:${grip.displayName}`;
+  } else {
+    name = cleanFunctionName(grip.userDisplayName || grip.displayName || grip.name || props.functionName || "");
+  }
+
+  return cropString(name, 100);
+}
+
+const objectProperty = /([\w\d]+)$/;
+const arrayProperty = /\[(.*?)\]$/;
+const functionProperty = /([\w\d]+)[\/\.<]*?$/;
+const annonymousProperty = /([\w\d]+)\(\^\)$/;
+
+/**
+ * Decodes an anonymous naming scheme that
+ * spider monkey implements based on "Naming Anonymous JavaScript Functions"
+ * http://johnjbarton.github.io/nonymous/index.html
+ *
+ * @param {String} name : Function name to clean up
+ * @returns String
+ */
+function cleanFunctionName(name) {
+  for (const reg of [objectProperty, arrayProperty, functionProperty, annonymousProperty]) {
+    const match = reg.exec(name);
+    if (match) {
+      return match[1];
+    }
+  }
+
+  return name;
+}
+
+function renderParams(props) {
+  const { parameterNames = [] } = props;
+
+  return parameterNames.filter(param => param).reduce((res, param, index, arr) => {
+    res.push(span({ className: "param" }, param));
+    if (index < arr.length - 1) {
+      res.push(span({ className: "delimiter" }, ", "));
+    }
+    return res;
+  }, []);
+}
+
+// Registration
+function supportsObject(grip, noGrip = false) {
+  const type = getGripType(grip, noGrip);
+  if (noGrip === true || !isGrip(grip)) {
+    return type == "function";
+  }
+
+  return type == "Function";
+}
+
+// Exports from this module
+
+module.exports = {
+  rep: wrapRender(FunctionRep),
+  supportsObject,
+  cleanFunctionName,
+  // exported for testing purpose.
+  getFunctionName
+};
+
+/***/ }),
+
+/***/ 3659:
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+/* 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/>. */
+
+module.exports = {
+  ELEMENT_NODE: 1,
+  ATTRIBUTE_NODE: 2,
+  TEXT_NODE: 3,
+  CDATA_SECTION_NODE: 4,
+  ENTITY_REFERENCE_NODE: 5,
+  ENTITY_NODE: 6,
+  PROCESSING_INSTRUCTION_NODE: 7,
+  COMMENT_NODE: 8,
+  DOCUMENT_NODE: 9,
+  DOCUMENT_TYPE_NODE: 10,
+  DOCUMENT_FRAGMENT_NODE: 11,
+  NOTATION_NODE: 12,
+
+  // DocumentPosition
+  DOCUMENT_POSITION_DISCONNECTED: 0x01,
+  DOCUMENT_POSITION_PRECEDING: 0x02,
+  DOCUMENT_POSITION_FOLLOWING: 0x04,
+  DOCUMENT_POSITION_CONTAINS: 0x08,
+  DOCUMENT_POSITION_CONTAINED_BY: 0x10,
+  DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC: 0x20
+};
+
+/***/ }),
+
+/***/ 3660:
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+/* 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/>. */
+
+// ReactJS
+const PropTypes = __webpack_require__(3642);
+// Utils
+const { getGripType, isGrip, wrapRender } = __webpack_require__(3644);
+const { cleanFunctionName } = __webpack_require__(3658);
+const { isLongString } = __webpack_require__(3648);
+const { MODE } = __webpack_require__(3645);
+
+const dom = __webpack_require__(3643);
+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 when supported in Node's version of V8
+  mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
+  // An optional function that will be used to render the Error stacktrace.
+  renderStacktrace: PropTypes.func
+};
+
+function ErrorRep(props) {
+  const object = props.object;
+  const preview = object.preview;
+
+  let name;
+  if (preview && preview.name && preview.kind) {
+    switch (preview.kind) {
+      case "Error":
+        name = preview.name;
+        break;
+      case "DOMException":
+        name = preview.kind;
+        break;
+      default:
+        throw new Error("Unknown preview kind for the Error rep.");
+    }
+  } else {
+    name = "Error";
+  }
+
+  const content = [];
+
+  if (props.mode === MODE.TINY) {
+    content.push(name);
+  } else {
+    content.push(`${name}: "${preview.message}"`);
+  }
+
+  if (preview.stack && props.mode !== MODE.TINY) {
+    const stacktrace = props.renderStacktrace ? props.renderStacktrace(parseStackString(preview.stack)) : getStacktraceElements(props, preview);
+    content.push("\n", stacktrace);
+  }
+
+  return span({
+    "data-link-actor-id": object.actor,
+    className: "objectBox-stackTrace"
+  }, content);
+}
+
+/**
+ * Returns a React element reprensenting the Error stacktrace, i.e.
+ * transform error.stack from:
+ *
+ * semicolon@debugger eval code:1:109
+ * jkl@debugger eval code:1:63
+ * asdf@debugger eval code:1:28
+ * @debugger eval code:1:227
+ *
+ * Into a column layout:
+ *
+ * semicolon  (<anonymous>:8:10)
+ * jkl        (<anonymous>:5:10)
+ * asdf       (<anonymous>:2:10)
+ *            (<anonymous>:11:1)
+ */
+function getStacktraceElements(props, preview) {
+  const stack = [];
+  if (!preview.stack) {
+    return stack;
+  }
+
+  parseStackString(preview.stack).forEach((frame, index, frames) => {
+    let onLocationClick;
+    const {
+      filename,
+      lineNumber,
+      columnNumber,
+      functionName,
+      location
+    } = frame;
+
+    if (props.onViewSourceInDebugger && !IGNORED_SOURCE_URLS.includes(filename)) {
+      onLocationClick = e => {
+        // Don't trigger ObjectInspector expand/collapse.
+        e.stopPropagation();
+        props.onViewSourceInDebugger({
+          url: filename,
+          line: lineNumber,
+          column: columnNumber
+        });
+      };
+    }
+
+    stack.push("\t", span({
+      key: `fn${index}`,
+      className: "objectBox-stackTrace-fn"
+    }, cleanFunctionName(functionName)), " ", span({
+      key: `location${index}`,
+      className: "objectBox-stackTrace-location",
+      onClick: onLocationClick,
+      title: onLocationClick ? `View source in debugger → ${location}` : undefined
+    }, location), "\n");
+  });
+
+  return span({
+    key: "stack",
+    className: "objectBox-stackTrace-grid"
+  }, stack);
+}
+
+/**
+ * Parse a string that should represent a stack trace and returns an array of
+ * the frames. The shape of the frames are extremely important as they can then
+ * be processed here or in the toolbox by other components.
+ * @param {String} stack
+ * @returns {Array} Array of frames, which are object with the following shape:
+ *                  - {String} filename
+ *                  - {String} functionName
+ *                  - {String} location
+ *                  - {Number} columnNumber
+ *                  - {Number} lineNumber
+ */
+function parseStackString(stack) {
+  const res = [];
+  if (!stack) {
+    return res;
+  }
+
+  const isStacktraceALongString = isLongString(stack);
+  const stackString = isStacktraceALongString ? stack.initial : stack;
+
+  stackString.split("\n").forEach((frame, index, frames) => {
+    if (!frame) {
+      // Skip any blank lines
+      return;
+    }
+
+    // If the stacktrace is a longString, don't include the last frame in the
+    // array, since it is certainly incomplete.
+    // Can be removed when https://bugzilla.mozilla.org/show_bug.cgi?id=1448833
+    // is fixed.
+    if (isStacktraceALongString && index === frames.length - 1) {
+      return;
+    }
+
+    let functionName;
+    let location;
+
+    // Given the input: "functionName@scriptLocation:2:100"
+    // Result: [
+    //   "functionName@scriptLocation:2:100",
+    //   "functionName",
+    //   "scriptLocation:2:100"
+    // ]
+    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>";
+    }
+
+    // Given the input: "scriptLocation:2:100"
+    // Result:
+    // ["scriptLocation:2:100", "scriptLocation", "2", "100"]
+    const locationParts = location.match(/^(.*):(\d+):(\d+)$/);
+
+    if (location && locationParts) {
+      const [, filename, line, column] = locationParts;
+      res.push({
+        filename,
+        functionName,
+        location,
+        columnNumber: Number(column),
+        lineNumber: Number(line)
+      });
+    }
+  });
+
+  return res;
+}
+
+// Registration
+function supportsObject(object, noGrip = false) {
+  if (noGrip === true || !isGrip(object)) {
+    return false;
+  }
+  return object.preview && getGripType(object, noGrip) === "Error" || object.class === "DOMException";
+}
+
+// Exports from this module
+module.exports = {
+  rep: wrapRender(ErrorRep),
+  supportsObject
+};
+
+/***/ }),
+
+/***/ 3661:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 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; };
 
 /* 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 { maybeEscapePropertyName } = __webpack_require__(1760);
-const ArrayRep = __webpack_require__(1774);
-const GripArrayRep = __webpack_require__(1798);
-const GripMap = __webpack_require__(1800);
-const GripMapEntryRep = __webpack_require__(1801);
-const ErrorRep = __webpack_require__(1797);
-const { isLongString } = __webpack_require__(1770);
+// Dependencies
+const PropTypes = __webpack_require__(3642);
+
+const { lengthBubble } = __webpack_require__(3662);
+const {
+  interleave,
+  getGripType,
+  isGrip,
+  wrapRender,
+  ellipsisElement
+} = __webpack_require__(3644);
+const { MODE } = __webpack_require__(3645);
+
+const dom = __webpack_require__(3643);
+const { span } = dom;
+const { ModePropType } = __webpack_require__(3649);
+const DEFAULT_TITLE = "Array";
+
+/**
+ * Renders an array. The array is enclosed by left and right bracket
+ * and the max number of rendered items depends on the current mode.
+ */
+GripArray.propTypes = {
+  object: PropTypes.object.isRequired,
+  // @TODO Change this to Object.values when supported in Node's version of V8
+  mode: ModePropType,
+  provider: PropTypes.object,
+  onDOMNodeMouseOver: PropTypes.func,
+  onDOMNodeMouseOut: PropTypes.func,
+  onInspectIconClick: PropTypes.func
+};
+
+function GripArray(props) {
+  const { object, mode = MODE.SHORT } = props;
+
+  let brackets;
+  const needSpace = function (space) {
+    return space ? { left: "[ ", right: " ]" } : { left: "[", right: "]" };
+  };
+
+  const config = {
+    "data-link-actor-id": object.actor,
+    className: "objectBox objectBox-array"
+  };
+
+  const title = getTitle(props, object);
+
+  if (mode === MODE.TINY) {
+    const isEmpty = getLength(object) === 0;
+
+    // Omit bracketed ellipsis for non-empty non-Array arraylikes (f.e: Sets).
+    if (!isEmpty && object.class !== "Array") {
+      return span(config, title);
+    }
+
+    brackets = needSpace(false);
+    return span(config, title, span({
+      className: "arrayLeftBracket"
+    }, brackets.left), isEmpty ? null : ellipsisElement, span({
+      className: "arrayRightBracket"
+    }, brackets.right));
+  }
+
+  const max = maxLengthMap.get(mode);
+  const items = arrayIterator(props, object, max);
+  brackets = needSpace(items.length > 0);
+
+  return span({
+    "data-link-actor-id": object.actor,
+    className: "objectBox objectBox-array"
+  }, title, span({
+    className: "arrayLeftBracket"
+  }, brackets.left), ...interleave(items, ", "), span({
+    className: "arrayRightBracket"
+  }, brackets.right), span({
+    className: "arrayProperties",
+    role: "group"
+  }));
+}
+
+function getLength(grip) {
+  if (!grip.preview) {
+    return 0;
+  }
+
+  return grip.preview.length || grip.preview.childNodesLength || 0;
+}
+
+function getTitle(props, object) {
+  const objectLength = getLength(object);
+  const isEmpty = objectLength === 0;
+
+  let title = props.title || object.class || DEFAULT_TITLE;
+
+  const length = lengthBubble({
+    object,
+    mode: props.mode,
+    maxLengthMap,
+    getLength
+  });
+
+  if (props.mode === MODE.TINY) {
+    if (isEmpty) {
+      if (object.class === DEFAULT_TITLE) {
+        return null;
+      }
+
+      return span({ className: "objectTitle" }, `${title} `);
+    }
+
+    let trailingSpace;
+    if (object.class === DEFAULT_TITLE) {
+      title = null;
+      trailingSpace = " ";
+    }
+
+    return span({ className: "objectTitle" }, title, length, trailingSpace);
+  }
+
+  return span({ className: "objectTitle" }, title, length, " ");
+}
+
+function getPreviewItems(grip) {
+  if (!grip.preview) {
+    return null;
+  }
+
+  return grip.preview.items || grip.preview.childNodes || [];
+}
+
+function arrayIterator(props, grip, max) {
+  const { Rep } = __webpack_require__(3647);
+
+  let items = [];
+  const gripLength = getLength(grip);
+
+  if (!gripLength) {
+    return items;
+  }
+
+  const previewItems = getPreviewItems(grip);
+  const provider = props.provider;
+
+  let emptySlots = 0;
+  let foldedEmptySlots = 0;
+  items = previewItems.reduce((res, itemGrip) => {
+    if (res.length >= max) {
+      return res;
+    }
+
+    let object;
+    try {
+      if (!provider && itemGrip === null) {
+        emptySlots++;
+        return res;
+      }
+
+      object = provider ? provider.getValue(itemGrip) : itemGrip;
+    } catch (exc) {
+      object = exc;
+    }
+
+    if (emptySlots > 0) {
+      res.push(getEmptySlotsElement(emptySlots));
+      foldedEmptySlots = foldedEmptySlots + emptySlots - 1;
+      emptySlots = 0;
+    }
+
+    if (res.length < max) {
+      res.push(Rep(_extends({}, props, {
+        object,
+        mode: MODE.TINY,
+        // Do not propagate title to array items reps
+        title: undefined
+      })));
+    }
+
+    return res;
+  }, []);
+
+  // Handle trailing empty slots if there are some.
+  if (items.length < max && emptySlots > 0) {
+    items.push(getEmptySlotsElement(emptySlots));
+    foldedEmptySlots = foldedEmptySlots + emptySlots - 1;
+  }
+
+  const itemsShown = items.length + foldedEmptySlots;
+  if (gripLength > itemsShown) {
+    items.push(ellipsisElement);
+  }
+
+  return items;
+}
+
+function getEmptySlotsElement(number) {
+  // TODO: Use l10N - See https://github.com/devtools-html/reps/issues/141
+  return `<${number} empty slot${number > 1 ? "s" : ""}>`;
+}
+
+function supportsObject(grip, noGrip = false) {
+  if (noGrip === true || !isGrip(grip)) {
+    return false;
+  }
+
+  return grip.preview && (grip.preview.kind == "ArrayLike" || getGripType(grip, noGrip) === "DocumentFragment");
+}
+
+const maxLengthMap = new Map();
+maxLengthMap.set(MODE.SHORT, 3);
+maxLengthMap.set(MODE.LONG, 10);
+
+// Exports from this module
+module.exports = {
+  rep: wrapRender(GripArray),
+  supportsObject,
+  maxLengthMap,
+  getLength
+};
+
+/***/ }),
+
+/***/ 3662:
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+/* 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 PropTypes = __webpack_require__(3642);
+
+const { wrapRender } = __webpack_require__(3644);
+const { MODE } = __webpack_require__(3645);
+const { ModePropType } = __webpack_require__(3649);
+
+const dom = __webpack_require__(3643);
+const { span } = dom;
+
+GripLengthBubble.propTypes = {
+  object: PropTypes.object.isRequired,
+  maxLengthMap: PropTypes.instanceOf(Map).isRequired,
+  getLength: PropTypes.func.isRequired,
+  mode: ModePropType,
+  visibilityThreshold: PropTypes.number
+};
+
+function GripLengthBubble(props) {
+  const {
+    object,
+    mode = MODE.SHORT,
+    visibilityThreshold = 2,
+    maxLengthMap,
+    getLength,
+    showZeroLength = false
+  } = props;
+
+  const length = getLength(object);
+  const isEmpty = length === 0;
+  const isObvious = [MODE.SHORT, MODE.LONG].includes(mode) && length > 0 && length <= maxLengthMap.get(mode) && length <= visibilityThreshold;
+  if (isEmpty && !showZeroLength || isObvious) {
+    return "";
+  }
+
+  return span({
+    className: "objectLengthBubble"
+  }, `(${length})`);
+}
+
+module.exports = {
+  lengthBubble: wrapRender(GripLengthBubble)
+};
+
+/***/ }),
+
+/***/ 3663:
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+/* 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/>. */
+
+// Dependencies
+
+const { lengthBubble } = __webpack_require__(3662);
+const PropTypes = __webpack_require__(3642);
+const {
+  interleave,
+  isGrip,
+  wrapRender,
+  ellipsisElement
+} = __webpack_require__(3644);
+const PropRep = __webpack_require__(3650);
+const { MODE } = __webpack_require__(3645);
+const { ModePropType } = __webpack_require__(3649);
+
+const { span } = __webpack_require__(3643);
+
+/**
+ * Renders an map. A map is represented by a list of its
+ * entries enclosed in curly brackets.
+ */
+GripMap.propTypes = {
+  object: PropTypes.object,
+  // @TODO Change this to Object.values when supported in Node's version of V8
+  mode: ModePropType,
+  isInterestingEntry: PropTypes.func,
+  onDOMNodeMouseOver: PropTypes.func,
+  onDOMNodeMouseOut: PropTypes.func,
+  onInspectIconClick: PropTypes.func,
+  title: PropTypes.string
+};
+
+function GripMap(props) {
+  const { mode, object } = props;
+
+  const config = {
+    "data-link-actor-id": object.actor,
+    className: "objectBox objectBox-object"
+  };
+
+  const title = getTitle(props, object);
+  const isEmpty = getLength(object) === 0;
+
+  if (isEmpty || mode === MODE.TINY) {
+    return span(config, title);
+  }
+
+  const propsArray = safeEntriesIterator(props, object, maxLengthMap.get(mode));
+
+  return span(config, title, span({
+    className: "objectLeftBrace"
+  }, " { "), ...interleave(propsArray, ", "), span({
+    className: "objectRightBrace"
+  }, " }"));
+}
+
+function getTitle(props, object) {
+  const title = props.title || (object && object.class ? object.class : "Map");
+  return span({
+    className: "objectTitle"
+  }, title, lengthBubble({
+    object,
+    mode: props.mode,
+    maxLengthMap,
+    getLength,
+    showZeroLength: true
+  }));
+}
+
+function safeEntriesIterator(props, object, max) {
+  max = typeof max === "undefined" ? 3 : max;
+  try {
+    return entriesIterator(props, object, max);
+  } catch (err) {
+    console.error(err);
+  }
+  return [];
+}
+
+function entriesIterator(props, object, max) {
+  // Entry filter. Show only interesting entries to the user.
+  const isInterestingEntry = props.isInterestingEntry || ((type, value) => {
+    return type == "boolean" || type == "number" || type == "string" && value.length != 0;
+  });
+
+  const mapEntries = object.preview && object.preview.entries ? object.preview.entries : [];
+
+  let indexes = getEntriesIndexes(mapEntries, max, isInterestingEntry);
+  if (indexes.length < max && indexes.length < mapEntries.length) {
+    // There are not enough entries yet, so we add uninteresting entries.
+    indexes = indexes.concat(getEntriesIndexes(mapEntries, max - indexes.length, (t, value, name) => {
+      return !isInterestingEntry(t, value, name);
+    }));
+  }
+
+  const entries = getEntries(props, mapEntries, indexes);
+  if (entries.length < getLength(object)) {
+    // There are some undisplayed entries. Then display "…".
+    entries.push(ellipsisElement);
+  }
+
+  return entries;
+}
+
+/**
+ * Get entries ordered by index.
+ *
+ * @param {Object} props Component props.
+ * @param {Array} entries Entries array.
+ * @param {Array} indexes Indexes of entries.
+ * @return {Array} Array of PropRep.
+ */
+function getEntries(props, entries, indexes) {
+  const { onDOMNodeMouseOver, onDOMNodeMouseOut, onInspectIconClick } = props;
+
+  // Make indexes ordered by ascending.
+  indexes.sort(function (a, b) {
+    return a - b;
+  });
+
+  return indexes.map((index, i) => {
+    const [key, entryValue] = entries[index];
+    const value = entryValue.value !== undefined ? entryValue.value : entryValue;
+
+    return PropRep({
+      name: key,
+      equal: " \u2192 ",
+      object: value,
+      mode: MODE.TINY,
+      onDOMNodeMouseOver,
+      onDOMNodeMouseOut,
+      onInspectIconClick
+    });
+  });
+}
+
+/**
+ * Get the indexes of entries in the map.
+ *
+ * @param {Array} entries Entries array.
+ * @param {Number} max The maximum length of indexes array.
+ * @param {Function} filter Filter the entry you want.
+ * @return {Array} Indexes of filtered entries in the map.
+ */
+function getEntriesIndexes(entries, max, filter) {
+  return entries.reduce((indexes, [key, entry], i) => {
+    if (indexes.length < max) {
+      const value = entry && entry.value !== undefined ? entry.value : entry;
+      // Type is specified in grip's "class" field and for primitive
+      // values use typeof.
+      const type = (value && value.class ? value.class : typeof value).toLowerCase();
+
+      if (filter(type, value, key)) {
+        indexes.push(i);
+      }
+    }
+
+    return indexes;
+  }, []);
+}
+
+function getLength(grip) {
+  return grip.preview.size || 0;
+}
+
+function supportsObject(grip, noGrip = false) {
+  if (noGrip === true || !isGrip(grip)) {
+    return false;
+  }
+  return grip.preview && grip.preview.kind == "MapLike";
+}
+
+const maxLengthMap = new Map();
+maxLengthMap.set(MODE.SHORT, 3);
+maxLengthMap.set(MODE.LONG, 10);
+
+// Exports from this module
+module.exports = {
+  rep: wrapRender(GripMap),
+  supportsObject,
+  maxLengthMap,
+  getLength
+};
+
+/***/ }),
+
+/***/ 3664:
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+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; };
+
+/* 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/>. */
+
+// Dependencies
+const PropTypes = __webpack_require__(3642);
+// Shortcuts
+const dom = __webpack_require__(3643);
+const { span } = dom;
+const { wrapRender } = __webpack_require__(3644);
+const PropRep = __webpack_require__(3650);
+const { MODE } = __webpack_require__(3645);
+/**
+ * Renders an map entry. A map entry is represented by its key,
+ * a column and its value.
+ */
+GripMapEntry.propTypes = {
+  object: PropTypes.object,
+  // @TODO Change this to Object.values when supported in Node's version of V8
+  mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
+  onDOMNodeMouseOver: PropTypes.func,
+  onDOMNodeMouseOut: PropTypes.func,
+  onInspectIconClick: PropTypes.func
+};
+
+function GripMapEntry(props) {
+  const { object } = props;
+
+  const { key, value } = object.preview;
+
+  return span({
+    className: "objectBox objectBox-map-entry"
+  }, PropRep(_extends({}, props, {
+    name: key,
+    object: value,
+    equal: " \u2192 ",
+    title: null,
+    suppressQuotes: false
+  })));
+}
+
+function supportsObject(grip, noGrip = false) {
+  if (noGrip === true) {
+    return false;
+  }
+  return grip && (grip.type === "mapEntry" || grip.type === "storageEntry") && grip.preview;
+}
+
+function createGripMapEntry(key, value) {
+  return {
+    type: "mapEntry",
+    preview: {
+      key,
+      value
+    }
+  };
+}
+
+// Exports from this module
+module.exports = {
+  rep: wrapRender(GripMapEntry),
+  createGripMapEntry,
+  supportsObject
+};
+
+/***/ }),
+
+/***/ 3665:
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+const { getValue, nodeHasFullText } = __webpack_require__(3667); /* 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/>. */
+
+async function enumIndexedProperties(objectClient, start, end) {
+  try {
+    const { iterator } = await objectClient.enumProperties({
+      ignoreNonIndexedProperties: true
+    });
+    const response = await iteratorSlice(iterator, start, end);
+    return response;
+  } catch (e) {
+    console.error("Error in enumIndexedProperties", e);
+    return {};
+  }
+}
+
+async function enumNonIndexedProperties(objectClient, start, end) {
+  try {
+    const { iterator } = await objectClient.enumProperties({
+      ignoreIndexedProperties: true
+    });
+    const response = await iteratorSlice(iterator, start, end);
+    return response;
+  } catch (e) {
+    console.error("Error in enumNonIndexedProperties", e);
+    return {};
+  }
+}
+
+async function enumEntries(objectClient, start, end) {
+  try {
+    const { iterator } = await objectClient.enumEntries();
+    const response = await iteratorSlice(iterator, start, end);
+    return response;
+  } catch (e) {
+    console.error("Error in enumEntries", e);
+    return {};
+  }
+}
+
+async function enumSymbols(objectClient, start, end) {
+  try {
+    const { iterator } = await objectClient.enumSymbols();
+    const response = await iteratorSlice(iterator, start, end);
+    return response;
+  } catch (e) {
+    console.error("Error in enumSymbols", e);
+    return {};
+  }
+}
+
+async function getPrototype(objectClient) {
+  if (typeof objectClient.getPrototype !== "function") {
+    console.error("objectClient.getPrototype is not a function");
+    return Promise.resolve({});
+  }
+  return objectClient.getPrototype();
+}
+
+async function getFullText(longStringClient, item) {
+  const { initial, fullText, length } = getValue(item);
+
+  // Return fullText property if it exists so that it can be added to the
+  // loadedProperties map.
+  if (nodeHasFullText(item)) {
+    return Promise.resolve({ fullText });
+  }
+
+  return new Promise((resolve, reject) => {
+    longStringClient.substring(initial.length, length, response => {
+      if (response.error) {
+        console.error("LongStringClient.substring", `${response.error}: ${response.message}`);
+        reject({});
+        return;
+      }
+
+      resolve({
+        fullText: initial + response.substring
+      });
+    });
+  });
+}
+
+function iteratorSlice(iterator, start, end) {
+  start = start || 0;
+  const count = end ? end - start + 1 : iterator.count;
+
+  if (count === 0) {
+    return Promise.resolve({});
+  }
+  return iterator.slice(start, count);
+}
+
+module.exports = {
+  enumEntries,
+  enumIndexedProperties,
+  enumNonIndexedProperties,
+  enumSymbols,
+  getPrototype,
+  getFullText
+};
+
+/***/ }),
+
+/***/ 3666:
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+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; };
+
+/* 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 {
+  enumEntries,
+  enumIndexedProperties,
+  enumNonIndexedProperties,
+  getPrototype,
+  enumSymbols,
+  getFullText
+} = __webpack_require__(3665);
+
+const {
+  getClosestGripNode,
+  getClosestNonBucketNode,
+  getValue,
+  nodeHasAccessors,
+  nodeHasAllEntriesInPreview,
+  nodeHasProperties,
+  nodeIsBucket,
+  nodeIsDefaultProperties,
+  nodeIsEntries,
+  nodeIsMapEntry,
+  nodeIsPrimitive,
+  nodeIsProxy,
+  nodeNeedsNumericalBuckets,
+  nodeIsLongString
+} = __webpack_require__(3667);
+
+function loadItemProperties(item, createObjectClient, createLongStringClient, loadedProperties) {
+  const gripItem = getClosestGripNode(item);
+  const value = getValue(gripItem);
+
+  const [start, end] = item.meta ? [item.meta.startIndex, item.meta.endIndex] : [];
+
+  const promises = [];
+  let objectClient;
+  const getObjectClient = () => objectClient || createObjectClient(value);
+
+  if (shouldLoadItemIndexedProperties(item, loadedProperties)) {
+    promises.push(enumIndexedProperties(getObjectClient(), start, end));
+  }
+
+  if (shouldLoadItemNonIndexedProperties(item, loadedProperties)) {
+    promises.push(enumNonIndexedProperties(getObjectClient(), start, end));
+  }
+
+  if (shouldLoadItemEntries(item, loadedProperties)) {
+    promises.push(enumEntries(getObjectClient(), start, end));
+  }
+
+  if (shouldLoadItemPrototype(item, loadedProperties)) {
+    promises.push(getPrototype(getObjectClient()));
+  }
+
+  if (shouldLoadItemSymbols(item, loadedProperties)) {
+    promises.push(enumSymbols(getObjectClient(), start, end));
+  }
+
+  if (shouldLoadItemFullText(item, loadedProperties)) {
+    promises.push(getFullText(createLongStringClient(value), item));
+  }
+
+  return Promise.all(promises).then(mergeResponses);
+}
+
+function mergeResponses(responses) {
+  const data = {};
+
+  for (const response of responses) {
+    if (response.hasOwnProperty("ownProperties")) {
+      data.ownProperties = _extends({}, data.ownProperties, response.ownProperties);
+    }
+
+    if (response.ownSymbols && response.ownSymbols.length > 0) {
+      data.ownSymbols = response.ownSymbols;
+    }
+
+    if (response.prototype) {
+      data.prototype = response.prototype;
+    }
+
+    if (response.fullText) {
+      data.fullText = response.fullText;
+    }
+  }
+
+  return data;
+}
+
+function shouldLoadItemIndexedProperties(item, loadedProperties = new Map()) {
+  const gripItem = getClosestGripNode(item);
+  const value = getValue(gripItem);
+
+  return value && nodeHasProperties(gripItem) && !loadedProperties.has(item.path) && !nodeIsProxy(item) && !nodeNeedsNumericalBuckets(item) && !nodeIsEntries(getClosestNonBucketNode(item)) &&
+  // The data is loaded when expanding the window node.
+  !nodeIsDefaultProperties(item);
+}
+
+function shouldLoadItemNonIndexedProperties(item, loadedProperties = new Map()) {
+  const gripItem = getClosestGripNode(item);
+  const value = getValue(gripItem);
+
+  return value && nodeHasProperties(gripItem) && !loadedProperties.has(item.path) && !nodeIsProxy(item) && !nodeIsEntries(getClosestNonBucketNode(item)) && !nodeIsBucket(item) &&
+  // The data is loaded when expanding the window node.
+  !nodeIsDefaultProperties(item);
+}
+
+function shouldLoadItemEntries(item, loadedProperties = new Map()) {
+  const gripItem = getClosestGripNode(item);
+  const value = getValue(gripItem);
+
+  return value && nodeIsEntries(getClosestNonBucketNode(item)) && !nodeHasAllEntriesInPreview(gripItem) && !loadedProperties.has(item.path) && !nodeNeedsNumericalBuckets(item);
+}
+
+function shouldLoadItemPrototype(item, loadedProperties = new Map()) {
+  const value = getValue(item);
+
+  return value && !loadedProperties.has(item.path) && !nodeIsBucket(item) && !nodeIsMapEntry(item) && !nodeIsEntries(item) && !nodeIsDefaultProperties(item) && !nodeHasAccessors(item) && !nodeIsPrimitive(item) && !nodeIsLongString(item);
+}
+
+function shouldLoadItemSymbols(item, loadedProperties = new Map()) {
+  const value = getValue(item);
+
+  return value && !loadedProperties.has(item.path) && !nodeIsBucket(item) && !nodeIsMapEntry(item) && !nodeIsEntries(item) && !nodeIsDefaultProperties(item) && !nodeHasAccessors(item) && !nodeIsPrimitive(item) && !nodeIsLongString(item) && !nodeIsProxy(item);
+}
+
+function shouldLoadItemFullText(item, loadedProperties = new Map()) {
+  return !loadedProperties.has(item.path) && nodeIsLongString(item);
+}
+
+module.exports = {
+  loadItemProperties,
+  mergeResponses,
+  shouldLoadItemEntries,
+  shouldLoadItemIndexedProperties,
+  shouldLoadItemNonIndexedProperties,
+  shouldLoadItemPrototype,
+  shouldLoadItemSymbols,
+  shouldLoadItemFullText
+};
+
+/***/ }),
+
+/***/ 3667:
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+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; };
+
+/* 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 { maybeEscapePropertyName } = __webpack_require__(3644);
+const ArrayRep = __webpack_require__(3649);
+const GripArrayRep = __webpack_require__(3661);
+const GripMap = __webpack_require__(3663);
+const GripMapEntryRep = __webpack_require__(3664);
+const ErrorRep = __webpack_require__(3660);
+const { isLongString } = __webpack_require__(3648);
 
 const MAX_NUMERICAL_PROPERTIES = 100;
 
 const NODE_TYPES = {
   BUCKET: Symbol("[n…m]"),
   DEFAULT_PROPERTIES: Symbol("<default properties>"),
   ENTRIES: Symbol("<entries>"),
   GET: Symbol("<get>"),
@@ -2440,1208 +3749,65 @@ module.exports = {
   nodeSupportsNumericalBucketing,
   setNodeChildren,
   sortProperties,
   NODE_TYPES
 };
 
 /***/ }),
 
-/***/ 1786:
+/***/ 3669:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
-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; };
-
-function initialState() {
-  return {
-    expandedPaths: new Set(),
-    loadedProperties: new Map(),
-    evaluations: new Map(),
-    actors: new Set()
-  };
-} /* 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 reducer(state = initialState(), action = {}) {
-  const { type, data } = action;
-
-  const cloneState = overrides => _extends({}, state, overrides);
-
-  if (type === "NODE_EXPAND") {
-    return cloneState({
-      expandedPaths: new Set(state.expandedPaths).add(data.node.path)
-    });
-  }
-
-  if (type === "NODE_COLLAPSE") {
-    const expandedPaths = new Set(state.expandedPaths);
-    expandedPaths.delete(data.node.path);
-    return cloneState({ expandedPaths });
-  }
-
-  if (type === "NODE_PROPERTIES_LOADED") {
-    return cloneState({
-      actors: data.actor ? new Set(state.actors || []).add(data.actor) : state.actors,
-      loadedProperties: new Map(state.loadedProperties).set(data.node.path, action.data.properties)
-    });
-  }
-
-  if (type === "ROOTS_CHANGED") {
-    return cloneState();
-  }
-
-  if (type === "GETTER_INVOKED") {
-    return cloneState({
-      actors: data.actor ? new Set(state.actors || []).add(data.result.from) : state.actors,
-      evaluations: new Map(state.evaluations).set(data.node.path, {
-        getterValue: data.result && data.result.value && (data.result.value.return || data.result.value.throw)
-      })
-    });
-  }
-
-  return state;
-}
-
-function getObjectInspectorState(state) {
-  return state.objectInspector;
-}
-
-function getExpandedPaths(state) {
-  return getObjectInspectorState(state).expandedPaths;
-}
-
-function getExpandedPathKeys(state) {
-  return [...getExpandedPaths(state).keys()];
-}
-
-function getActors(state) {
-  return getObjectInspectorState(state).actors;
-}
-
-function getLoadedProperties(state) {
-  return getObjectInspectorState(state).loadedProperties;
-}
-
-function getLoadedPropertyKeys(state) {
-  return [...getLoadedProperties(state).keys()];
-}
-
-function getEvaluations(state) {
-  return getObjectInspectorState(state).evaluations;
-}
-
-const selectors = {
-  getActors,
-  getEvaluations,
-  getExpandedPathKeys,
-  getExpandedPaths,
-  getLoadedProperties,
-  getLoadedPropertyKeys
-};
-
-Object.defineProperty(module.exports, "__esModule", {
-  value: true
-});
-module.exports = selectors;
-module.exports.default = reducer;
-
-/***/ }),
-
-/***/ 1787:
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-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; };
-
-/* 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__(1805);
-const loadProperties = __webpack_require__(1804);
-const node = __webpack_require__(1785);
-const { nodeIsError, nodeIsPrimitive } = node;
-const selection = __webpack_require__(1860);
-
-const { MODE } = __webpack_require__(1762);
-const {
-  REPS: { Rep, Grip }
-} = __webpack_require__(1767);
-
-
-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(_extends({}, props, {
-    object: node.getValue(item),
-    mode: props.mode || MODE.TINY,
-    defaultRep: Grip
-  }));
-}
-
-module.exports = {
-  client,
-  loadProperties,
-  node,
-  renderRep,
-  selection,
-  shouldRenderRootsInReps
-};
-
-/***/ }),
-
-/***/ 1792:
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-var _tree = __webpack_require__(1802);
+var _tree = __webpack_require__(3670);
 
 var _tree2 = _interopRequireDefault(_tree);
 
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
 module.exports = {
   Tree: _tree2.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/. */
 
 /***/ }),
 
-/***/ 1795:
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-/* 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/>. */
-
-// ReactJS
-const PropTypes = __webpack_require__(1758);
-
-// Reps
-const { getGripType, isGrip, cropString, wrapRender } = __webpack_require__(1760);
-const { MODE } = __webpack_require__(1762);
-
-const dom = __webpack_require__(1759);
-const { span } = dom;
-
-const IGNORED_SOURCE_URLS = ["debugger eval code"];
-
-/**
- * This component represents a template for Function objects.
- */
-FunctionRep.propTypes = {
-  object: PropTypes.object.isRequired,
-  parameterNames: PropTypes.array,
-  onViewSourceInDebugger: PropTypes.func
-};
-
-function FunctionRep(props) {
-  const { object: grip, onViewSourceInDebugger, recordTelemetryEvent } = props;
-
-  let jumpToDefinitionButton;
-  if (onViewSourceInDebugger && grip.location && grip.location.url && !IGNORED_SOURCE_URLS.includes(grip.location.url)) {
-    jumpToDefinitionButton = dom.button({
-      className: "jump-definition",
-      draggable: false,
-      title: "Jump to definition",
-      onClick: e => {
-        // Stop the event propagation so we don't trigger ObjectInspector
-        // expand/collapse.
-        e.stopPropagation();
-        if (recordTelemetryEvent) {
-          recordTelemetryEvent("jump_to_definition");
-        }
-        onViewSourceInDebugger(grip.location);
-      }
-    });
-  }
-
-  return span({
-    "data-link-actor-id": grip.actor,
-    className: "objectBox objectBox-function",
-    // Set dir="ltr" to prevent function parentheses from
-    // appearing in the wrong direction
-    dir: "ltr"
-  }, getTitle(grip, props), getFunctionName(grip, props), "(", ...renderParams(props), ")", jumpToDefinitionButton);
-}
-
-function getTitle(grip, props) {
-  const { mode } = props;
-
-  if (mode === MODE.TINY && !grip.isGenerator && !grip.isAsync) {
-    return null;
-  }
-
-  let title = mode === MODE.TINY ? "" : "function ";
-
-  if (grip.isGenerator) {
-    title = mode === MODE.TINY ? "* " : "function* ";
-  }
-
-  if (grip.isAsync) {
-    title = `${"async" + " "}${title}`;
-  }
-
-  return span({
-    className: "objectTitle"
-  }, title);
-}
-
-/**
- * Returns a ReactElement representing the function name.
- *
- * @param {Object} grip : Function grip
- * @param {Object} props: Function rep props
- */
-function getFunctionName(grip, props = {}) {
-  let { functionName } = props;
-  let name;
-
-  if (functionName) {
-    const end = functionName.length - 1;
-    functionName = functionName.startsWith('"') && functionName.endsWith('"') ? functionName.substring(1, end) : functionName;
-  }
-
-  if (grip.displayName != undefined && functionName != undefined && grip.displayName != functionName) {
-    name = `${functionName}:${grip.displayName}`;
-  } else {
-    name = cleanFunctionName(grip.userDisplayName || grip.displayName || grip.name || props.functionName || "");
-  }
-
-  return cropString(name, 100);
-}
-
-const objectProperty = /([\w\d\$]+)$/;
-const arrayProperty = /\[(.*?)\]$/;
-const functionProperty = /([\w\d]+)[\/\.<]*?$/;
-const annonymousProperty = /([\w\d]+)\(\^\)$/;
-
-/**
- * Decodes an anonymous naming scheme that
- * spider monkey implements based on "Naming Anonymous JavaScript Functions"
- * http://johnjbarton.github.io/nonymous/index.html
- *
- * @param {String} name : Function name to clean up
- * @returns String
- */
-function cleanFunctionName(name) {
-  for (const reg of [objectProperty, arrayProperty, functionProperty, annonymousProperty]) {
-    const match = reg.exec(name);
-    if (match) {
-      return match[1];
-    }
-  }
-
-  return name;
-}
-
-function renderParams(props) {
-  const { parameterNames = [] } = props;
-
-  return parameterNames.filter(param => param).reduce((res, param, index, arr) => {
-    res.push(span({ className: "param" }, param));
-    if (index < arr.length - 1) {
-      res.push(span({ className: "delimiter" }, ", "));
-    }
-    return res;
-  }, []);
-}
-
-// Registration
-function supportsObject(grip, noGrip = false) {
-  const type = getGripType(grip, noGrip);
-  if (noGrip === true || !isGrip(grip)) {
-    return type == "function";
-  }
-
-  return type == "Function";
-}
-
-// Exports from this module
-
-module.exports = {
-  rep: wrapRender(FunctionRep),
-  supportsObject,
-  cleanFunctionName,
-  // exported for testing purpose.
-  getFunctionName
-};
-
-/***/ }),
-
-/***/ 1796:
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-/* 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/>. */
-
-module.exports = {
-  ELEMENT_NODE: 1,
-  ATTRIBUTE_NODE: 2,
-  TEXT_NODE: 3,
-  CDATA_SECTION_NODE: 4,
-  ENTITY_REFERENCE_NODE: 5,
-  ENTITY_NODE: 6,
-  PROCESSING_INSTRUCTION_NODE: 7,
-  COMMENT_NODE: 8,
-  DOCUMENT_NODE: 9,
-  DOCUMENT_TYPE_NODE: 10,
-  DOCUMENT_FRAGMENT_NODE: 11,
-  NOTATION_NODE: 12,
-
-  // DocumentPosition
-  DOCUMENT_POSITION_DISCONNECTED: 0x01,
-  DOCUMENT_POSITION_PRECEDING: 0x02,
-  DOCUMENT_POSITION_FOLLOWING: 0x04,
-  DOCUMENT_POSITION_CONTAINS: 0x08,
-  DOCUMENT_POSITION_CONTAINED_BY: 0x10,
-  DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC: 0x20
-};
-
-/***/ }),
-
-/***/ 1797:
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-/* 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/>. */
-
-// ReactJS
-const PropTypes = __webpack_require__(1758);
-// Utils
-const { getGripType, isGrip, wrapRender } = __webpack_require__(1760);
-const { cleanFunctionName } = __webpack_require__(1795);
-const { isLongString } = __webpack_require__(1770);
-const { MODE } = __webpack_require__(1762);
-
-const dom = __webpack_require__(1759);
-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 when supported in Node's version of V8
-  mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
-  // An optional function that will be used to render the Error stacktrace.
-  renderStacktrace: PropTypes.func
-};
-
-function ErrorRep(props) {
-  const object = props.object;
-  const preview = object.preview;
-
-  let name;
-  if (preview && preview.name && preview.kind) {
-    switch (preview.kind) {
-      case "Error":
-        name = preview.name;
-        break;
-      case "DOMException":
-        name = preview.kind;
-        break;
-      default:
-        throw new Error("Unknown preview kind for the Error rep.");
-    }
-  } else {
-    name = "Error";
-  }
-
-  const content = [];
-
-  if (props.mode === MODE.TINY) {
-    content.push(name);
-  } else {
-    content.push(`${name}: "${preview.message}"`);
-  }
-
-  if (preview.stack && props.mode !== MODE.TINY) {
-    const stacktrace = props.renderStacktrace ? props.renderStacktrace(parseStackString(preview.stack)) : getStacktraceElements(props, preview);
-    content.push("\n", stacktrace);
-  }
-
-  return span({
-    "data-link-actor-id": object.actor,
-    className: "objectBox-stackTrace"
-  }, content);
-}
-
-/**
- * Returns a React element reprensenting the Error stacktrace, i.e.
- * transform error.stack from:
- *
- * semicolon@debugger eval code:1:109
- * jkl@debugger eval code:1:63
- * asdf@debugger eval code:1:28
- * @debugger eval code:1:227
- *
- * Into a column layout:
- *
- * semicolon  (<anonymous>:8:10)
- * jkl        (<anonymous>:5:10)
- * asdf       (<anonymous>:2:10)
- *            (<anonymous>:11:1)
- */
-function getStacktraceElements(props, preview) {
-  const stack = [];
-  if (!preview.stack) {
-    return stack;
-  }
-
-  parseStackString(preview.stack).forEach((frame, index, frames) => {
-    let onLocationClick;
-    const {
-      filename,
-      lineNumber,
-      columnNumber,
-      functionName,
-      location
-    } = frame;
-
-    if (props.onViewSourceInDebugger && !IGNORED_SOURCE_URLS.includes(filename)) {
-      onLocationClick = e => {
-        // Don't trigger ObjectInspector expand/collapse.
-        e.stopPropagation();
-        props.onViewSourceInDebugger({
-          url: filename,
-          line: lineNumber,
-          column: columnNumber
-        });
-      };
-    }
-
-    stack.push("\t", span({
-      key: `fn${index}`,
-      className: "objectBox-stackTrace-fn"
-    }, cleanFunctionName(functionName)), " ", span({
-      key: `location${index}`,
-      className: "objectBox-stackTrace-location",
-      onClick: onLocationClick,
-      title: onLocationClick ? `View source in debugger → ${location}` : undefined
-    }, location), "\n");
-  });
-
-  return span({
-    key: "stack",
-    className: "objectBox-stackTrace-grid"
-  }, stack);
-}
-
-/**
- * Parse a string that should represent a stack trace and returns an array of
- * the frames. The shape of the frames are extremely important as they can then
- * be processed here or in the toolbox by other components.
- * @param {String} stack
- * @returns {Array} Array of frames, which are object with the following shape:
- *                  - {String} filename
- *                  - {String} functionName
- *                  - {String} location
- *                  - {Number} columnNumber
- *                  - {Number} lineNumber
- */
-function parseStackString(stack) {
-  const res = [];
-  if (!stack) {
-    return res;
-  }
-
-  const isStacktraceALongString = isLongString(stack);
-  const stackString = isStacktraceALongString ? stack.initial : stack;
-
-  stackString.split("\n").forEach((frame, index, frames) => {
-    if (!frame) {
-      // Skip any blank lines
-      return;
-    }
-
-    // If the stacktrace is a longString, don't include the last frame in the
-    // array, since it is certainly incomplete.
-    // Can be removed when https://bugzilla.mozilla.org/show_bug.cgi?id=1448833
-    // is fixed.
-    if (isStacktraceALongString && index === frames.length - 1) {
-      return;
-    }
-
-    let functionName;
-    let location;
-
-    // Given the input: "functionName@scriptLocation:2:100"
-    // Result: [
-    //   "functionName@scriptLocation:2:100",
-    //   "functionName",
-    //   "scriptLocation:2:100"
-    // ]
-    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>";
-    }
-
-    // Given the input: "scriptLocation:2:100"
-    // Result:
-    // ["scriptLocation:2:100", "scriptLocation", "2", "100"]
-    const locationParts = location.match(/^(.*):(\d+):(\d+)$/);
-
-    if (location && locationParts) {
-      const [, filename, line, column] = locationParts;
-      res.push({
-        filename,
-        functionName,
-        location,
-        columnNumber: Number(column),
-        lineNumber: Number(line)
-      });
-    }
-  });
-
-  return res;
-}
-
-// Registration
-function supportsObject(object, noGrip = false) {
-  if (noGrip === true || !isGrip(object)) {
-    return false;
-  }
-  return object.preview && getGripType(object, noGrip) === "Error" || object.class === "DOMException";
-}
-
-// Exports from this module
-module.exports = {
-  rep: wrapRender(ErrorRep),
-  supportsObject
-};
-
-/***/ }),
-
-/***/ 1798:
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-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; };
-
-/* 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/>. */
-
-// Dependencies
-const PropTypes = __webpack_require__(1758);
-
-const { lengthBubble } = __webpack_require__(1799);
-const {
-  interleave,
-  getGripType,
-  isGrip,
-  wrapRender,
-  ellipsisElement
-} = __webpack_require__(1760);
-const { MODE } = __webpack_require__(1762);
-
-const dom = __webpack_require__(1759);
-const { span } = dom;
-const { ModePropType } = __webpack_require__(1774);
-const DEFAULT_TITLE = "Array";
-
-/**
- * Renders an array. The array is enclosed by left and right bracket
- * and the max number of rendered items depends on the current mode.
- */
-GripArray.propTypes = {
-  object: PropTypes.object.isRequired,
-  // @TODO Change this to Object.values when supported in Node's version of V8
-  mode: ModePropType,
-  provider: PropTypes.object,
-  onDOMNodeMouseOver: PropTypes.func,
-  onDOMNodeMouseOut: PropTypes.func,
-  onInspectIconClick: PropTypes.func
-};
-
-function GripArray(props) {
-  const { object, mode = MODE.SHORT } = props;
-
-  let brackets;
-  const needSpace = function (space) {
-    return space ? { left: "[ ", right: " ]" } : { left: "[", right: "]" };
-  };
-
-  const config = {
-    "data-link-actor-id": object.actor,
-    className: "objectBox objectBox-array"
-  };
-
-  const title = getTitle(props, object);
-
-  if (mode === MODE.TINY) {
-    const isEmpty = getLength(object) === 0;
-
-    // Omit bracketed ellipsis for non-empty non-Array arraylikes (f.e: Sets).
-    if (!isEmpty && object.class !== "Array") {
-      return span(config, title);
-    }
-
-    brackets = needSpace(false);
-    return span(config, title, span({
-      className: "arrayLeftBracket"
-    }, brackets.left), isEmpty ? null : ellipsisElement, span({
-      className: "arrayRightBracket"
-    }, brackets.right));
-  }
-
-  const max = maxLengthMap.get(mode);
-  const items = arrayIterator(props, object, max);
-  brackets = needSpace(items.length > 0);
-
-  return span({
-    "data-link-actor-id": object.actor,
-    className: "objectBox objectBox-array"
-  }, title, span({
-    className: "arrayLeftBracket"
-  }, brackets.left), ...interleave(items, ", "), span({
-    className: "arrayRightBracket"
-  }, brackets.right), span({
-    className: "arrayProperties",
-    role: "group"
-  }));
-}
-
-function getLength(grip) {
-  if (!grip.preview) {
-    return 0;
-  }
-
-  return grip.preview.length || grip.preview.childNodesLength || 0;
-}
-
-function getTitle(props, object) {
-  const objectLength = getLength(object);
-  const isEmpty = objectLength === 0;
-
-  let title = props.title || object.class || DEFAULT_TITLE;
-
-  const length = lengthBubble({
-    object,
-    mode: props.mode,
-    maxLengthMap,
-    getLength
-  });
-
-  if (props.mode === MODE.TINY) {
-    if (isEmpty) {
-      if (object.class === DEFAULT_TITLE) {
-        return null;
-      }
-
-      return span({ className: "objectTitle" }, `${title} `);
-    }
-
-    let trailingSpace;
-    if (object.class === DEFAULT_TITLE) {
-      title = null;
-      trailingSpace = " ";
-    }
-
-    return span({ className: "objectTitle" }, title, length, trailingSpace);
-  }
-
-  return span({ className: "objectTitle" }, title, length, " ");
-}
-
-function getPreviewItems(grip) {
-  if (!grip.preview) {
-    return null;
-  }
-
-  return grip.preview.items || grip.preview.childNodes || [];
-}
-
-function arrayIterator(props, grip, max) {
-  const { Rep } = __webpack_require__(1767);
-
-  let items = [];
-  const gripLength = getLength(grip);
-
-  if (!gripLength) {
-    return items;
-  }
-
-  const previewItems = getPreviewItems(grip);
-  const provider = props.provider;
-
-  let emptySlots = 0;
-  let foldedEmptySlots = 0;
-  items = previewItems.reduce((res, itemGrip) => {
-    if (res.length >= max) {
-      return res;
-    }
-
-    let object;
-    try {
-      if (!provider && itemGrip === null) {
-        emptySlots++;
-        return res;
-      }
-
-      object = provider ? provider.getValue(itemGrip) : itemGrip;
-    } catch (exc) {
-      object = exc;
-    }
-
-    if (emptySlots > 0) {
-      res.push(getEmptySlotsElement(emptySlots));
-      foldedEmptySlots = foldedEmptySlots + emptySlots - 1;
-      emptySlots = 0;
-    }
-
-    if (res.length < max) {
-      res.push(Rep(_extends({}, props, {
-        object,
-        mode: MODE.TINY,
-        // Do not propagate title to array items reps
-        title: undefined
-      })));
-    }
-
-    return res;
-  }, []);
-
-  // Handle trailing empty slots if there are some.
-  if (items.length < max && emptySlots > 0) {
-    items.push(getEmptySlotsElement(emptySlots));
-    foldedEmptySlots = foldedEmptySlots + emptySlots - 1;
-  }
-
-  const itemsShown = items.length + foldedEmptySlots;
-  if (gripLength > itemsShown) {
-    items.push(ellipsisElement);
-  }
-
-  return items;
-}
-
-function getEmptySlotsElement(number) {
-  // TODO: Use l10N - See https://github.com/devtools-html/reps/issues/141
-  return `<${number} empty slot${number > 1 ? "s" : ""}>`;
-}
-
-function supportsObject(grip, noGrip = false) {
-  if (noGrip === true || !isGrip(grip)) {
-    return false;
-  }
-
-  return grip.preview && (grip.preview.kind == "ArrayLike" || getGripType(grip, noGrip) === "DocumentFragment");
-}
-
-const maxLengthMap = new Map();
-maxLengthMap.set(MODE.SHORT, 3);
-maxLengthMap.set(MODE.LONG, 10);
-
-// Exports from this module
-module.exports = {
-  rep: wrapRender(GripArray),
-  supportsObject,
-  maxLengthMap,
-  getLength
-};
-
-/***/ }),
-
-/***/ 1799:
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-/* 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 PropTypes = __webpack_require__(1758);
-
-const { wrapRender } = __webpack_require__(1760);
-const { MODE } = __webpack_require__(1762);
-const { ModePropType } = __webpack_require__(1774);
-
-const dom = __webpack_require__(1759);
-const { span } = dom;
-
-GripLengthBubble.propTypes = {
-  object: PropTypes.object.isRequired,
-  maxLengthMap: PropTypes.instanceOf(Map).isRequired,
-  getLength: PropTypes.func.isRequired,
-  mode: ModePropType,
-  visibilityThreshold: PropTypes.number
-};
-
-function GripLengthBubble(props) {
-  const {
-    object,
-    mode = MODE.SHORT,
-    visibilityThreshold = 2,
-    maxLengthMap,
-    getLength,
-    showZeroLength = false
-  } = props;
-
-  const length = getLength(object);
-  const isEmpty = length === 0;
-  const isObvious = [MODE.SHORT, MODE.LONG].includes(mode) && length > 0 && length <= maxLengthMap.get(mode) && length <= visibilityThreshold;
-  if (isEmpty && !showZeroLength || isObvious) {
-    return "";
-  }
-
-  return span({
-    className: "objectLengthBubble"
-  }, `(${length})`);
-}
-
-module.exports = {
-  lengthBubble: wrapRender(GripLengthBubble)
-};
-
-/***/ }),
-
-/***/ 1800:
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-/* 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/>. */
-
-// Dependencies
-
-const { lengthBubble } = __webpack_require__(1799);
-const PropTypes = __webpack_require__(1758);
-const {
-  interleave,
-  isGrip,
-  wrapRender,
-  ellipsisElement
-} = __webpack_require__(1760);
-const PropRep = __webpack_require__(1775);
-const { MODE } = __webpack_require__(1762);
-const { ModePropType } = __webpack_require__(1774);
-
-const { span } = __webpack_require__(1759);
-
-/**
- * Renders an map. A map is represented by a list of its
- * entries enclosed in curly brackets.
- */
-GripMap.propTypes = {
-  object: PropTypes.object,
-  // @TODO Change this to Object.values when supported in Node's version of V8
-  mode: ModePropType,
-  isInterestingEntry: PropTypes.func,
-  onDOMNodeMouseOver: PropTypes.func,
-  onDOMNodeMouseOut: PropTypes.func,
-  onInspectIconClick: PropTypes.func,
-  title: PropTypes.string
-};
-
-function GripMap(props) {
-  const { mode, object } = props;
-
-  const config = {
-    "data-link-actor-id": object.actor,
-    className: "objectBox objectBox-object"
-  };
-
-  const title = getTitle(props, object);
-  const isEmpty = getLength(object) === 0;
-
-  if (isEmpty || mode === MODE.TINY) {
-    return span(config, title);
-  }
-
-  const propsArray = safeEntriesIterator(props, object, maxLengthMap.get(mode));
-
-  return span(config, title, span({
-    className: "objectLeftBrace"
-  }, " { "), ...interleave(propsArray, ", "), span({
-    className: "objectRightBrace"
-  }, " }"));
-}
-
-function getTitle(props, object) {
-  const title = props.title || (object && object.class ? object.class : "Map");
-  return span({
-    className: "objectTitle"
-  }, title, lengthBubble({
-    object,
-    mode: props.mode,
-    maxLengthMap,
-    getLength,
-    showZeroLength: true
-  }));
-}
-
-function safeEntriesIterator(props, object, max) {
-  max = typeof max === "undefined" ? 3 : max;
-  try {
-    return entriesIterator(props, object, max);
-  } catch (err) {
-    console.error(err);
-  }
-  return [];
-}
-
-function entriesIterator(props, object, max) {
-  // Entry filter. Show only interesting entries to the user.
-  const isInterestingEntry = props.isInterestingEntry || ((type, value) => {
-    return type == "boolean" || type == "number" || type == "string" && value.length != 0;
-  });
-
-  const mapEntries = object.preview && object.preview.entries ? object.preview.entries : [];
-
-  let indexes = getEntriesIndexes(mapEntries, max, isInterestingEntry);
-  if (indexes.length < max && indexes.length < mapEntries.length) {
-    // There are not enough entries yet, so we add uninteresting entries.
-    indexes = indexes.concat(getEntriesIndexes(mapEntries, max - indexes.length, (t, value, name) => {
-      return !isInterestingEntry(t, value, name);
-    }));
-  }
-
-  const entries = getEntries(props, mapEntries, indexes);
-  if (entries.length < getLength(object)) {
-    // There are some undisplayed entries. Then display "…".
-    entries.push(ellipsisElement);
-  }
-
-  return entries;
-}
-
-/**
- * Get entries ordered by index.
- *
- * @param {Object} props Component props.
- * @param {Array} entries Entries array.
- * @param {Array} indexes Indexes of entries.
- * @return {Array} Array of PropRep.
- */
-function getEntries(props, entries, indexes) {
-  const { onDOMNodeMouseOver, onDOMNodeMouseOut, onInspectIconClick } = props;
-
-  // Make indexes ordered by ascending.
-  indexes.sort(function (a, b) {
-    return a - b;
-  });
-
-  return indexes.map((index, i) => {
-    const [key, entryValue] = entries[index];
-    const value = entryValue.value !== undefined ? entryValue.value : entryValue;
-
-    return PropRep({
-      name: key,
-      equal: " \u2192 ",
-      object: value,
-      mode: MODE.TINY,
-      onDOMNodeMouseOver,
-      onDOMNodeMouseOut,
-      onInspectIconClick
-    });
-  });
-}
-
-/**
- * Get the indexes of entries in the map.
- *
- * @param {Array} entries Entries array.
- * @param {Number} max The maximum length of indexes array.
- * @param {Function} filter Filter the entry you want.
- * @return {Array} Indexes of filtered entries in the map.
- */
-function getEntriesIndexes(entries, max, filter) {
-  return entries.reduce((indexes, [key, entry], i) => {
-    if (indexes.length < max) {
-      const value = entry && entry.value !== undefined ? entry.value : entry;
-      // Type is specified in grip's "class" field and for primitive
-      // values use typeof.
-      const type = (value && value.class ? value.class : typeof value).toLowerCase();
-
-      if (filter(type, value, key)) {
-        indexes.push(i);
-      }
-    }
-
-    return indexes;
-  }, []);
-}
-
-function getLength(grip) {
-  return grip.preview.size || 0;
-}
-
-function supportsObject(grip, noGrip = false) {
-  if (noGrip === true || !isGrip(grip)) {
-    return false;
-  }
-  return grip.preview && grip.preview.kind == "MapLike";
-}
-
-const maxLengthMap = new Map();
-maxLengthMap.set(MODE.SHORT, 3);
-maxLengthMap.set(MODE.LONG, 10);
-
-// Exports from this module
-module.exports = {
-  rep: wrapRender(GripMap),
-  supportsObject,
-  maxLengthMap,
-  getLength
-};
-
-/***/ }),
-
-/***/ 1801:
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-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; };
-
-/* 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/>. */
-
-// Dependencies
-const PropTypes = __webpack_require__(1758);
-// Shortcuts
-const dom = __webpack_require__(1759);
-const { span } = dom;
-const { wrapRender } = __webpack_require__(1760);
-const PropRep = __webpack_require__(1775);
-const { MODE } = __webpack_require__(1762);
-/**
- * Renders an map entry. A map entry is represented by its key,
- * a column and its value.
- */
-GripMapEntry.propTypes = {
-  object: PropTypes.object,
-  // @TODO Change this to Object.values when supported in Node's version of V8
-  mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
-  onDOMNodeMouseOver: PropTypes.func,
-  onDOMNodeMouseOut: PropTypes.func,
-  onInspectIconClick: PropTypes.func
-};
-
-function GripMapEntry(props) {
-  const { object } = props;
-
-  const { key, value } = object.preview;
-
-  return span({
-    className: "objectBox objectBox-map-entry"
-  }, PropRep(_extends({}, props, {
-    name: key,
-    object: value,
-    equal: " \u2192 ",
-    title: null,
-    suppressQuotes: false
-  })));
-}
-
-function supportsObject(grip, noGrip = false) {
-  if (noGrip === true) {
-    return false;
-  }
-  return grip && (grip.type === "mapEntry" || grip.type === "storageEntry") && grip.preview;
-}
-
-function createGripMapEntry(key, value) {
-  return {
-    type: "mapEntry",
-    preview: {
-      key,
-      value
-    }
-  };
-}
-
-// Exports from this module
-module.exports = {
-  rep: wrapRender(GripMapEntry),
-  createGripMapEntry,
-  supportsObject
-};
-
-/***/ }),
-
-/***/ 1802:
+/***/ 3670:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
 
 var _react = __webpack_require__(0);
 
 var _react2 = _interopRequireDefault(_react);
 
-var _reactDomFactories = __webpack_require__(1759);
+var _reactDomFactories = __webpack_require__(3643);
 
 var _reactDomFactories2 = _interopRequireDefault(_reactDomFactories);
 
-var _propTypes = __webpack_require__(1758);
+var _propTypes = __webpack_require__(3642);
 
 var _propTypes2 = _interopRequireDefault(_propTypes);
 
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
 const { Component, createFactory } = _react2.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/>. */
 
-__webpack_require__(1803);
+__webpack_require__(3671);
 
 // depth
 const AUTO_EXPAND_DEPTH = 0;
 
 /**
  * An arrow that displays whether its node is expanded (▼) or collapsed
  * (▶). When its node has no children, it is hidden.
  */
@@ -4429,308 +4595,44 @@ class Tree extends Component {
     }, nodes);
   }
 }
 
 exports.default = Tree;
 
 /***/ }),
 
-/***/ 1803:
+/***/ 3671:
 /***/ (function(module, exports) {
 
 // removed by extract-text-webpack-plugin
 
 /***/ }),
 
-/***/ 1804:
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-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; };
-
-/* 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 {
-  enumEntries,
-  enumIndexedProperties,
-  enumNonIndexedProperties,
-  getPrototype,
-  enumSymbols,
-  getFullText
-} = __webpack_require__(1805);
-
-const {
-  getClosestGripNode,
-  getClosestNonBucketNode,
-  getValue,
-  nodeHasAccessors,
-  nodeHasAllEntriesInPreview,
-  nodeHasProperties,
-  nodeIsBucket,
-  nodeIsDefaultProperties,
-  nodeIsEntries,
-  nodeIsMapEntry,
-  nodeIsPrimitive,
-  nodeIsProxy,
-  nodeNeedsNumericalBuckets,
-  nodeIsLongString
-} = __webpack_require__(1785);
-
-function loadItemProperties(item, createObjectClient, createLongStringClient, loadedProperties) {
-  const gripItem = getClosestGripNode(item);
-  const value = getValue(gripItem);
-
-  const [start, end] = item.meta ? [item.meta.startIndex, item.meta.endIndex] : [];
-
-  const promises = [];
-  let objectClient;
-  const getObjectClient = () => objectClient || createObjectClient(value);
-
-  if (shouldLoadItemIndexedProperties(item, loadedProperties)) {
-    promises.push(enumIndexedProperties(getObjectClient(), start, end));
-  }
-
-  if (shouldLoadItemNonIndexedProperties(item, loadedProperties)) {
-    promises.push(enumNonIndexedProperties(getObjectClient(), start, end));
-  }
-
-  if (shouldLoadItemEntries(item, loadedProperties)) {
-    promises.push(enumEntries(getObjectClient(), start, end));
-  }
-
-  if (shouldLoadItemPrototype(item, loadedProperties)) {
-    promises.push(getPrototype(getObjectClient()));
-  }
-
-  if (shouldLoadItemSymbols(item, loadedProperties)) {
-    promises.push(enumSymbols(getObjectClient(), start, end));
-  }
-
-  if (shouldLoadItemFullText(item, loadedProperties)) {
-    promises.push(getFullText(createLongStringClient(value), item));
-  }
-
-  return Promise.all(promises).then(mergeResponses);
-}
-
-function mergeResponses(responses) {
-  const data = {};
-
-  for (const response of responses) {
-    if (response.hasOwnProperty("ownProperties")) {
-      data.ownProperties = _extends({}, data.ownProperties, response.ownProperties);
-    }
-
-    if (response.ownSymbols && response.ownSymbols.length > 0) {
-      data.ownSymbols = response.ownSymbols;
-    }
-
-    if (response.prototype) {
-      data.prototype = response.prototype;
-    }
-
-    if (response.fullText) {
-      data.fullText = response.fullText;
-    }
-  }
-
-  return data;
-}
-
-function shouldLoadItemIndexedProperties(item, loadedProperties = new Map()) {
-  const gripItem = getClosestGripNode(item);
-  const value = getValue(gripItem);
-
-  return value && nodeHasProperties(gripItem) && !loadedProperties.has(item.path) && !nodeIsProxy(item) && !nodeNeedsNumericalBuckets(item) && !nodeIsEntries(getClosestNonBucketNode(item)) &&
-  // The data is loaded when expanding the window node.
-  !nodeIsDefaultProperties(item);
-}
-
-function shouldLoadItemNonIndexedProperties(item, loadedProperties = new Map()) {
-  const gripItem = getClosestGripNode(item);
-  const value = getValue(gripItem);
-
-  return value && nodeHasProperties(gripItem) && !loadedProperties.has(item.path) && !nodeIsProxy(item) && !nodeIsEntries(getClosestNonBucketNode(item)) && !nodeIsBucket(item) &&
-  // The data is loaded when expanding the window node.
-  !nodeIsDefaultProperties(item);
-}
-
-function shouldLoadItemEntries(item, loadedProperties = new Map()) {
-  const gripItem = getClosestGripNode(item);
-  const value = getValue(gripItem);
-
-  return value && nodeIsEntries(getClosestNonBucketNode(item)) && !nodeHasAllEntriesInPreview(gripItem) && !loadedProperties.has(item.path) && !nodeNeedsNumericalBuckets(item);
-}
-
-function shouldLoadItemPrototype(item, loadedProperties = new Map()) {
-  const value = getValue(item);
-
-  return value && !loadedProperties.has(item.path) && !nodeIsBucket(item) && !nodeIsMapEntry(item) && !nodeIsEntries(item) && !nodeIsDefaultProperties(item) && !nodeHasAccessors(item) && !nodeIsPrimitive(item) && !nodeIsLongString(item);
-}
-
-function shouldLoadItemSymbols(item, loadedProperties = new Map()) {
-  const value = getValue(item);
-
-  return value && !loadedProperties.has(item.path) && !nodeIsBucket(item) && !nodeIsMapEntry(item) && !nodeIsEntries(item) && !nodeIsDefaultProperties(item) && !nodeHasAccessors(item) && !nodeIsPrimitive(item) && !nodeIsLongString(item) && !nodeIsProxy(item);
-}
-
-function shouldLoadItemFullText(item, loadedProperties = new Map()) {
-  return !loadedProperties.has(item.path) && nodeIsLongString(item);
-}
-
-module.exports = {
-  loadItemProperties,
-  mergeResponses,
-  shouldLoadItemEntries,
-  shouldLoadItemIndexedProperties,
-  shouldLoadItemNonIndexedProperties,
-  shouldLoadItemPrototype,
-  shouldLoadItemSymbols,
-  shouldLoadItemFullText
-};
-
-/***/ }),
-
-/***/ 1805:
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-const { getValue, nodeHasFullText } = __webpack_require__(1785); /* 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/>. */
-
-async function enumIndexedProperties(objectClient, start, end) {
-  try {
-    const { iterator } = await objectClient.enumProperties({
-      ignoreNonIndexedProperties: true
-    });
-    const response = await iteratorSlice(iterator, start, end);
-    return response;
-  } catch (e) {
-    console.error("Error in enumIndexedProperties", e);
-    return {};
-  }
-}
-
-async function enumNonIndexedProperties(objectClient, start, end) {
-  try {
-    const { iterator } = await objectClient.enumProperties({
-      ignoreIndexedProperties: true
-    });
-    const response = await iteratorSlice(iterator, start, end);
-    return response;
-  } catch (e) {
-    console.error("Error in enumNonIndexedProperties", e);
-    return {};
-  }
-}
-
-async function enumEntries(objectClient, start, end) {
-  try {
-    const { iterator } = await objectClient.enumEntries();
-    const response = await iteratorSlice(iterator, start, end);
-    return response;
-  } catch (e) {
-    console.error("Error in enumEntries", e);
-    return {};
-  }
-}
-
-async function enumSymbols(objectClient, start, end) {
-  try {
-    const { iterator } = await objectClient.enumSymbols();
-    const response = await iteratorSlice(iterator, start, end);
-    return response;
-  } catch (e) {
-    console.error("Error in enumSymbols", e);
-    return {};
-  }
-}
-
-async function getPrototype(objectClient) {
-  if (typeof objectClient.getPrototype !== "function") {
-    console.error("objectClient.getPrototype is not a function");
-    return Promise.resolve({});
-  }
-  return objectClient.getPrototype();
-}
-
-async function getFullText(longStringClient, item) {
-  const { initial, fullText, length } = getValue(item);
-
-  // Return fullText property if it exists so that it can be added to the
-  // loadedProperties map.
-  if (nodeHasFullText(item)) {
-    return Promise.resolve({ fullText });
-  }
-
-  return new Promise((resolve, reject) => {
-    longStringClient.substring(initial.length, length, response => {
-      if (response.error) {
-        console.error("LongStringClient.substring", `${response.error}: ${response.message}`);
-        reject({});
-        return;
-      }
-
-      resolve({
-        fullText: initial + response.substring
-      });
-    });
-  });
-}
-
-function iteratorSlice(iterator, start, end) {
-  start = start || 0;
-  const count = end ? end - start + 1 : iterator.count;
-
-  if (count === 0) {
-    return Promise.resolve({});
-  }
-  return iterator.slice(start, count);
-}
-
-module.exports = {
-  enumEntries,
-  enumIndexedProperties,
-  enumNonIndexedProperties,
-  enumSymbols,
-  getPrototype,
-  getFullText
-};
-
-/***/ }),
-
-/***/ 1831:
+/***/ 3672:
 /***/ (function(module, exports) {
 
 // removed by extract-text-webpack-plugin
 
 /***/ }),
 
-/***/ 1832:
+/***/ 3673:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 /* 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/>. */
 
 // Dependencies
-const { getGripType, wrapRender } = __webpack_require__(1760);
-
-const dom = __webpack_require__(1759);
+const { getGripType, wrapRender } = __webpack_require__(3644);
+
+const dom = __webpack_require__(3643);
 const { span } = dom;
 
 /**
  * Renders undefined value
  */
 const Undefined = function () {
   return span({ className: "objectBox objectBox-undefined" }, "undefined");
 };
@@ -4747,29 +4649,29 @@ function supportsObject(object, noGrip =
 
 module.exports = {
   rep: wrapRender(Undefined),
   supportsObject
 };
 
 /***/ }),
 
-/***/ 1833:
+/***/ 3674:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 /* 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/>. */
 
 // Dependencies
-const { wrapRender } = __webpack_require__(1760);
-const dom = __webpack_require__(1759);
+const { wrapRender } = __webpack_require__(3644);
+const dom = __webpack_require__(3643);
 const { span } = dom;
 
 /**
  * Renders null value
  */
 function Null(props) {
   return span({ className: "objectBox objectBox-null" }, "null");
 }
@@ -4790,32 +4692,32 @@ function supportsObject(object, noGrip =
 
 module.exports = {
   rep: wrapRender(Null),
   supportsObject
 };
 
 /***/ }),
 
-/***/ 1834:
+/***/ 3675:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 /* 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/>. */
 
 // Dependencies
-const PropTypes = __webpack_require__(1758);
-
-const { getGripType, wrapRender } = __webpack_require__(1760);
-
-const dom = __webpack_require__(1759);
+const PropTypes = __webpack_require__(3642);
+
+const { getGripType, wrapRender } = __webpack_require__(3644);
+
+const dom = __webpack_require__(3643);
 const { span } = dom;
 
 /**
  * Renders a number
  */
 Number.propTypes = {
   object: PropTypes.oneOfType([PropTypes.object, PropTypes.number, PropTypes.bool]).isRequired
 };
@@ -4840,35 +4742,35 @@ function supportsObject(object, noGrip =
 
 module.exports = {
   rep: wrapRender(Number),
   supportsObject
 };
 
 /***/ }),
 
-/***/ 1835:
+/***/ 3676:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 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; };
 
 /* 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/>. */
 
 // Dependencies
-const PropTypes = __webpack_require__(1758);
-const { wrapRender, ellipsisElement } = __webpack_require__(1760);
-const PropRep = __webpack_require__(1775);
-const { MODE } = __webpack_require__(1762);
-
-const dom = __webpack_require__(1759);
+const PropTypes = __webpack_require__(3642);
+const { wrapRender, ellipsisElement } = __webpack_require__(3644);
+const PropRep = __webpack_require__(3650);
+const { MODE } = __webpack_require__(3645);
+
+const dom = __webpack_require__(3643);
 const { span } = dom;
 
 const DEFAULT_TITLE = "Object";
 
 /**
  * Renders an object. An object is represented by a list of its
  * properties enclosed in curly brackets.
  */
@@ -5012,32 +4914,32 @@ function supportsObject(object, noGrip =
 // Exports from this module
 module.exports = {
   rep: wrapRender(ObjectRep),
   supportsObject
 };
 
 /***/ }),
 
-/***/ 1836:
+/***/ 3677:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 /* 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/>. */
 
 // Dependencies
-const PropTypes = __webpack_require__(1758);
-
-const { getGripType, wrapRender } = __webpack_require__(1760);
-
-const dom = __webpack_require__(1759);
+const PropTypes = __webpack_require__(3642);
+
+const { getGripType, wrapRender } = __webpack_require__(3644);
+
+const dom = __webpack_require__(3643);
 const { span } = dom;
 
 /**
  * Renders a symbol.
  */
 SymbolRep.propTypes = {
   object: PropTypes.object.isRequired
 };
@@ -5059,32 +4961,32 @@ function supportsObject(object, noGrip =
 // Exports from this module
 module.exports = {
   rep: wrapRender(SymbolRep),
   supportsObject
 };
 
 /***/ }),
 
-/***/ 1837:
+/***/ 3678:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 /* 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/>. */
 
 // Dependencies
-const PropTypes = __webpack_require__(1758);
-
-const { getGripType, wrapRender } = __webpack_require__(1760);
-
-const dom = __webpack_require__(1759);
+const PropTypes = __webpack_require__(3642);
+
+const { getGripType, wrapRender } = __webpack_require__(3644);
+
+const dom = __webpack_require__(3643);
 const { span } = dom;
 
 /**
  * Renders a Infinity object
  */
 InfinityRep.propTypes = {
   object: PropTypes.object.isRequired
 };
@@ -5103,30 +5005,30 @@ function supportsObject(object, noGrip =
 // Exports from this module
 module.exports = {
   rep: wrapRender(InfinityRep),
   supportsObject
 };
 
 /***/ }),
 
-/***/ 1838:
+/***/ 3679:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 /* 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/>. */
 
 // Dependencies
-const { getGripType, wrapRender } = __webpack_require__(1760);
-
-const dom = __webpack_require__(1759);
+const { getGripType, wrapRender } = __webpack_require__(3644);
+
+const dom = __webpack_require__(3643);
 const { span } = dom;
 
 /**
  * Renders a NaN object
  */
 function NaNRep(props) {
   return span({ className: "objectBox objectBox-nan" }, "NaN");
 }
@@ -5138,49 +5040,49 @@ function supportsObject(object, noGrip =
 // Exports from this module
 module.exports = {
   rep: wrapRender(NaNRep),
   supportsObject
 };
 
 /***/ }),
 
-/***/ 1839:
+/***/ 3680:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 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; };
 
 /* 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/>. */
 
 // Dependencies
-const dom = __webpack_require__(1759);
-const PropTypes = __webpack_require__(1758);
-const { wrapRender } = __webpack_require__(1760);
-const { MODE } = __webpack_require__(1762);
+const dom = __webpack_require__(3643);
+const PropTypes = __webpack_require__(3642);
+const { wrapRender } = __webpack_require__(3644);
+const { MODE } = __webpack_require__(3645);
 const { span } = dom;
 
 /**
  * Renders an object. An object is represented by a list of its
  * properties enclosed in curly brackets.
  */
 Accessor.propTypes = {
   object: PropTypes.object.isRequired,
   mode: PropTypes.oneOf(Object.values(MODE))
 };
 
 function Accessor(props) {
   const { object, evaluation, onInvokeGetterButtonClick } = props;
 
   if (evaluation) {
-    const { Rep, Grip } = __webpack_require__(1767);
+    const { Rep, Grip } = __webpack_require__(3647);
     return span({
       className: "objectBox objectBox-accessor objectTitle"
     }, Rep(_extends({}, props, {
       object: evaluation.getterValue,
       mode: props.mode || MODE.TINY,
       defaultRep: Grip
     })));
   }
@@ -5227,157 +5129,34 @@ function supportsObject(object, noGrip =
 // Exports from this module
 module.exports = {
   rep: wrapRender(Accessor),
   supportsObject
 };
 
 /***/ }),
 
-/***/ 1840:
+/***/ 3681:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 /* 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/>. */
 
 // ReactJS
-const PropTypes = __webpack_require__(1758);
-const { button, span } = __webpack_require__(1759);
-
-// Utils
-const { isGrip, wrapRender } = __webpack_require__(1760);
-const { rep: StringRep } = __webpack_require__(1770);
-
-/**
- * Renders Accessible object.
- */
-Accessible.propTypes = {
-  object: PropTypes.object.isRequired,
-  inspectIconTitle: PropTypes.string,
-  nameMaxLength: PropTypes.number,
-  onAccessibleClick: PropTypes.func,
-  onAccessibleMouseOver: PropTypes.func,
-  onAccessibleMouseOut: PropTypes.func,
-  onInspectIconClick: PropTypes.func,
-  roleFirst: PropTypes.bool,
-  separatorText: PropTypes.string
-};
-
-function Accessible(props) {
-  const {
-    object,
-    inspectIconTitle,
-    nameMaxLength,
-    onAccessibleClick,
-    onAccessibleMouseOver,
-    onAccessibleMouseOut,
-    onInspectIconClick,
-    roleFirst,
-    separatorText
-  } = props;
-  const elements = getElements(object, nameMaxLength, roleFirst, separatorText);
-  const isInTree = object.preview && object.preview.isConnected === true;
-  const baseConfig = {
-    "data-link-actor-id": object.actor,
-    className: "objectBox objectBox-accessible"
-  };
-
-  let inspectIcon;
-  if (isInTree) {
-    if (onAccessibleClick) {
-      Object.assign(baseConfig, {
-        onClick: _ => onAccessibleClick(object),
-        className: `${baseConfig.className} clickable`
-      });
-    }
-
-    if (onAccessibleMouseOver) {
-      Object.assign(baseConfig, {
-        onMouseOver: _ => onAccessibleMouseOver(object)
-      });
-    }
-
-    if (onAccessibleMouseOut) {
-      Object.assign(baseConfig, {
-        onMouseOut: onAccessibleMouseOut
-      });
-    }
-
-    if (onInspectIconClick) {
-      inspectIcon = button({
-        className: "open-accessibility-inspector",
-        title: inspectIconTitle,
-        onClick: e => {
-          if (onAccessibleClick) {
-            e.stopPropagation();
-          }
-
-          onInspectIconClick(object, e);
-        }
-      });
-    }
-  }
-
-  return span(baseConfig, ...elements, inspectIcon);
-}
-
-function getElements(grip, nameMaxLength, roleFirst = false, separatorText = ": ") {
-  const { name, role } = grip.preview;
-  const elements = [];
-  if (name) {
-    elements.push(StringRep({
-      className: "accessible-name",
-      object: name,
-      cropLimit: nameMaxLength
-    }), span({ className: "separator" }, separatorText));
-  }
-
-  elements.push(span({ className: "accessible-role" }, role));
-  return roleFirst ? elements.reverse() : elements;
-}
-
-// Registration
-function supportsObject(object, noGrip = false) {
-  if (noGrip === true || !isGrip(object)) {
-    return false;
-  }
-
-  return object.preview && object.typeName && object.typeName === "accessible";
-}
-
-// Exports from this module
-module.exports = {
-  rep: wrapRender(Accessible),
-  supportsObject
-};
-
-/***/ }),
-
-/***/ 1841:
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-/* 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/>. */
-
-// ReactJS
-const PropTypes = __webpack_require__(1758);
-const dom = __webpack_require__(1759);
+const PropTypes = __webpack_require__(3642);
+const dom = __webpack_require__(3643);
 const { span } = dom;
 
 // Reps
-const { getGripType, isGrip, wrapRender } = __webpack_require__(1760);
-const { rep: StringRep } = __webpack_require__(1770);
+const { getGripType, isGrip, wrapRender } = __webpack_require__(3644);
+const { rep: StringRep } = __webpack_require__(3648);
 
 /**
  * Renders DOM attribute
  */
 Attribute.propTypes = {
   object: PropTypes.object.isRequired
 };
 
@@ -5406,33 +5185,33 @@ function supportsObject(grip, noGrip = f
 
 module.exports = {
   rep: wrapRender(Attribute),
   supportsObject
 };
 
 /***/ }),
 
-/***/ 1842:
+/***/ 3682:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 /* 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/>. */
 
 // ReactJS
-const PropTypes = __webpack_require__(1758);
+const PropTypes = __webpack_require__(3642);
 
 // Reps
-const { getGripType, isGrip, wrapRender } = __webpack_require__(1760);
-
-const dom = __webpack_require__(1759);
+const { getGripType, isGrip, wrapRender } = __webpack_require__(3644);
+
+const dom = __webpack_require__(3643);
 const { span } = dom;
 
 /**
  * Used to render JS built-in Date() object.
  */
 DateTime.propTypes = {
   object: PropTypes.object.isRequired
 };
@@ -5470,38 +5249,38 @@ function supportsObject(grip, noGrip = f
 // Exports from this module
 module.exports = {
   rep: wrapRender(DateTime),
   supportsObject
 };
 
 /***/ }),
 
-/***/ 1843:
+/***/ 3683:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 /* 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/>. */
 
 // ReactJS
-const PropTypes = __webpack_require__(1758);
+const PropTypes = __webpack_require__(3642);
 
 // Reps
 const {
   getGripType,
   isGrip,
   getURLDisplayString,
   wrapRender
-} = __webpack_require__(1760);
-
-const dom = __webpack_require__(1759);
+} = __webpack_require__(3644);
+
+const dom = __webpack_require__(3643);
 const { span } = dom;
 
 /**
  * Renders DOM document object.
  */
 Document.propTypes = {
   object: PropTypes.object.isRequired
 };
@@ -5539,32 +5318,32 @@ function supportsObject(object, noGrip =
 // Exports from this module
 module.exports = {
   rep: wrapRender(Document),
   supportsObject
 };
 
 /***/ }),
 
-/***/ 1844:
+/***/ 3684:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 /* 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/>. */
 
 // ReactJS
-const PropTypes = __webpack_require__(1758);
+const PropTypes = __webpack_require__(3642);
 
 // Reps
-const { getGripType, isGrip, wrapRender } = __webpack_require__(1760);
-const dom = __webpack_require__(1759);
+const { getGripType, isGrip, wrapRender } = __webpack_require__(3644);
+const dom = __webpack_require__(3643);
 const { span } = dom;
 
 /**
  * Renders DOM documentType object.
  */
 DocumentType.propTypes = {
   object: PropTypes.object.isRequired
 };
@@ -5591,36 +5370,36 @@ function supportsObject(object, noGrip =
 // Exports from this module
 module.exports = {
   rep: wrapRender(DocumentType),
   supportsObject
 };
 
 /***/ }),
 
-/***/ 1845:
+/***/ 3685:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 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; };
 
 /* 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/>. */
 
 // ReactJS
-const PropTypes = __webpack_require__(1758);
+const PropTypes = __webpack_require__(3642);
 
 // Reps
-const { isGrip, wrapRender } = __webpack_require__(1760);
-
-const { MODE } = __webpack_require__(1762);
-const { rep } = __webpack_require__(1784);
+const { isGrip, wrapRender } = __webpack_require__(3644);
+
+const { MODE } = __webpack_require__(3645);
+const { rep } = __webpack_require__(3656);
 
 /**
  * Renders DOM event objects.
  */
 Event.propTypes = {
   object: PropTypes.object.isRequired,
   // @TODO Change this to Object.values when supported in Node's version of V8
   mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
@@ -5697,37 +5476,37 @@ function supportsObject(grip, noGrip = f
 // Exports from this module
 module.exports = {
   rep: wrapRender(Event),
   supportsObject
 };
 
 /***/ }),
 
-/***/ 1846:
+/***/ 3686:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 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; };
 
 /* 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/>. */
 
 // ReactJS
-const PropTypes = __webpack_require__(1758);
+const PropTypes = __webpack_require__(3642);
 // Dependencies
-const { getGripType, isGrip, wrapRender } = __webpack_require__(1760);
-
-const PropRep = __webpack_require__(1775);
-const { MODE } = __webpack_require__(1762);
-
-const dom = __webpack_require__(1759);
+const { getGripType, isGrip, wrapRender } = __webpack_require__(3644);
+
+const PropRep = __webpack_require__(3650);
+const { MODE } = __webpack_require__(3645);
+
+const dom = __webpack_require__(3643);
 const { span } = dom;
 
 /**
  * Renders a DOM Promise object.
  */
 PromiseRep.propTypes = {
   object: PropTypes.object.isRequired,
   // @TODO Change this to Object.values when supported in Node's version of V8
@@ -5742,17 +5521,17 @@ function PromiseRep(props) {
   const { promiseState } = object;
 
   const config = {
     "data-link-actor-id": object.actor,
     className: "objectBox objectBox-object"
   };
 
   if (props.mode === MODE.TINY) {
-    const { Rep } = __webpack_require__(1767);
+    const { Rep } = __webpack_require__(3647);
 
     return span(config, getTitle(object), span({
       className: "objectLeftBrace"
     }, " { "), Rep({ object: promiseState.state }), span({
       className: "objectRightBrace"
     }, " }"));
   }
 
@@ -5806,33 +5585,33 @@ function supportsObject(object, noGrip =
 // Exports from this module
 module.exports = {
   rep: wrapRender(PromiseRep),
   supportsObject
 };
 
 /***/ }),
 
-/***/ 1847:
+/***/ 3687:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 /* 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/>. */
 
 // ReactJS
-const PropTypes = __webpack_require__(1758);
+const PropTypes = __webpack_require__(3642);
 
 // Reps
-const { getGripType, isGrip, wrapRender } = __webpack_require__(1760);
-
-const dom = __webpack_require__(1759);
+const { getGripType, isGrip, wrapRender } = __webpack_require__(3644);
+
+const dom = __webpack_require__(3643);
 const { span } = dom;
 
 /**
  * Renders a grip object with regular expression.
  */
 RegExp.propTypes = {
   object: PropTypes.object.isRequired
 };
@@ -5862,38 +5641,38 @@ function supportsObject(object, noGrip =
 // Exports from this module
 module.exports = {
   rep: wrapRender(RegExp),
   supportsObject
 };
 
 /***/ }),
 
-/***/ 1848:
+/***/ 3688:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 /* 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/>. */
 
 // ReactJS
-const PropTypes = __webpack_require__(1758);
+const PropTypes = __webpack_require__(3642);
 
 // Reps
 const {
   getGripType,
   isGrip,
   getURLDisplayString,
   wrapRender
-} = __webpack_require__(1760);
-
-const dom = __webpack_require__(1759);
+} = __webpack_require__(3644);
+
+const dom = __webpack_require__(3643);
 const { span } = dom;
 
 /**
  * Renders a grip representing CSSStyleSheet
  */
 StyleSheet.propTypes = {
   object: PropTypes.object.isRequired
 };
@@ -5931,37 +5710,37 @@ function supportsObject(object, noGrip =
 
 module.exports = {
   rep: wrapRender(StyleSheet),
   supportsObject
 };
 
 /***/ }),
 
-/***/ 1849:
+/***/ 3689:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 /* 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/>. */
 
 // Dependencies
-const PropTypes = __webpack_require__(1758);
+const PropTypes = __webpack_require__(3642);
 const {
   isGrip,
   cropString,
   cropMultipleLines,
   wrapRender
-} = __webpack_require__(1760);
-const { MODE } = __webpack_require__(1762);
-const nodeConstants = __webpack_require__(1796);
-const dom = __webpack_require__(1759);
+} = __webpack_require__(3644);
+const { MODE } = __webpack_require__(3645);
+const nodeConstants = __webpack_require__(3659);
+const dom = __webpack_require__(3643);
 const { span } = dom;
 
 /**
  * Renders DOM comment node.
  */
 CommentNode.propTypes = {
   object: PropTypes.object.isRequired,
   // @TODO Change this to Object.values when supported in Node's version of V8
@@ -5995,36 +5774,36 @@ function supportsObject(object, noGrip =
 // Exports from this module
 module.exports = {
   rep: wrapRender(CommentNode),
   supportsObject
 };
 
 /***/ }),
 
-/***/ 1850:
+/***/ 3690:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 /* 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/>. */
 
 // ReactJS
-const PropTypes = __webpack_require__(1758);
+const PropTypes = __webpack_require__(3642);
 
 // Utils
-const { isGrip, wrapRender } = __webpack_require__(1760);
-const { rep: StringRep } = __webpack_require__(1770);
-const { MODE } = __webpack_require__(1762);
-const nodeConstants = __webpack_require__(1796);
-
-const dom = __webpack_require__(1759);
+const { isGrip, wrapRender } = __webpack_require__(3644);
+const { rep: StringRep } = __webpack_require__(3648);
+const { MODE } = __webpack_require__(3645);
+const nodeConstants = __webpack_require__(3659);
+
+const dom = __webpack_require__(3643);
 const { span } = dom;
 
 /**
  * Renders DOM element node.
  */
 ElementNode.propTypes = {
   object: PropTypes.object.isRequired,
   inspectIconTitle: PropTypes.string,
@@ -6150,34 +5929,34 @@ function supportsObject(object, noGrip =
 // Exports from this module
 module.exports = {
   rep: wrapRender(ElementNode),
   supportsObject
 };
 
 /***/ }),
 
-/***/ 1851:
+/***/ 3691:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 /* 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/>. */
 
 // ReactJS
-const PropTypes = __webpack_require__(1758);
+const PropTypes = __webpack_require__(3642);
 
 // Reps
-const { isGrip, cropString, wrapRender } = __webpack_require__(1760);
-const { MODE } = __webpack_require__(1762);
-
-const dom = __webpack_require__(1759);
+const { isGrip, cropString, wrapRender } = __webpack_require__(3644);
+const { MODE } = __webpack_require__(3645);
+
+const dom = __webpack_require__(3643);
 const { span } = dom;
 
 /**
  * Renders DOM #text node.
  */
 TextNode.propTypes = {
   object: PropTypes.object.isRequired,
   // @TODO Change this to Object.values when supported in Node's version of V8
@@ -6255,40 +6034,40 @@ function supportsObject(grip, noGrip = f
 // Exports from this module
 module.exports = {
   rep: wrapRender(TextNode),
   supportsObject
 };
 
 /***/ }),
 
-/***/ 1852:
+/***/ 3692:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 /* 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/>. */
 
 // ReactJS
-const PropTypes = __webpack_require__(1758);
+const PropTypes = __webpack_require__(3642);
 
 // Reps
 const {
   getGripType,
   isGrip,
   getURLDisplayString,
   wrapRender
-} = __webpack_require__(1760);
-
-const { MODE } = __webpack_require__(1762);
-
-const dom = __webpack_require__(1759);
+} = __webpack_require__(3644);
+
+const { MODE } = __webpack_require__(3645);
+
+const dom = __webpack_require__(3643);
 const { span } = dom;
 
 /**
  * Renders a grip representing a window.
  */
 WindowRep.propTypes = {
   // @TODO Change this to Object.values when supported in Node's version of V8
   mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
@@ -6334,35 +6113,35 @@ function supportsObject(object, noGrip =
 // Exports from this module
 module.exports = {
   rep: wrapRender(WindowRep),
   supportsObject
 };
 
 /***/ }),
 
-/***/ 1853:
+/***/ 3693:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 /* 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/>. */
 
 // ReactJS
-const PropTypes = __webpack_require__(1758);
+const PropTypes = __webpack_require__(3642);
 
 // Reps
-const { isGrip, wrapRender } = __webpack_require__(1760);
-
-const String = __webpack_require__(1770).rep;
-
-const dom = __webpack_require__(1759);
+const { isGrip, wrapRender } = __webpack_require__(3644);
+
+const String = __webpack_require__(3648).rep;
+
+const dom = __webpack_require__(3643);
 const { span } = dom;
 
 /**
  * Renders a grip object with textual data.
  */
 ObjectWithText.propTypes = {
   object: PropTypes.object.isRequired
 };
@@ -6397,33 +6176,33 @@ function supportsObject(grip, noGrip = f
 // Exports from this module
 module.exports = {
   rep: wrapRender(ObjectWithText),
   supportsObject
 };
 
 /***/ }),
 
-/***/ 1854:
+/***/ 3694:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 /* 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/>. */
 
 // ReactJS
-const PropTypes = __webpack_require__(1758);
+const PropTypes = __webpack_require__(3642);
 
 // Reps
-const { isGrip, getURLDisplayString, wrapRender } = __webpack_require__(1760);
-
-const dom = __webpack_require__(1759);
+const { isGrip, getURLDisplayString, wrapRender } = __webpack_require__(3644);
+
+const dom = __webpack_require__(3643);
 const { span } = dom;
 
 /**
  * Renders a grip object with URL data.
  */
 ObjectWithURL.propTypes = {
   object: PropTypes.object.isRequired
 };
@@ -6460,66 +6239,448 @@ function supportsObject(grip, noGrip = f
 // Exports from this module
 module.exports = {
   rep: wrapRender(ObjectWithURL),
   supportsObject
 };
 
 /***/ }),
 
-/***/ 1855:
+/***/ 3695:
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+/* 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 ObjectInspector = __webpack_require__(3829);
+const utils = __webpack_require__(3657);
+const reducer = __webpack_require__(3703);
+
+module.exports = { ObjectInspector, utils, reducer };
+
+/***/ }),
+
+/***/ 3698:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 /* 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 ObjectInspector = __webpack_require__(1856);
-const utils = __webpack_require__(1787);
-const reducer = __webpack_require__(1786);
-
-module.exports = { ObjectInspector, utils, reducer };
+function documentHasSelection() {
+  const selection = getSelection();
+  if (!selection) {
+    return false;
+  }
+
+  return selection.type === "Range";
+}
+
+module.exports = {
+  documentHasSelection
+};
 
 /***/ }),
 
-/***/ 1856:
+/***/ 3699:
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+const { loadItemProperties } = __webpack_require__(3666); /* 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 { getLoadedProperties, getActors } = __webpack_require__(3703);
+
+/**
+ * This action is responsible for expanding a given node, which also means that
+ * it will call the action responsible to fetch properties.
+ */
+function nodeExpand(node, actor) {
+  return async ({ dispatch, getState }) => {
+    dispatch({ type: "NODE_EXPAND", data: { node } });
+    dispatch(nodeLoadProperties(node, actor));
+  };
+}
+
+function nodeCollapse(node) {
+  return {
+    type: "NODE_COLLAPSE",
+    data: { 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(node, actor) {
+  return async ({ dispatch, client, getState }) => {
+    const state = getState();
+    const loadedProperties = getLoadedProperties(state);
+    if (loadedProperties.has(node.path)) {
+      return;
+    }
+
+    try {
+      const properties = await loadItemProperties(node, client.createObjectClient, client.createLongStringClient, loadedProperties);
+
+      dispatch(nodePropertiesLoaded(node, actor, properties));
+    } catch (e) {
+      console.error(e);
+    }
+  };
+}
+
+function nodePropertiesLoaded(node, actor, properties) {
+  return {
+    type: "NODE_PROPERTIES_LOADED",
+    data: { node, actor, properties }
+  };
+}
+
+function closeObjectInspector() {
+  return async ({ getState, client }) => {
+    releaseActors(getState(), client);
+  };
+}
+
+/*
+ * This action is dispatched when the `roots` prop, provided by a consumer of
+ * the ObjectInspector (inspector, console, …), is modified. It will clean the
+ * internal state properties (expandedPaths, loadedProperties, …) and release
+ * the actors consumed with the previous roots.
+ * It takes a props argument which reflects what is passed by the upper-level
+ * consumer.
+ */
+function rootsChanged(props) {
+  return async ({ dispatch, client, getState }) => {
+    releaseActors(getState(), client);
+    dispatch({
+      type: "ROOTS_CHANGED",
+      data: props
+    });
+  };
+}
+
+function releaseActors(state, client) {
+  const actors = getActors(state);
+  for (const actor of actors) {
+    client.releaseActor(actor);
+  }
+}
+
+function invokeGetter(node, grip, getterName) {
+  return async ({ dispatch, client, getState }) => {
+    try {
+      const objectClient = client.createObjectClient(grip);
+      const result = await objectClient.getPropertyValue(getterName);
+      dispatch({
+        type: "GETTER_INVOKED",
+        data: {
+          node,
+          result
+        }
+      });
+    } catch (e) {
+      console.error(e);
+    }
+  };
+}
+
+module.exports = {
+  closeObjectInspector,
+  invokeGetter,
+  nodeExpand,
+  nodeCollapse,
+  nodeLoadProperties,
+  nodePropertiesLoaded,
+  rootsChanged
+};
+
+/***/ }),
+
+/***/ 3703:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 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; };
 
-var _devtoolsComponents = __webpack_require__(1792);
+function initialState() {
+  return {
+    expandedPaths: new Set(),
+    loadedProperties: new Map(),
+    evaluations: new Map(),
+    actors: new Set()
+  };
+} /* 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 reducer(state = initialState(), action = {}) {
+  const { type, data } = action;
+
+  const cloneState = overrides => _extends({}, state, overrides);
+
+  if (type === "NODE_EXPAND") {
+    return cloneState({
+      expandedPaths: new Set(state.expandedPaths).add(data.node.path)
+    });
+  }
+
+  if (type === "NODE_COLLAPSE") {
+    const expandedPaths = new Set(state.expandedPaths);
+    expandedPaths.delete(data.node.path);
+    return cloneState({ expandedPaths });
+  }
+
+  if (type === "NODE_PROPERTIES_LOADED") {
+    return cloneState({
+      actors: data.actor ? new Set(state.actors || []).add(data.actor) : state.actors,
+      loadedProperties: new Map(state.loadedProperties).set(data.node.path, action.data.properties)
+    });
+  }
+
+  if (type === "ROOTS_CHANGED") {
+    return cloneState();
+  }
+
+  if (type === "GETTER_INVOKED") {
+    return cloneState({
+      actors: data.actor ? new Set(state.actors || []).add(data.result.from) : state.actors,
+      evaluations: new Map(state.evaluations).set(data.node.path, {
+        getterValue: data.result && data.result.value && (data.result.value.return || data.result.value.throw)
+      })
+    });
+  }
+
+  return state;
+}
+
+function getObjectInspectorState(state) {
+  return state.objectInspector;
+}
+
+function getExpandedPaths(state) {
+  return getObjectInspectorState(state).expandedPaths;
+}
+
+function getExpandedPathKeys(state) {
+  return [...getExpandedPaths(state).keys()];
+}
+
+function getActors(state) {
+  return getObjectInspectorState(state).actors;
+}
+
+function getLoadedProperties(state) {
+  return getObjectInspectorState(state).loadedProperties;
+}
+
+function getLoadedPropertyKeys(state) {
+  return [...getLoadedProperties(state).keys()];
+}
+
+function getEvaluations(state) {
+  return getObjectInspectorState(state).evaluations;
+}
+
+const selectors = {
+  getActors,
+  getEvaluations,
+  getExpandedPathKeys,
+  getExpandedPaths,
+  getLoadedProperties,
+  getLoadedPropertyKeys
+};
+
+Object.defineProperty(module.exports, "__esModule", {
+  value: true
+});
+module.exports = selectors;
+module.exports.default = reducer;
+
+/***/ }),
+
+/***/ 3730:
+/***/ (function(module, exports, __webpack_require__) {
+
+module.exports = __webpack_require__(3655);
+
+
+/***/ }),
+
+/***/ 3787:
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+/* 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/>. */
+
+// ReactJS
+const PropTypes = __webpack_require__(3642);
+const { button, span } = __webpack_require__(3643);
+
+// Utils
+const { isGrip, wrapRender } = __webpack_require__(3644);
+const { rep: StringRep } = __webpack_require__(3648);
+
+/**
+ * Renders Accessible object.
+ */
+Accessible.propTypes = {
+  object: PropTypes.object.isRequired,
+  inspectIconTitle: PropTypes.string,
+  nameMaxLength: PropTypes.number,
+  onAccessibleClick: PropTypes.func,
+  onAccessibleMouseOver: PropTypes.func,
+  onAccessibleMouseOut: PropTypes.func,
+  onInspectIconClick: PropTypes.func,
+  roleFirst: PropTypes.bool,
+  separatorText: PropTypes.string
+};
+
+function Accessible(props) {
+  const {
+    object,
+    inspectIconTitle,
+    nameMaxLength,
+    onAccessibleClick,
+    onAccessibleMouseOver,
+    onAccessibleMouseOut,
+    onInspectIconClick,
+    roleFirst,
+    separatorText
+  } = props;
+  const elements = getElements(object, nameMaxLength, roleFirst, separatorText);
+  const isInTree = object.preview && object.preview.isConnected === true;
+  const baseConfig = {
+    "data-link-actor-id": object.actor,
+    className: "objectBox objectBox-accessible"
+  };
+
+  let inspectIcon;
+  if (isInTree) {
+    if (onAccessibleClick) {
+      Object.assign(baseConfig, {
+        onClick: _ => onAccessibleClick(object),
+        className: `${baseConfig.className} clickable`
+      });
+    }
+
+    if (onAccessibleMouseOver) {
+      Object.assign(baseConfig, {
+        onMouseOver: _ => onAccessibleMouseOver(object)
+      });
+    }
+
+    if (onAccessibleMouseOut) {
+      Object.assign(baseConfig, {
+        onMouseOut: onAccessibleMouseOut
+      });
+    }
+
+    if (onInspectIconClick) {
+      inspectIcon = button({
+        className: "open-accessibility-inspector",
+        title: inspectIconTitle,
+        onClick: e => {
+          if (onAccessibleClick) {
+            e.stopPropagation();
+          }
+
+          onInspectIconClick(object, e);
+        }
+      });
+    }
+  }
+
+  return span(baseConfig, ...elements, inspectIcon);
+}
+
+function getElements(grip, nameMaxLength, roleFirst = false, separatorText = ": ") {
+  const { name, role } = grip.preview;
+  const elements = [];
+  if (name) {
+    elements.push(StringRep({
+      className: "accessible-name",
+      object: name,
+      cropLimit: nameMaxLength
+    }), span({ className: "separator" }, separatorText));
+  }
+
+  elements.push(span({ className: "accessible-role" }, role));
+  return roleFirst ? elements.reverse() : elements;
+}
+
+// Registration
+function supportsObject(object, noGrip = false) {
+  if (noGrip === true || !isGrip(object)) {
+    return false;
+  }
+
+  return object.preview && object.typeName && object.typeName === "accessible";
+}
+
+// Exports from this module
+module.exports = {
+  rep: wrapRender(Accessible),
+  supportsObject
+};
+
+/***/ }),
+
+/***/ 3829:
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+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; };
+
+var _devtoolsComponents = __webpack_require__(3669);
 
 var _devtoolsComponents2 = _interopRequireDefault(_devtoolsComponents);
 
 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/>. */
 
 const { Component, createFactory, createElement } = __webpack_require__(0);
-const { connect } = __webpack_require__(1763);
-const actions = __webpack_require__(1857);
-
-const selectors = __webpack_require__(1786);
+const { connect } = __webpack_require__(3592);
+const actions = __webpack_require__(3699);
+
+const selectors = __webpack_require__(3703);
 
 const Tree = createFactory(_devtoolsComponents2.default.Tree);
-__webpack_require__(1858);
-
-const ObjectInspectorItem = createFactory(__webpack_require__(1859));
+__webpack_require__(3830);
+
+const ObjectInspectorItem = createFactory(__webpack_require__(3831));
 
 const classnames = __webpack_require__(175);
 
-const Utils = __webpack_require__(1787);
+const Utils = __webpack_require__(3657);
 const { renderRep, shouldRenderRootsInReps } = Utils;
 const {
   getChildrenWithEvaluations,
   getActor,
   getParent,
   nodeIsPrimitive,
   nodeHasGetter,
   nodeHasSetter
@@ -6752,145 +6913,24 @@ module.exports = props => {
     return renderRep(roots[0], props);
   }
 
   return createElement(OI, props);
 };
 
 /***/ }),
 
-/***/ 1857:
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-const { loadItemProperties } = __webpack_require__(1804); /* 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 { getLoadedProperties, getActors } = __webpack_require__(1786);
-
-/**
- * This action is responsible for expanding a given node, which also means that
- * it will call the action responsible to fetch properties.
- */
-function nodeExpand(node, actor) {
-  return async ({ dispatch, getState }) => {
-    dispatch({ type: "NODE_EXPAND", data: { node } });
-    dispatch(nodeLoadProperties(node, actor));
-  };
-}
-
-function nodeCollapse(node) {
-  return {
-    type: "NODE_COLLAPSE",
-    data: { 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(node, actor) {
-  return async ({ dispatch, client, getState }) => {
-    const state = getState();
-    const loadedProperties = getLoadedProperties(state);
-    if (loadedProperties.has(node.path)) {
-      return;
-    }
-
-    try {
-      const properties = await loadItemProperties(node, client.createObjectClient, client.createLongStringClient, loadedProperties);
-
-      dispatch(nodePropertiesLoaded(node, actor, properties));
-    } catch (e) {
-      console.error(e);
-    }
-  };
-}
-
-function nodePropertiesLoaded(node, actor, properties) {
-  return {
-    type: "NODE_PROPERTIES_LOADED",
-    data: { node, actor, properties }
-  };
-}
-
-function closeObjectInspector() {
-  return async ({ getState, client }) => {
-    releaseActors(getState(), client);
-  };
-}
-
-/*
- * This action is dispatched when the `roots` prop, provided by a consumer of
- * the ObjectInspector (inspector, console, …), is modified. It will clean the
- * internal state properties (expandedPaths, loadedProperties, …) and release
- * the actors consumed with the previous roots.
- * It takes a props argument which reflects what is passed by the upper-level
- * consumer.
- */
-function rootsChanged(props) {
-  return async ({ dispatch, client, getState }) => {
-    releaseActors(getState(), client);
-    dispatch({
-      type: "ROOTS_CHANGED",
-      data: props
-    });
-  };
-}
-
-function releaseActors(state, client) {
-  const actors = getActors(state);
-  for (const actor of actors) {
-    client.releaseActor(actor);
-  }
-}
-
-function invokeGetter(node, grip, getterName) {
-  return async ({ dispatch, client, getState }) => {
-    try {
-      const objectClient = client.createObjectClient(grip);
-      const result = await objectClient.getPropertyValue(getterName);
-      dispatch({
-        type: "GETTER_INVOKED",
-        data: {
-          node,
-          result
-        }
-      });
-    } catch (e) {
-      console.error(e);
-    }
-  };
-}
-
-module.exports = {
-  closeObjectInspector,
-  invokeGetter,
-  nodeExpand,
-  nodeCollapse,
-  nodeLoadProperties,
-  nodePropertiesLoaded,
-  rootsChanged
-};
-
-/***/ }),
-
-/***/ 1858:
+/***/ 3830:
 /***/ (function(module, exports) {
 
 // removed by extract-text-webpack-plugin
 
 /***/ }),
 
-/***/ 1859:
+/***/ 3831:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 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; };
 
 var _devtoolsServices = __webpack_require__(22);
@@ -6899,25 +6939,25 @@ var _devtoolsServices2 = _interopRequire
 
 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/>. */
 
 const { Component } = __webpack_require__(0);
-const dom = __webpack_require__(1759);
+const dom = __webpack_require__(3643);
 
 const { appinfo } = _devtoolsServices2.default;
 const isMacOS = appinfo.OS === "Darwin";
 
 const classnames = __webpack_require__(175);
-const { MODE } = __webpack_require__(1762);
-
-const Utils = __webpack_require__(1787);
+const { MODE } = __webpack_require__(3645);
+
+const Utils = __webpack_require__(3657);
 
 const {
   getValue,
   nodeHasAccessors,
   nodeHasProperties,
   nodeIsBlock,
   nodeIsDefaultProperties,
   nodeIsFunction,
@@ -7116,52 +7156,12 @@ class ObjectInspectorItem extends Compon
     const delimiter = value && labelElement ? dom.span({ className: "object-delimiter" }, ": ") : null;
 
     return dom.div(this.getTreeItemProps(), arrow, labelElement, delimiter, value);
   }
 }
 
 module.exports = ObjectInspectorItem;
 
-/***/ }),
-
-/***/ 1860:
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-/* 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 documentHasSelection() {
-  const selection = getSelection();
-  if (!selection) {
-    return false;
-  }
-
-  return selection.type === "Range";
-}
-
-module.exports = {
-  documentHasSelection
-};
-
-/***/ }),
-
-/***/ 2105:
-/***/ (function(module, exports, __webpack_require__) {
-
-module.exports = __webpack_require__(1780);
-
-
-/***/ }),
-
-/***/ 22:
-/***/ (function(module, exports) {
-
-module.exports = __WEBPACK_EXTERNAL_MODULE_22__;
-
 /***/ })
 
 /******/ });
 });
\ No newline at end of file
--- a/devtools/client/themes/toolbox.css
+++ b/devtools/client/themes/toolbox.css
@@ -459,16 +459,18 @@
   background: var(--theme-tab-toolbar-background);
   border-bottom: 1px solid var(--theme-splitter-color);
   box-sizing: border-box;
   min-height: 29px;
 
   --progress-recording-background: hsl(0, 100%, 97%);
   --progress-playing-background: hsl(207, 100%, 97%);
 
+  --progress-bar-transition: 200ms;
+
   --recording-marker-background: hsl(14.9, 100%, 67%);
   --recording-marker-background-hover: hsl(14.9, 100%, 47%);
   --command-button-size: 14px;
   --command-button-primary-size: 20px;
 }
 
 .webreplay-player .overlay-container {
   display: flex;
@@ -483,55 +485,59 @@
   border: 1px solid #bfc9d2;
 }
 
 .webreplay-player .progress {
   position: absolute;
   width: 100%;
   height: 100%;
   background: var(--progress-playing-background);
+  transition-duration: var(--progress-bar-transition);
 }
 
 .webreplay-player #overlay:not(.recording) .progress::after {
   background: var(--purple-50);
   width: 1px;
   height: 100%;
   right: 0;
   opacity: 0.4;
   display: block;
   content: "";
   position: absolute;
 }
 
 .webreplay-player .recording .progress {
   background: var(--progress-recording-background);
+  transition-duration: var(--progress-bar-transition);
 }
 
 .webreplay-player .message {
   position: absolute;
   height: 100%;
   width: 7px;
   height: 7px;
   border-radius: 4.5px;
   top: calc(50% - 3.5px);
   background: var(--blue-40);
+  transition-duration: 100ms;
 }
 
 .webreplay-player .message.overlayed {
   border: 1px solid var(--progress-playing-background);
   top: 5.5px;
 }
 
 .webreplay-player .message.overlayed.future {
   border-color: #fff;
 }
 
 .webreplay-player .message.highlighted {
   background-color: var(--blue-60);
   transform: scale(1.25);
+  transition-duration: 100ms;
 }
 
 .webreplay-player .recording .message.highlighted {
   background-color: var(--recording-marker-background-hover);
 }
 
 .webreplay-player .recording .message.overlayed {
   border-color: var(--progress-recording-background);
@@ -606,18 +612,18 @@
 }
 
 .webreplay-player .progress-line {
   width: 0%;
   height: 1px;
   background: var(--blue-40);
   position: absolute;
   left: 0;
-  right: 10px;
   top: 50%;
+  transition-duration: var(--progress-bar-transition);
 }
 
 .webreplay-player .progress-line.end {
   opacity: 0.3;
 }
 
 .webreplay-player .recording .progress-line {
   background: #d0021b;
--- a/devtools/client/themes/webconsole.css
+++ b/devtools/client/themes/webconsole.css
@@ -117,17 +117,23 @@ a {
   height: 1px;
   bottom: 0px;
   left: -3px;
   display: block;
   content: "";
   position: absolute;
 }
 
-.message.paused ~ .message:not(.command):not(.result) .message-body-wrapper {
+.message.paused.paused-before::before {
+  top: 0px;
+  bottom: inherit;
+}
+
+.message.paused ~ .message:not(.command):not(.result) .message-body-wrapper,
+.message.paused.paused-before .message-body-wrapper {
   opacity: 0.5;
 }
 
 .message.startGroup,
 .message.startGroupCollapsed {
   --console-output-indent-border-color: transparent;
 }
 
--- a/devtools/client/webconsole/components/ConsoleOutput.js
+++ b/devtools/client/webconsole/components/ConsoleOutput.js
@@ -3,16 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const { Component, createFactory } = require("devtools/client/shared/vendor/react");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 const { connect } = require("devtools/client/shared/redux/visibility-handler-connect");
 const {initialize} = require("devtools/client/webconsole/actions/ui");
+const {sortBy} = require("devtools/client/shared/vendor/lodash");
 
 const {
   getAllMessagesById,
   getAllMessagesUiById,
   getAllMessagesTableDataById,
   getAllNetworkMessagesUpdateById,
   getVisibleMessages,
   getPausedExecutionPoint,
@@ -21,34 +22,28 @@ const {
 const MessageContainer = createFactory(require("devtools/client/webconsole/components/MessageContainer").MessageContainer);
 const {
   MESSAGE_TYPE,
 } = require("devtools/client/webconsole/constants");
 const {
   getInitialMessageCountForViewport,
 } = require("devtools/client/webconsole/utils/messages.js");
 
-// Finds the message that comes right after the current paused execution point.
-// NOTE: visibleMessages are not guaranteed to be ordered.
-function getPausedMessage(visibleMessages, messages, executionPoint) {
+function getClosestMessage(visibleMessages, messages, executionPoint) {
   if (!executionPoint || !visibleMessages) {
     return null;
   }
 
-  let pausedMessage = messages.get(visibleMessages[0]);
-  for (const messageId of visibleMessages) {
-    const message = messages.get(messageId);
-    if (message.executionPoint &&
-        executionPoint.progress >= message.executionPoint.progress &&
-        message.executionPoint.progress > pausedMessage.executionPoint.progress) {
-      pausedMessage = message;
-    }
-  }
+  const { progress } = executionPoint;
+  const getProgress = m => m && m.executionPoint && m.executionPoint.progress;
 
-  return pausedMessage;
+  return sortBy(
+    visibleMessages.map(id => messages.get(id)),
+    m => Math.abs(progress - getProgress(m))
+  )[0];
 }
 
 class ConsoleOutput extends Component {
   static get propTypes() {
     return {
       initialized: PropTypes.bool.isRequired,
       messages: PropTypes.object.isRequired,
       messagesUi: PropTypes.array.isRequired,
@@ -160,17 +155,17 @@ class ConsoleOutput extends Component {
     if (!initialized) {
       const numberMessagesFitViewport = getInitialMessageCountForViewport(window);
       if (numberMessagesFitViewport < visibleMessages.length) {
         visibleMessages = visibleMessages.slice(
           visibleMessages.length - numberMessagesFitViewport);
       }
     }
 
-    const pausedMessage = getPausedMessage(
+    const pausedMessage =