Bug 1446501 - Update Debugger Frontend v24. r=jdescottes
authorJason Laster <jason.laster.11@gmail.com>
Fri, 16 Mar 2018 14:30:34 -0400
changeset 462242 5c72707149c3885a42a573c558dda1224594633c
parent 462241 00da6f45a098a51748cce5759fb3ed6241d6afa9
child 462243 384c57a10906f919aa27af99575186e9f14aaa20
push id9165
push userasasaki@mozilla.com
push dateThu, 26 Apr 2018 21:04:54 +0000
treeherdermozilla-beta@064c3804de2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjdescottes
bugs1446501
milestone61.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1446501 - Update Debugger Frontend v24. r=jdescottes MozReview-Commit-ID: DgETcSJv89H
devtools/client/debugger/new/README.mozilla
devtools/client/debugger/new/debugger.css
devtools/client/debugger/new/debugger.js
devtools/client/debugger/new/parser-worker.js
devtools/client/debugger/new/test/mochitest/browser_dbg-content-script-sources.js
devtools/client/debugger/new/test/mochitest/browser_dbg-editor-highlight.js
devtools/client/debugger/new/test/mochitest/browser_dbg-outline.js
devtools/client/debugger/new/test/mochitest/browser_dbg-pretty-print-console.js
devtools/client/debugger/new/test/mochitest/browser_dbg-reload.js
devtools/client/debugger/new/test/mochitest/browser_dbg-sourcemaps2.js
devtools/client/debugger/new/test/mochitest/browser_dbg-stepping.js
devtools/client/debugger/new/test/mochitest/head.js
--- 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 23.0
+Version 24.0
 
-Comparison: https://github.com/devtools-html/debugger.html/compare/release-22...release-23
+Comparison: https://github.com/devtools-html/debugger.html/compare/release-23...release-24
 
 Packages:
 - babel-plugin-transform-es2015-modules-commonjs @6.26.0
 - babel-preset-react @6.24.1
 - react @16.2.0
 - react-dom @16.2.0
 - webpack @3.11.0
--- a/devtools/client/debugger/new/debugger.css
+++ b/devtools/client/debugger/new/debugger.css
@@ -101,21 +101,23 @@
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 :root {
   --icon-size: 13px;
 }
 
 :root.theme-light,
 :root .theme-light {
+  --search-overlays-semitransparent: rgba(221, 225, 228, 0.66);
   --popup-shadow-color: #d0d0d0;
 }
 
 :root.theme-dark,
 :root .theme-dark {
+  --search-overlays-semitransparent: rgba(42, 46, 56, 0.66);
   --popup-shadow-color: #5c667b;
 }
 /* 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/>. */
 
 * {
   box-sizing: border-box;
@@ -1131,21 +1133,68 @@ html[dir="rtl"] .arrow svg,
   position: relative;
 }
 
 .sources-panel * {
   -moz-user-select: none;
   user-select: none;
 }
 
+.sources-clear-root {
+  padding: 4px 3px 4px 3px;
+  width: 100%;
+  text-align: start;
+  white-space: nowrap;
+  color: inherit;
+  display: block;
+  position: absolute;
+  top: 0;
+  left: 0;
+  border-bottom: 1px solid var(--theme-splitter-color);
+}
+
+.sources-clear-root i {
+  margin-right: 5px;
+  position: relative;
+}
+
+.sources-clear-root svg {
+  width: 13px;
+  height: 13px;
+}
+
+.theme-dark .sources-clear-root svg {
+  fill: var(--theme-body-color);
+}
+
+.sources-clear-root .home {
+  opacity: 0.5;
+}
+
+.sources-clear-root .breadcrumb svg {
+  width: 5px;
+  top: 2px;
+  position: absolute;
+  margin-right: 5px;
+}
+
+.sources-clear-root-label {
+  margin-left: 5px;
+}
+
+.sources-pane {
+  display: flex;
+  flex: 1;
+  overflow-x: auto;
+  overflow-y: auto;
+}
+
 .sources-list {
   flex: 1;
   display: flex;
-  overflow-x: hidden;
-  overflow-y: auto;
 }
 
 .sources-list .tree:focus {
   outline: none;
 }
 
 .sources-list .managed-tree {
   flex: 1;
@@ -1283,16 +1332,41 @@ html[dir="rtl"] .arrow svg,
   background-color: white;
 }
 
 .tree:not(.object-inspector)
   .tree-node[data-expandable="false"]
   .tree-indent:last-of-type {
   margin-inline-end: 4px;
 }
+
+/*
+  Custom root styles
+*/
+.sources-pane.sources-list-custom-root {
+  display: block;
+  position: relative;
+}
+
+.sources-list-custom-root .sources-pane {
+  display: block;
+}
+
+.sources-list-custom-root .sources-list {
+  position: absolute;
+  top: 26px;
+  right: 0;
+  bottom: 0;
+  left: 0;
+}
+
+/* Removes start margin when a custom root is used */
+.sources-list-custom-root .tree > .tree-node[data-expandable="false"][aria-level="0"] {
+  padding-inline-start: 4px;
+}
 /* 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/>. */
 
 .outline {
   overflow-y: hidden;
 }
 
@@ -1383,16 +1457,20 @@ html[dir="rtl"] .arrow svg,
   z-index: 1;
   -moz-user-select: none;
   user-select: none;
   height: 25px;
   box-sizing: border-box;
   display: flex;
 }
 
+.theme-dark .outline-footer button {
+    color: var(--theme-body-color);
+}
+
 .outline-footer button.active {
   background: var(--theme-highlight-blue);
   color: #ffffff;
 }
 /* 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/>. */
 
@@ -3858,29 +3936,43 @@ html .welcomebox .toggle-button-end.coll
   line-height: 1.5em;
   word-break: break-all;
 }
 
 .result-list li.selected .title {
   color: white;
 }
 
-.result-list.big li.selected .title {
-  color: var(--theme-body-color);
+.result-list.big li.selected {
+  background-color: var(--theme-selection-background);
+  color: white;
+}
+
+.result-list.big li.selected .subtitle {
+  color: white;
+}
+
+.result-list.big li.selected .subtitle .highlight {
+  color: white;
+  font-weight: bold;
 }
 
 .result-list.big li .subtitle {
   word-break: break-all;
   color: var(--theme-body-color-inactive);
 }
 
 .result-list.big li .subtitle {
   line-height: 1.5em;
 }
 
+.theme-dark .result-list.big li.selected .subtitle {
+  color: white;
+}
+
 .theme-dark .result-list.big li .subtitle {
   color: var(--theme-comment-alt);
 }
 
 .search-bar .result-list li.selected .subtitle {
   color: white;
 }
 
@@ -3888,21 +3980,23 @@ html .welcomebox .toggle-button-end.coll
   border-bottom: 1px solid var(--theme-splitter-color);
 }
 
 .theme-dark .result-list {
   background-color: var(--theme-body-background);
 }
 .result-item .title .highlight {
   font-weight: bold;
+  background-color: transparent;
 }
 
 .result-item .subtitle .highlight {
   color: var(--grey-90);
   font-weight: 500;
+  background-color: transparent;
 }
 
 .theme-dark .result-item .title .highlight,
 .theme-dark .result-item .subtitle .highlight {
   color: white;
 }
 
 .loading-indicator {
--- a/devtools/client/debugger/new/debugger.js
+++ b/devtools/client/debugger/new/debugger.js
@@ -3593,16 +3593,17 @@ function update(state = initialSourcesSt
 function getTextPropsFromAction(action) {
   const { value, sourceId } = action;
 
   if (action.status === "start") {
     return { id: sourceId, loadedState: "loading" };
   } else if (action.status === "error") {
     return { id: sourceId, error: action.error, loadedState: "loaded" };
   }
+
   return {
     text: value.text,
     id: sourceId,
     contentType: value.contentType,
     loadedState: "loaded"
   };
 }
 
@@ -3614,22 +3615,24 @@ function setSourceTextProps(state, actio
   const text = getTextPropsFromAction(action);
   return updateSource(state, text);
 }
 
 function updateSource(state, source) {
   if (!source.id) {
     return state;
   }
+
   const existingSource = state.getIn(["sources", source.id]);
 
   if (existingSource) {
     const updatedSource = existingSource.merge(source);
     return state.setIn(["sources", source.id], updatedSource);
   }
+
   return state.setIn(["sources", source.id], new SourceRecordClass(source));
 }
 
 function removeSourceFromTabList(tabs, url) {
   return tabs.filter(tab => tab != url);
 }
 
 function removeSourcesFromTabList(tabs, urls) {
@@ -4531,300 +4534,16 @@ SearchInput.defaultProps = {
   hasPrefix: false,
   selectedItemId: "",
   size: ""
 };
 exports.default = SearchInput;
 
 /***/ }),
 
-/***/ 1380:
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-Object.defineProperty(exports, "__esModule", {
-  value: true
-});
-
-var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; /* 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/>. */
-
-exports.getLibraryFromUrl = getLibraryFromUrl;
-exports.annotateFrames = annotateFrames;
-exports.simplifyDisplayName = simplifyDisplayName;
-exports.formatDisplayName = formatDisplayName;
-exports.formatCopyName = formatCopyName;
-exports.collapseFrames = collapseFrames;
-
-var _utils = __webpack_require__(1366);
-
-var _source = __webpack_require__(1356);
-
-var _lodash = __webpack_require__(2);
-
-function getFrameUrl(frame) {
-  return (0, _lodash.get)(frame, "source.url", "") || "";
-}
-
-const libraryMap = [{
-  label: "Backbone",
-  pattern: /backbone/i
-}, {
-  label: "jQuery",
-  pattern: /jquery/i
-}, {
-  label: "Preact",
-  pattern: /preact/i
-}, {
-  label: "React",
-  pattern: /react/i
-}, {
-  label: "Immutable",
-  pattern: /immutable/i
-}, {
-  label: "Webpack",
-  pattern: /webpack\/bootstrap/i
-}, {
-  label: "Node",
-  pattern: /(^internal\/|^[^.\/]+\.js)/
-}, {
-  label: "Express",
-  pattern: /node_modules\/express/
-}, {
-  label: "Pug",
-  pattern: /node_modules\/pug/
-}, {
-  label: "ExtJS",
-  pattern: /\/ext-all[\.\-]/
-}, {
-  label: "MobX",
-  pattern: /mobx/i
-}, {
-  label: "Underscore",
-  pattern: /underscore/i
-}, {
-  label: "Lodash",
-  pattern: /lodash/i
-}, {
-  label: "Ember",
-  pattern: /ember/i
-}, {
-  label: "Choo",
-  pattern: /choo/i
-}, {
-  label: "VueJS",
-  pattern: /vue\.js/i
-}, {
-  label: "RxJS",
-  pattern: /rxjs/i
-}, {
-  label: "Angular",
-  pattern: /angular/i
-}, {
-  label: "Redux",
-  pattern: /redux/i
-}, {
-  label: "Dojo",
-  pattern: /dojo/i
-}, {
-  label: "Marko",
-  pattern: /marko/i
-}, {
-  label: "NuxtJS",
-  pattern: /[\._]nuxt/i
-}, {
-  label: "Aframe",
-  pattern: /aframe/i
-}, {
-  label: "NextJS",
-  pattern: /[\._]next/i
-}];
-
-function getLibraryFromUrl(frame) {
-  // @TODO each of these fns calls getFrameUrl, just call it once
-  // (assuming there's not more complex logic to identify a lib)
-  const frameUrl = getFrameUrl(frame);
-  const match = (0, _lodash.find)(libraryMap, o => frameUrl.match(o.pattern));
-  return match && match.label;
-}
-
-const displayNameMap = {
-  Babel: {
-    tryCatch: "Async"
-  },
-  Backbone: {
-    "extend/child": "Create Class",
-    ".create": "Create Model"
-  },
-  jQuery: {
-    "jQuery.event.dispatch": "Dispatch Event"
-  },
-  React: {
-    // eslint-disable-next-line max-len
-    "ReactCompositeComponent._renderValidatedComponentWithoutOwnerOrContext/renderedElement<": "Render",
-    _renderValidatedComponentWithoutOwnerOrContext: "Render"
-  },
-  VueJS: {
-    "renderMixin/Vue.prototype._render": "Render"
-  },
-  Webpack: {
-    // eslint-disable-next-line camelcase
-    __webpack_require__: "Bootstrap"
-  }
-};
-
-function mapDisplayNames(frame, library) {
-  const { displayName } = frame;
-  return displayNameMap[library] && displayNameMap[library][displayName] || displayName;
-}
-
-function annotateFrames(frames) {
-  const annotatedFrames = frames.map(annotateFrame);
-  return annotateBabelAsyncFrames(annotatedFrames);
-}
-
-function annotateFrame(frame) {
-  const library = getLibraryFromUrl(frame);
-  if (library) {
-    return _extends({}, frame, { library });
-  }
-
-  return frame;
-}
-
-function annotateBabelAsyncFrames(frames) {
-  const babelFrameIndexes = getBabelFrameIndexes(frames);
-  const isBabelFrame = frameIndex => babelFrameIndexes.includes(frameIndex);
-
-  return frames.map((frame, frameIndex) => isBabelFrame(frameIndex) ? _extends({}, frame, { library: "Babel" }) : frame);
-}
-
-// Receives an array of frames and looks for babel async
-// call stack groups.
-function getBabelFrameIndexes(frames) {
-  const startIndexes = getFrameIndices(frames, (displayName, url) => url.match(/regenerator-runtime/i) && displayName === "tryCatch");
-
-  const endIndexes = getFrameIndices(frames, (displayName, url) => displayName === "_asyncToGenerator/<" || url.match(/_microtask/i) && displayName === "flush");
-
-  if (startIndexes.length != endIndexes.length || startIndexes.length === 0) {
-    return frames;
-  }
-
-  // Receives an array of start and end index tuples and returns
-  // an array of async call stack index ranges.
-  // e.g. [[1,3], [5,7]] => [[1,2,3], [5,6,7]]
-  return (0, _lodash.flatMap)((0, _lodash.zip)(startIndexes, endIndexes), ([startIndex, endIndex]) => (0, _lodash.range)(startIndex, endIndex + 1));
-}
-
-function getFrameIndices(frames, predicate) {
-  return frames.reduce((accumulator, frame, index) => predicate(frame.displayName, getFrameUrl(frame)) ? [...accumulator, index] : accumulator, []);
-}
-
-// Decodes an anonymous naming scheme that
-// spider monkey implements based on "Naming Anonymous JavaScript Functions"
-// http://johnjbarton.github.io/nonymous/index.html
-const objectProperty = /([\w\d]+)$/;
-const arrayProperty = /\[(.*?)\]$/;
-const functionProperty = /([\w\d]+)[\/\.<]*?$/;
-const annonymousProperty = /([\w\d]+)\(\^\)$/;
-
-function simplifyDisplayName(displayName) {
-  // if the display name has a space it has already been mapped
-  if (/\s/.exec(displayName)) {
-    return displayName;
-  }
-
-  const scenarios = [objectProperty, arrayProperty, functionProperty, annonymousProperty];
-
-  for (const reg of scenarios) {
-    const match = reg.exec(displayName);
-    if (match) {
-      return match[1];
-    }
-  }
-
-  return displayName;
-}
-
-function formatDisplayName(frame, { shouldMapDisplayName = true } = {}) {
-  let { displayName, library } = frame;
-  if (library && shouldMapDisplayName) {
-    displayName = mapDisplayNames(frame, library);
-  }
-
-  displayName = simplifyDisplayName(displayName);
-  return (0, _utils.endTruncateStr)(displayName, 25);
-}
-
-function formatCopyName(frame) {
-  const displayName = formatDisplayName(frame);
-  const fileName = (0, _source.getFilename)(frame.source);
-  const frameLocation = frame.location.line;
-
-  return `${displayName} (${fileName}#${frameLocation})`;
-}
-
-function collapseFrames(frames) {
-  // We collapse groups of one so that user frames
-  // are not in a group of one
-  function addGroupToList(group, list) {
-    if (!group) {
-      return list;
-    }
-
-    if (group.length > 1) {
-      list.push(group);
-    } else {
-      list = list.concat(group);
-    }
-
-    return list;
-  }
-  const { newFrames, lastGroup } = collapseLastFrames(frames);
-  frames = newFrames;
-  let items = [];
-  let currentGroup = null;
-  let prevItem = null;
-  for (const frame of frames) {
-    const prevLibrary = (0, _lodash.get)(prevItem, "library");
-
-    if (!currentGroup) {
-      currentGroup = [frame];
-    } else if (prevLibrary && prevLibrary == frame.library) {
-      currentGroup.push(frame);
-    } else {
-      items = addGroupToList(currentGroup, items);
-      currentGroup = [frame];
-    }
-
-    prevItem = frame;
-  }
-
-  items = addGroupToList(currentGroup, items);
-  items = addGroupToList(lastGroup, items);
-  return items;
-}
-
-function collapseLastFrames(frames) {
-  const index = (0, _lodash.findIndex)(frames, frame => getFrameUrl(frame).match(/webpack\/bootstrap/i));
-
-  if (index == -1) {
-    return { newFrames: frames, lastGroup: [] };
-  }
-
-  const newFrames = frames.slice(0, index);
-  const lastGroup = frames.slice(index);
-  return { newFrames, lastGroup };
-}
-
-/***/ }),
-
 /***/ 1381:
 /***/ (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
@@ -5056,16 +4775,17 @@ var _extends = Object.assign || function
 /**
  * Ast reducer
  * @module reducers/ast
  */
 
 exports.initialASTState = initialASTState;
 exports.getSymbols = getSymbols;
 exports.hasSymbols = hasSymbols;
+exports.isSymbolsLoading = isSymbolsLoading;
 exports.isEmptyLineInSource = isEmptyLineInSource;
 exports.getEmptyLines = getEmptyLines;
 exports.getOutOfScopeLocations = getOutOfScopeLocations;
 exports.getPreview = getPreview;
 exports.getSourceMetaData = getSourceMetaData;
 exports.getInScopeLines = getInScopeLines;
 exports.isLineInScope = isLineInScope;
 
@@ -5091,20 +4811,22 @@ function initialASTState() {
     sourceMetaData: I.Map()
   })();
 }
 
 function update(state = initialASTState(), action) {
   switch (action.type) {
     case "SET_SYMBOLS":
       {
-        const { source, symbols } = action;
-        return state.setIn(["symbols", source.id], symbols);
-      }
-
+        const { source } = action;
+        if (action.status === "start") {
+          return state.setIn(["symbols", source.id], { loading: true });
+        }
+        return state.setIn(["symbols", source.id], action.value);
+      }
     case "SET_EMPTY_LINES":
       {
         const { source, emptyLines } = action;
         return state.setIn(["emptyLines", source.id], emptyLines);
       }
 
     case "OUT_OF_SCOPE_LOCATIONS":
       {
@@ -5155,34 +4877,41 @@ function update(state = initialASTState(
       {
         return state;
       }
   }
 }
 
 // NOTE: we'd like to have the app state fully typed
 // https://github.com/devtools-html/debugger.html/blob/master/src/reducers/sources.js#L179-L185
-
-
-const emptySymbols = { variables: [], functions: [] };
 function getSymbols(state, source) {
   if (!source) {
-    return emptySymbols;
-  }
-
-  const symbols = state.ast.getIn(["symbols", source.id]);
-  return symbols || emptySymbols;
+    return null;
+  }
+
+  return state.ast.getIn(["symbols", source.id]) || null;
 }
 
 function hasSymbols(state, source) {
-  if (!source) {
-    return false;
-  }
-
-  return !!state.ast.getIn(["symbols", source.id]);
+  const symbols = getSymbols(state, source);
+
+  if (!symbols) {
+    return false;
+  }
+
+  return !symbols.loading;
+}
+
+function isSymbolsLoading(state, source) {
+  const symbols = getSymbols(state, source);
+  if (!symbols) {
+    return false;
+  }
+
+  return !!symbols.loading;
 }
 
 function isEmptyLineInSource(state, line, selectedSource) {
   const emptyLines = getEmptyLines(state, selectedSource);
   return emptyLines.includes(line);
 }
 
 function getEmptyLines(state, source) {
@@ -6603,16 +6332,22 @@ exports.setEmptyLines = setEmptyLines;
 exports.setOutOfScopeLocations = setOutOfScopeLocations;
 
 var _selectors = __webpack_require__(3590);
 
 var _setInScopeLines = __webpack_require__(1781);
 
 var _parser = __webpack_require__(1365);
 
+var _promise = __webpack_require__(1653);
+
+/* 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 setSourceMetaData(sourceId) {
   return async ({ dispatch, getState }) => {
     const sourceRecord = (0, _selectors.getSource)(getState(), sourceId);
     if (!sourceRecord) {
       return;
     }
 
     const source = sourceRecord.toJS();
@@ -6624,34 +6359,36 @@ function setSourceMetaData(sourceId) {
     dispatch({
       type: "SET_SOURCE_METADATA",
       sourceId: source.id,
       sourceMetaData: {
         framework
       }
     });
   };
-} /* 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 setSymbols(sourceId) {
   return async ({ dispatch, getState }) => {
     const sourceRecord = (0, _selectors.getSource)(getState(), sourceId);
     if (!sourceRecord) {
       return;
     }
 
     const source = sourceRecord.toJS();
     if (!source.text || source.isWasm || (0, _selectors.hasSymbols)(getState(), source)) {
       return;
     }
 
-    const symbols = await (0, _parser.getSymbols)(source.id);
-    dispatch({ type: "SET_SYMBOLS", source, symbols });
+    await dispatch({
+      type: "SET_SYMBOLS",
+      source,
+      [_promise.PROMISE]: (0, _parser.getSymbols)(source.id)
+    });
+
     dispatch(setEmptyLines(sourceId));
     dispatch(setSourceMetaData(sourceId));
   };
 }
 
 function setEmptyLines(sourceId) {
   return async ({ dispatch, getState }) => {
     const sourceRecord = (0, _selectors.getSource)(getState(), sourceId);
@@ -8027,17 +7764,17 @@ function findClosestScope(functions, loc
       return found;
     }
 
     return currNode;
   }, null);
 }
 
 function getASTLocation(source, symbols, location) {
-  if (source.isWasm) {
+  if (source.isWasm || !symbols || symbols.loading) {
     return { name: undefined, offset: location };
   }
 
   const functions = [...symbols.functions];
 
   const scope = findClosestScope(functions, location);
   if (scope) {
     // we only record the line, but at some point we may
@@ -9557,30 +9294,31 @@ async function loadSource(source, { sour
 }
 
 /**
  * @memberof actions/sources
  * @static
  */
 function loadSourceText(source) {
   return async ({ dispatch, getState, client, sourceMaps }) => {
-    const telemetryStart = performance.now();
-    const deferred = (0, _defer2.default)();
+    const id = source.get("id");
 
     // Fetch the source text only once.
+    if (requests.has(id)) {
+      return requests.get(id);
+    }
+
     if ((0, _source.isLoaded)(source)) {
-      return Promise.resolve(source);
-    }
-
-    const id = source.get("id");
-    if ((0, _source.isLoading)(source) || requests.has(id)) {
-      return requests.get(id);
-    }
-
+      return Promise.resolve();
+    }
+
+    const telemetryStart = performance.now();
+    const deferred = (0, _defer2.default)();
     requests.set(id, deferred.promise);
+
     try {
       await dispatch({
         type: "LOAD_SOURCE_TEXT",
         sourceId: id,
         [_promise.PROMISE]: loadSource(source, { sourceMaps, client })
       });
     } catch (e) {
       deferred.resolve();
@@ -10431,29 +10169,29 @@ Object.defineProperty(exports, "__esModu
 });
 
 var _react = __webpack_require__(0);
 
 var _react2 = _interopRequireDefault(_react);
 
 var _lodash = __webpack_require__(2);
 
-var _frame = __webpack_require__(1380);
+var _frames = __webpack_require__(3605);
 
 __webpack_require__(1320);
 
 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/>. */
 
 function getFunctionName(func) {
   const name = func.userDisplayName || func.displayName || func.name;
-  return (0, _frame.simplifyDisplayName)(name);
+  return (0, _frames.simplifyDisplayName)(name);
 }
 
 class PreviewFunction extends _react.Component {
   renderFunctionName(func) {
     const name = getFunctionName(func);
     return _react2.default.createElement(
       "span",
       { className: "function-name" },
@@ -11307,32 +11045,32 @@ var _react2 = _interopRequireDefault(_re
 var _classnames = __webpack_require__(175);
 
 var _classnames2 = _interopRequireDefault(_classnames);
 
 var _Svg = __webpack_require__(1359);
 
 var _Svg2 = _interopRequireDefault(_Svg);
 
-var _frame = __webpack_require__(1380);
+var _frames = __webpack_require__(3605);
 
 var _source = __webpack_require__(1356);
 
 var _FrameMenu = __webpack_require__(1454);
 
 var _FrameMenu2 = _interopRequireDefault(_FrameMenu);
 
 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/>. */
 
 function FrameTitle({ frame, options }) {
-  const displayName = (0, _frame.formatDisplayName)(frame, options);
+  const displayName = (0, _frames.formatDisplayName)(frame, options);
   return _react2.default.createElement(
     "div",
     { className: "title" },
     displayName
   );
 }
 
 function FrameLocation({ frame }) {
@@ -15522,27 +15260,29 @@ const { isDevelopment } = __webpack_requ
 
 const svg = {
   "angle-brackets": __webpack_require__(347),
   angular: __webpack_require__(247),
   arrow: __webpack_require__(348),
   babel: __webpack_require__(3595),
   backbone: __webpack_require__(997),
   blackBox: __webpack_require__(349),
+  breadcrumb: __webpack_require__(3603),
   breakpoint: __webpack_require__(350),
   "column-breakpoint": __webpack_require__(998),
   "case-match": __webpack_require__(351),
   choo: __webpack_require__(1290),
   close: __webpack_require__(352),
   coffeescript: __webpack_require__(2250),
   dojo: __webpack_require__(806),
   domain: __webpack_require__(353),
   file: __webpack_require__(354),
   folder: __webpack_require__(355),
   globe: __webpack_require__(356),
+  home: __webpack_require__(3604),
   javascript: __webpack_require__(2251),
   jquery: __webpack_require__(999),
   underscore: __webpack_require__(1117),
   lodash: __webpack_require__(1118),
   ember: __webpack_require__(1119),
   vuejs: __webpack_require__(1174),
   "magnifying-glass": __webpack_require__(357),
   "arrow-up": __webpack_require__(919),
@@ -16943,16 +16683,24 @@ class Outline extends _react.Component {
 
     return _react2.default.createElement(
       "div",
       { className: "outline-pane-info" },
       placeholderMessage
     );
   }
 
+  renderLoading() {
+    return _react2.default.createElement(
+      "div",
+      { className: "outline-pane-info" },
+      L10N.getStr("loadingText")
+    );
+  }
+
   renderFunction(func) {
     const { name, location, parameterNames } = func;
 
     return _react2.default.createElement(
       "li",
       {
         key: `${name}:${location.start.line}:${location.start.column}`,
         className: "outline-list__element",
@@ -17041,19 +16789,20 @@ class Outline extends _react.Component {
           L10N.getStr("outline.sortLabel")
         )
       )
     );
   }
 
   render() {
     const { symbols } = this.props;
-
+    if (!symbols || symbols.loading) {
+      return this.renderLoading();
+    }
     const symbolsToDisplay = symbols.functions.filter(func => func.name != "anonymous");
-
     return _react2.default.createElement(
       "div",
       { className: "outline" },
       symbolsToDisplay.length > 0 ? this.renderFunctions(symbols.functions) : this.renderPlaceholder()
     );
   }
 }
 
@@ -17190,42 +16939,68 @@ class SourcesTree extends _react.Compone
         projectRoot,
         uncollapsedTree,
         sourceTree
       }));
     }
   }
 
   render() {
-    const expanded = this.props.expanded;
+    const { expanded, projectRoot } = this.props;
     const {
       focusedItem,
       highlightItems,
       listItems,
       parentMap,
       sourceTree
     } = this.state;
 
     const onExpand = (item, expandedState) => {
       this.props.setExpandedState(expandedState);
     };
 
     const onCollapse = (item, expandedState) => {
       this.props.setExpandedState(expandedState);
     };
 
+    const isCustomRoot = projectRoot !== "";
+    let roots = () => sourceTree.contents;
+
+    let clearProjectRootButton = null;
+
+    // The "sourceTree.contents[0]" check ensures that there are contents
+    // A custom root with no existing sources will be ignored
+    if (isCustomRoot && sourceTree.contents[0]) {
+      clearProjectRootButton = _react2.default.createElement(
+        "button",
+        {
+          className: "sources-clear-root",
+          onClick: () => this.props.clearProjectDirectoryRoot(),
+          title: L10N.getStr("removeDirectoryRoot.label")
+        },
+        _react2.default.createElement(_Svg2.default, { name: "home" }),
+        _react2.default.createElement(_Svg2.default, { name: "breadcrumb", "class": true }),
+        _react2.default.createElement(
+          "span",
+          { className: "sources-clear-root-label" },
+          sourceTree.contents[0].name
+        )
+      );
+      roots = () => sourceTree.contents[0].contents;
+    }
+
     const isEmpty = sourceTree.contents.length === 0;
     const treeProps = {
       autoExpandAll: false,
       autoExpandDepth: expanded ? 0 : 1,
       expanded,
       getChildren: item => (0, _sourcesTree.nodeHasChildren)(item) ? item.contents : [],
       getParent: item => parentMap.get(item),
       getPath: this.getPath,
-      getRoots: () => sourceTree.contents,
+      getRoots: roots,
       highlightItems,
       itemHeight: 21,
       key: isEmpty ? "empty" : "full",
       listItems,
       onCollapse,
       onExpand,
       onFocus: this.focusItem,
       renderItem: this.renderItem
@@ -17244,18 +17019,31 @@ class SourcesTree extends _react.Compone
     const onKeyDown = e => {
       if (e.keyCode === 13 && focusedItem) {
         this.selectItem(focusedItem);
       }
     };
 
     return _react2.default.createElement(
       "div",
-      { className: "sources-list", onKeyDown: onKeyDown },
-      tree
+      {
+        className: (0, _classnames2.default)("sources-pane", {
+          "sources-list-custom-root": isCustomRoot
+        })
+      },
+      isCustomRoot ? _react2.default.createElement(
+        "div",
+        { className: "sources-clear-root-container" },
+        clearProjectRootButton
+      ) : null,
+      _react2.default.createElement(
+        "div",
+        { className: "sources-list", onKeyDown: onKeyDown },
+        tree
+      )
     );
   }
 }
 
 // Actions
 
 var _initialiseProps = function () {
   this.focusItem = item => {
@@ -17277,26 +17065,26 @@ var _initialiseProps = function () {
     if (typeof obj !== "undefined" && sources.has(obj) && sources.get(obj).get("isBlackBoxed")) {
       blackBoxedPart = "update";
     }
 
     return `${item.path}/${item.name}/${blackBoxedPart}`;
   };
 
   this.getIcon = (sources, item, depth) => {
-    const { debuggeeUrl } = this.props;
+    const { debuggeeUrl, projectRoot } = this.props;
 
     if (item.path === "/Webpack") {
       return _react2.default.createElement(_Svg2.default, { name: "webpack" });
     }
     if (item.path === "/Angular") {
       return _react2.default.createElement(_Svg2.default, { name: "angular" });
     }
 
-    if (depth === 0) {
+    if (depth === 0 && projectRoot === "") {
       return _react2.default.createElement("img", {
         className: (0, _classnames2.default)("domain", {
           debuggee: debuggeeUrl && debuggeeUrl.includes(item.name)
         })
       });
     }
 
     if (!(0, _sourcesTree.nodeHasChildren)(item)) {
@@ -17368,16 +17156,17 @@ var _initialiseProps = function () {
   };
 
   this.renderItem = (item, depth, focused, _, expanded, { setExpanded }) => {
     const arrow = (0, _sourcesTree.nodeHasChildren)(item) ? _react2.default.createElement("img", {
       className: (0, _classnames2.default)("arrow", {
         expanded: expanded
       })
     }) : _react2.default.createElement("i", { className: "no-arrow" });
+
     const { sources } = this.props;
     const icon = this.getIcon(sources, item, depth);
 
     return _react2.default.createElement(
       "div",
       {
         className: (0, _classnames2.default)("node", { focused }),
         key: item.path,
@@ -24045,17 +23834,17 @@ var _Group2 = _interopRequireDefault(_Gr
 var _WhyPaused = __webpack_require__(1604);
 
 var _WhyPaused2 = _interopRequireDefault(_WhyPaused);
 
 var _actions = __webpack_require__(1354);
 
 var _actions2 = _interopRequireDefault(_actions);
 
-var _frame = __webpack_require__(1380);
+var _frames = __webpack_require__(3605);
 
 var _clipboard = __webpack_require__(1388);
 
 var _selectors = __webpack_require__(3590);
 
 __webpack_require__(1338);
 
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
@@ -24072,17 +23861,17 @@ class Frames extends _react.Component {
     this.toggleFramesDisplay = () => {
       this.setState(prevState => ({
         showAllFrames: !prevState.showAllFrames
       }));
     };
 
     this.copyStackTrace = () => {
       const { frames } = this.props;
-      const framesToCopy = frames.map(f => (0, _frame.formatCopyName)(f)).join("\n");
+      const framesToCopy = frames.map(f => (0, _frames.formatCopyName)(f)).join("\n");
       (0, _clipboard.copyToTheClipboard)(framesToCopy);
     };
 
     this.toggleFrameworkGrouping = () => {
       const { toggleFrameworkGrouping, frameworkGroupingOn } = this.props;
       toggleFrameworkGrouping(!frameworkGroupingOn);
     };
 
@@ -24098,17 +23887,17 @@ class Frames extends _react.Component {
   }
 
   collapseFrames(frames) {
     const { frameworkGroupingOn } = this.props;
     if (!frameworkGroupingOn) {
       return frames;
     }
 
-    return (0, _frame.collapseFrames)(frames);
+    return (0, _frames.collapseFrames)(frames);
   }
 
   truncateFrames(frames) {
     const numFramesToShow = this.state.showAllFrames ? frames.length : NUM_FRAMES_SHOWN;
 
     return frames.slice(0, numFramesToShow);
   }
 
@@ -24219,17 +24008,17 @@ var _react2 = _interopRequireDefault(_re
 var _classnames = __webpack_require__(175);
 
 var _classnames2 = _interopRequireDefault(_classnames);
 
 var _Svg = __webpack_require__(1359);
 
 var _Svg2 = _interopRequireDefault(_Svg);
 
-var _frame = __webpack_require__(1380);
+var _frames = __webpack_require__(3605);
 
 var _FrameMenu = __webpack_require__(1454);
 
 var _FrameMenu2 = _interopRequireDefault(_FrameMenu);
 
 __webpack_require__(1336);
 
 var _Frame = __webpack_require__(1453);
@@ -24242,17 +24031,17 @@ var _Badge2 = _interopRequireDefault(_Ba
 
 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/>. */
 
 function FrameLocation({ frame }) {
-  const library = frame.library || (0, _frame.getLibraryFromUrl)(frame);
+  const library = frame.library || (0, _frames.getLibraryFromUrl)(frame);
   if (!library) {
     return null;
   }
 
   return _react2.default.createElement(
     "div",
     { className: "location" },
     library,
@@ -24317,17 +24106,17 @@ class Group extends _react.Component {
         toggleBlackBox: toggleBlackBox,
         toggleFrameworkGrouping: toggleFrameworkGrouping
       }))
     );
   }
 
   renderDescription() {
     const frame = this.props.group[0];
-    const displayName = (0, _frame.formatDisplayName)(frame);
+    const displayName = (0, _frames.formatDisplayName)(frame);
     return _react2.default.createElement(
       "li",
       {
         key: frame.id,
         className: (0, _classnames2.default)("group"),
         onClick: this.toggleFrames,
         tabIndex: 0
       },
@@ -27054,17 +26843,23 @@ function reverseStepOut() {
  * not at an async expression.
  */
 function hasAwait(source, pauseLocation) {
   const { line, column } = pauseLocation;
   if (!source.text) {
     return false;
   }
 
-  const snippet = source.text.split("\n")[line - 1].slice(column - 50, column + 50);
+  const lineText = source.text.split("\n")[line - 1];
+
+  if (!lineText) {
+    return false;
+  }
+
+  const snippet = lineText.slice(column - 50, column + 50);
 
   return !!snippet.match(/(yield|await)/);
 }
 
 /**
  * @memberOf actions/pause
  * @static
  * @param stepType
@@ -27074,17 +26869,17 @@ function astCommand(stepType) {
   return async ({ dispatch, getState, sourceMaps }) => {
     if (!_prefs.features.asyncStepping) {
       return dispatch(command(stepType));
     }
 
     if (stepType == "stepOver") {
       // This type definition is ambiguous:
       const frame = (0, _selectors.getTopFrame)(getState());
-      const source = (0, _selectors.getSelectedSource)(getState()).toJS();
+      const source = (0, _selectors.getSource)(getState(), frame.location.sourceId);
 
       if (source && hasAwait(source, frame.location)) {
         const nextLocation = await (0, _parser.getNextStep)(source.id, frame.location);
         if (nextLocation) {
           await dispatch((0, _breakpoints.addHiddenBreakpoint)(nextLocation));
           return dispatch(command("resume"));
         }
       }
@@ -27105,17 +26900,17 @@ function astCommand(stepType) {
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
 exports.findBestMatchExpression = findBestMatchExpression;
 /* 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 findBestMatchExpression(symbols, tokenPos, token) {
+function findBestMatchExpression(symbols, tokenPos) {
   const { memberExpressions, identifiers } = symbols;
   const { line, column } = tokenPos;
   return identifiers.concat(memberExpressions).reduce((found, expression) => {
     const overlaps = expression.location.start.line == line && expression.location.start.column <= column && expression.location.end.column >= column && !expression.computed;
 
     if (overlaps) {
       return expression;
     }
@@ -28076,18 +27871,23 @@ class QuickOpenModal extends _react.Comp
 
     this.isShortcutQuery = () => this.props.searchType === "shortcuts";
 
     this.isSourcesQuery = () => this.props.searchType === "sources";
 
     this.isSourceSearch = () => this.isSourcesQuery() || this.isGotoSourceQuery();
 
     this.renderHighlight = (candidateString, query, name) => {
-      const html = _fuzzaldrinPlus2.default.wrap(candidateString, query);
-
+      const options = {
+        wrap: {
+          tagOpen: '<mark class="highlight">',
+          tagClose: "</mark>"
+        }
+      };
+      const html = _fuzzaldrinPlus2.default.wrap(candidateString, query, options);
       return _react2.default.createElement("div", { dangerouslySetInnerHTML: { __html: html } });
     };
 
     this.highlightMatching = (query, results) => {
       let newQuery = query;
       if (newQuery === "") {
         return results;
       }
@@ -30645,17 +30445,17 @@ var _extends = Object.assign || function
                                                                                                                                                                                                                                                                    * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 exports.formatCallStackFrames = formatCallStackFrames;
 
 var _sources = __webpack_require__(1369);
 
 var _pause = __webpack_require__(1394);
 
-var _frame = __webpack_require__(1380);
+var _frames = __webpack_require__(3605);
 
 var _devtoolsSourceMap = __webpack_require__(1360);
 
 var _lodash = __webpack_require__(2);
 
 var _reselect = __webpack_require__(993);
 
 function getLocation(frame, isGeneratedSource) {
@@ -30677,17 +30477,17 @@ function appendSource(sources, frame, se
 
 function formatCallStackFrames(frames, sources, selectedSource) {
   if (!frames) {
     return null;
   }
 
   const formattedFrames = frames.filter(frame => getSourceForFrame(sources, frame)).map(frame => appendSource(sources, frame, selectedSource)).filter(frame => !(0, _lodash.get)(frame, "source.isBlackBoxed"));
 
-  return (0, _frame.annotateFrames)(formattedFrames);
+  return (0, _frames.annotateFrames)(formattedFrames);
 }
 
 const getCallStackFrames = exports.getCallStackFrames = (0, _reselect.createSelector)(_pause.getFrames, _sources.getSources, _sources.getSelectedSource, formatCallStackFrames);
 
 /***/ }),
 
 /***/ 178:
 /***/ (function(module, exports, __webpack_require__) {
@@ -30802,26 +30602,26 @@ function getOutOfScopeLines(outOfScopeLo
 
   return (0, _lodash.uniq)((0, _lodash.flatMap)(outOfScopeLocations, location => (0, _lodash.range)(location.start.line, location.end.line)));
 } /* 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 setInScopeLines() {
   return ({ dispatch, getState }) => {
-    const source = (0, _selectors.getSelectedSource)(getState());
+    const sourceRecord = (0, _selectors.getSelectedSource)(getState());
     const outOfScopeLocations = (0, _selectors.getOutOfScopeLocations)(getState());
 
-    if (!source || !source.get("text")) {
-      return;
-    }
-
-    const linesOutOfScope = getOutOfScopeLines(outOfScopeLocations, source.toJS());
-
-    const sourceNumLines = (0, _source.getSourceLineCount)(source.toJS());
+    if (!sourceRecord || !sourceRecord.text) {
+      return;
+    }
+
+    const linesOutOfScope = getOutOfScopeLines(outOfScopeLocations);
+
+    const sourceNumLines = (0, _source.getSourceLineCount)(sourceRecord);
     const sourceLines = (0, _lodash.range)(1, sourceNumLines + 1);
 
     const inScopeLines = !linesOutOfScope ? sourceLines : (0, _lodash.without)(sourceLines, ...linesOutOfScope);
 
     dispatch({
       type: "IN_SCOPE_LINES",
       lines: inScopeLines
     });
@@ -30941,17 +30741,16 @@ function isInvalidTarget(target) {
   // exclude codemirror elements that are not tokens
   const invalidTarget = target.parentElement && !target.parentElement.closest(".CodeMirror-line") || cursorPos.top == 0;
 
   return invalidTarget || invalidToken || invaildType;
 }
 
 function updatePreview(target, editor) {
   return ({ dispatch, getState, client, sourceMaps }) => {
-    const tokenText = target.innerText ? target.innerText.trim() : "";
     const tokenPos = (0, _editor.getTokenLocation)(editor.codeMirror, target);
     const cursorPos = target.getBoundingClientRect();
     const preview = (0, _selectors.getPreview)(getState());
 
     if ((0, _selectors.getCanRewind)(getState())) {
       return;
     }
 
@@ -30975,20 +30774,20 @@ function updatePreview(target, editor) {
     if (!(0, _selectors.isLineInScope)(getState(), tokenPos.line)) {
       return;
     }
 
     const source = (0, _selectors.getSelectedSource)(getState());
     const symbols = (0, _selectors.getSymbols)(getState(), source.toJS());
 
     let match;
-    if (!symbols || symbols.identifiers.length > 0) {
-      match = (0, _ast.findBestMatchExpression)(symbols, tokenPos, tokenText);
-    } else {
+    if (!symbols || symbols.loading) {
       match = (0, _getExpression.getExpressionFromCoords)(editor.codeMirror, tokenPos);
+    } else {
+      match = (0, _ast.findBestMatchExpression)(symbols, tokenPos);
     }
 
     if (!match || !match.expression) {
       return;
     }
 
     const { expression, location } = match;
     dispatch(setPreview(expression, location, tokenPos, cursorPos));
@@ -31285,25 +31084,25 @@ var _extends = Object.assign || function
                                                                                                                                                                                                                                                                    * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 exports.getScope = getScope;
 
 var _getVariables = __webpack_require__(1765);
 
 var _utils = __webpack_require__(1766);
 
-var _frame = __webpack_require__(1380);
+var _frames = __webpack_require__(3605);
 
 function getScopeTitle(type, scope) {
   if (type === "block" && scope.block && scope.block.displayName) {
     return scope.block.displayName;
   }
 
   if (type === "function" && scope.function) {
-    return scope.function.displayName ? (0, _frame.simplifyDisplayName)(scope.function.displayName) : L10N.getStr("anonymous");
+    return scope.function.displayName ? (0, _frames.simplifyDisplayName)(scope.function.displayName) : L10N.getStr("anonymous");
   }
   return L10N.getStr("scopes.block");
 }
 
 function getScope(scope, selectedFrame, frameScopes, why, scopeIndex) {
   const { type, actor } = scope;
 
   const isLocalScope = scope.actor === frameScopes.actor;
@@ -31884,21 +31683,23 @@ var _extends = Object.assign || function
                                                                                                                                                                                                                                                                    * License, v. 2.0. If a copy of the MPL was not distributed with this
                                                                                                                                                                                                                                                                    * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 /**
  * Redux actions for the sources state
  * @module actions/sources
  */
 
-
-exports.loadSourceMap = loadSourceMap;
 exports.newSource = newSource;
 exports.newSources = newSources;
 
+var _devtoolsSourceMap = __webpack_require__(1360);
+
+var _lodash = __webpack_require__(2);
+
 var _blackbox = __webpack_require__(1802);
 
 var _breakpoints = __webpack_require__(1396);
 
 var _loadSourceText = __webpack_require__(1435);
 
 var _prettyPrint = __webpack_require__(1798);
 
@@ -31914,45 +31715,53 @@ function createOriginalSource(originalUr
     id: sourceMaps.generatedToOriginalId(generatedSource.id, originalUrl),
     isPrettyPrinted: false,
     isWasm: false,
     isBlackBoxed: false,
     loadedState: "unloaded"
   };
 }
 
+function loadSourceMaps(sources) {
+  return async function ({ dispatch, getState, sourceMaps }) {
+    const originalSources = await Promise.all(sources.map(source => dispatch(loadSourceMap(source.id))));
+
+    await dispatch(newSources((0, _lodash.flatten)(originalSources)));
+  };
+}
+
 /**
  * @memberof actions/sources
  * @static
  */
-function loadSourceMap(generatedSource) {
+function loadSourceMap(sourceId) {
   return async function ({ dispatch, getState, sourceMaps }) {
-    let urls;
+    const source = (0, _selectors.getSource)(getState(), sourceId).toJS();
+
+    if (!(0, _devtoolsSourceMap.isGeneratedId)(sourceId) || !source.sourceMapURL) {
+      return;
+    }
+
+    let urls = null;
     try {
-      urls = await sourceMaps.getOriginalURLs(generatedSource);
+      urls = await sourceMaps.getOriginalURLs(source);
     } catch (e) {
       console.error(e);
-      urls = null;
-    }
+    }
+
     if (!urls) {
       // If this source doesn't have a sourcemap, enable it for pretty printing
       dispatch({
         type: "UPDATE_SOURCE",
-        source: _extends({}, generatedSource, { sourceMapURL: "" })
-      });
-      return;
-    }
-
-    const originalSources = urls.map(url => createOriginalSource(url, generatedSource, sourceMaps));
-
-    // TODO: check if this line is really needed, it introduces
-    // a lot of lag to the application.
-    const generatedSourceRecord = (0, _selectors.getSource)(getState(), generatedSource.id);
-    await dispatch((0, _loadSourceText.loadSourceText)(generatedSourceRecord));
-    dispatch(newSources(originalSources));
+        source: _extends({}, source, { sourceMapURL: "" })
+      });
+      return;
+    }
+
+    return urls.map(url => createOriginalSource(url, source, sourceMaps));
   };
 }
 
 // If a request has been made to show this source, go ahead and
 // select it.
 function checkSelectedSource(sourceId) {
   return async ({ dispatch, getState }) => {
     const source = (0, _selectors.getSource)(getState(), sourceId).toJS();
@@ -32019,36 +31828,37 @@ function restoreBlackBoxedSources(source
 function newSource(source) {
   return async ({ dispatch }) => {
     await dispatch(newSources([source]));
   };
 }
 
 function newSources(sources) {
   return async ({ dispatch, getState }) => {
-    const filteredSources = sources.filter(source => !(0, _selectors.getSource)(getState(), source.id));
+    const filteredSources = sources.filter(source => source && !(0, _selectors.getSource)(getState(), source.id));
 
     if (filteredSources.length == 0) {
       return;
     }
 
     dispatch({
       type: "ADD_SOURCES",
       sources: filteredSources
     });
 
     for (const source of filteredSources) {
       dispatch(checkSelectedSource(source.id));
       dispatch(checkPendingBreakpoints(source.id));
     }
 
-    await Promise.all(filteredSources.map(source => dispatch(loadSourceMap(source))));
+    await dispatch(loadSourceMaps(filteredSources));
+
     // We would like to restore the blackboxed state
     // after loading all states to make sure the correctness.
-    dispatch(restoreBlackBoxedSources(filteredSources));
+    await dispatch(restoreBlackBoxedSources(filteredSources));
   };
 }
 
 /***/ }),
 
 /***/ 1802:
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -34679,43 +34489,48 @@ if (prefs.debuggerPrefsSchemaVersion !==
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
 
-var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
+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/>. */
 
 exports.initialState = initialState;
 exports.getHistory = getHistory;
 exports.getHistoryFrame = getHistoryFrame;
 exports.getHistoryPosition = getHistoryPosition;
+
+var _prefs = __webpack_require__(226);
+
+/**
+ * Breakpoints reducer
+ * @module reducers/replay
+ */
 function initialState() {
   return {
     history: [],
     position: -1
   };
-} /* 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/>. */
-
-/**
- * Breakpoints reducer
- * @module reducers/replay
- */
-
+}
 
 const defaultFrameScopes = {
   original: {},
   generated: {}
 };
 
 function update(state = initialState(), action) {
+  if (!_prefs.features.replay) {
+    return state;
+  }
+
   switch (action.type) {
     case "TRAVEL_TO":
       {
         return _extends({}, state, { position: action.position });
       }
 
     case "ADD_SCOPES":
       {
@@ -34745,16 +34560,21 @@ function update(state = initialState(), 
 
   return state;
 }
 
 function addScopes(state, action) {
   const { frame, status, value } = action;
   const selectedFrameId = frame.id;
   const instance = state.history[state.position];
+
+  if (!instance) {
+    return state;
+  }
+
   const pausedInst = instance.paused;
 
   const generated = _extends({}, pausedInst.frameScopes.generated, {
     [selectedFrameId]: {
       pending: status !== "done",
       scope: value
     }
   });
@@ -34769,16 +34589,21 @@ function addScopes(state, action) {
   history[state.position] = _extends({}, instance, { paused: newPaused });
   return _extends({}, state, { history });
 }
 
 function mapScopes(state, action) {
   const { frame, status, value } = action;
   const selectedFrameId = frame.id;
   const instance = state.history[state.position];
+
+  if (!instance) {
+    return state;
+  }
+
   const pausedInst = instance.paused;
 
   const original = _extends({}, pausedInst.frameScopes.original, {
     [selectedFrameId]: {
       pending: status !== "done",
       scope: value
     }
   });
@@ -34795,16 +34620,17 @@ function mapScopes(state, action) {
 }
 
 function evaluateExpression(state, action) {
   const { input, value } = action;
   const instance = state.history[state.position];
   if (!instance) {
     return state;
   }
+
   const prevExpressions = instance.expressions || [];
   const expression = { input, value };
   const expressions = [...prevExpressions, expression];
 
   const history = [...state.history];
   history[state.position] = _extends({}, instance, { expressions });
   return _extends({}, state, { history });
 }
@@ -37574,23 +37400,472 @@ module.exports = "<!-- This Source Code 
 
 /***/ 360:
 /***/ (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\"><path d=\"M8.5 8.5V14a.5.5 0 1 1-1 0V8.5H2a.5.5 0 0 1 0-1h5.5V2a.5.5 0 0 1 1 0v5.5H14a.5.5 0 1 1 0 1H8.5z\" fill-rule=\"evenodd\"></path></svg>"
 
 /***/ }),
 
+/***/ 3603:
+/***/ (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\"><defs><linearGradient id=\"b\"><stop offset=\"0\" stop-color=\"#9a9aba\"></stop><stop offset=\"1\" stop-color=\"#a6a6c2\"></stop></linearGradient><linearGradient id=\"a\"><stop offset=\"0\" stop-color=\"#8e8eb2\"></stop><stop offset=\"1\" stop-color=\"#9a9aba\"></stop></linearGradient><linearGradient x1=\"3.616\" y1=\"3.893\" x2=\"1.285\" y2=\"-.757\" id=\"d\" xlink:href=\"#a\" gradientUnits=\"userSpaceOnUse\" gradientTransform=\"matrix(1 0 0 .8684 0 1046.257)\"></linearGradient><linearGradient x1=\"2.232\" y1=\"4.162\" x2=\".629\" y2=\".966\" id=\"c\" xlink:href=\"#b\" gradientUnits=\"userSpaceOnUse\" gradientTransform=\"matrix(1 0 0 .8684 0 1046.257)\"></linearGradient></defs><path d=\"M.2 1045.562l4.6 3.3-4.6 3.3 2-3.3z\" fill=\"url(#c)\" stroke=\"url(#d)\" stroke-width=\".4\" stroke-linejoin=\"round\" transform=\"translate(0 -1045.362)\"></path></svg>"
+
+/***/ }),
+
+/***/ 3604:
+/***/ (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\" viewBox=\"0 0 16 16\"><path d=\"M15.7 7.3l-7-7c-.4-.4-1-.4-1.4 0l-7 7c-.4.4-.4 1 0 1.4.4.4 1 .4 1.4 0l.3-.3V13c0 1.7 1.3 3 3 3h6c1.7 0 3-1.3 3-3V8.4l.3.3c.2.2.4.3.7.3.3 0 .5-.1.7-.3.4-.4.4-1 0-1.4zM8 11.5c0-.3.2-.5.5-.5s.5.2.5.5-.2.5-.5.5-.5-.2-.5-.5zm4 1.5c0 .6-.4 1-1 1h-1V9c0-.6-.4-1-1-1H7c-.6 0-1 .4-1 1v5H5c-.6 0-1-.4-1-1V6.4l4-4 4 4V13z\"></path></svg>"
+
+/***/ }),
+
+/***/ 3605:
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+
+var _annotateFrames = __webpack_require__(3608);
+
+Object.keys(_annotateFrames).forEach(function (key) {
+  if (key === "default" || key === "__esModule") return;
+  Object.defineProperty(exports, key, {
+    enumerable: true,
+    get: function () {
+      return _annotateFrames[key];
+    }
+  });
+});
+
+var _collapseFrames = __webpack_require__(3609);
+
+Object.keys(_collapseFrames).forEach(function (key) {
+  if (key === "default" || key === "__esModule") return;
+  Object.defineProperty(exports, key, {
+    enumerable: true,
+    get: function () {
+      return _collapseFrames[key];
+    }
+  });
+});
+
+var _displayName = __webpack_require__(3610);
+
+Object.keys(_displayName).forEach(function (key) {
+  if (key === "default" || key === "__esModule") return;
+  Object.defineProperty(exports, key, {
+    enumerable: true,
+    get: function () {
+      return _displayName[key];
+    }
+  });
+});
+
+var _getFrameUrl = __webpack_require__(3606);
+
+Object.keys(_getFrameUrl).forEach(function (key) {
+  if (key === "default" || key === "__esModule") return;
+  Object.defineProperty(exports, key, {
+    enumerable: true,
+    get: function () {
+      return _getFrameUrl[key];
+    }
+  });
+});
+
+var _getLibraryFromUrl = __webpack_require__(3607);
+
+Object.keys(_getLibraryFromUrl).forEach(function (key) {
+  if (key === "default" || key === "__esModule") return;
+  Object.defineProperty(exports, key, {
+    enumerable: true,
+    get: function () {
+      return _getLibraryFromUrl[key];
+    }
+  });
+});
+
+/***/ }),
+
+/***/ 3606:
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.getFrameUrl = getFrameUrl;
+
+var _lodash = __webpack_require__(2);
+
+function getFrameUrl(frame) {
+  return (0, _lodash.get)(frame, "source.url", "") || "";
+} /* 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/>. */
+
+/***/ }),
+
+/***/ 3607:
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.getLibraryFromUrl = getLibraryFromUrl;
+
+var _lodash = __webpack_require__(2);
+
+var _getFrameUrl = __webpack_require__(3606);
+
+/* 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 libraryMap = [{
+  label: "Backbone",
+  pattern: /backbone/i
+}, {
+  label: "jQuery",
+  pattern: /jquery/i
+}, {
+  label: "Preact",
+  pattern: /preact/i
+}, {
+  label: "React",
+  pattern: /react/i
+}, {
+  label: "Immutable",
+  pattern: /immutable/i
+}, {
+  label: "Webpack",
+  pattern: /webpack\/bootstrap/i
+}, {
+  label: "Node",
+  pattern: /(^internal\/|^[^.\/]+\.js)/
+}, {
+  label: "Express",
+  pattern: /node_modules\/express/
+}, {
+  label: "Pug",
+  pattern: /node_modules\/pug/
+}, {
+  label: "ExtJS",
+  pattern: /\/ext-all[\.\-]/
+}, {
+  label: "MobX",
+  pattern: /mobx/i
+}, {
+  label: "Underscore",
+  pattern: /underscore/i
+}, {
+  label: "Lodash",
+  pattern: /lodash/i
+}, {
+  label: "Ember",
+  pattern: /ember/i
+}, {
+  label: "Choo",
+  pattern: /choo/i
+}, {
+  label: "VueJS",
+  pattern: /vue\.js/i
+}, {
+  label: "RxJS",
+  pattern: /rxjs/i
+}, {
+  label: "Angular",
+  pattern: /angular/i
+}, {
+  label: "Redux",
+  pattern: /redux/i
+}, {
+  label: "Dojo",
+  pattern: /dojo/i
+}, {
+  label: "Marko",
+  pattern: /marko/i
+}, {
+  label: "NuxtJS",
+  pattern: /[\._]nuxt/i
+}, {
+  label: "Aframe",
+  pattern: /aframe/i
+}, {
+  label: "NextJS",
+  pattern: /[\._]next/i
+}];
+
+function getLibraryFromUrl(frame) {
+  // @TODO each of these fns calls getFrameUrl, just call it once
+  // (assuming there's not more complex logic to identify a lib)
+  const frameUrl = (0, _getFrameUrl.getFrameUrl)(frame);
+  const match = (0, _lodash.find)(libraryMap, o => frameUrl.match(o.pattern));
+  return match && match.label;
+}
+
+/***/ }),
+
+/***/ 3608:
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+
+var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; /* 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/>. */
+
+exports.annotateFrames = annotateFrames;
+
+var _lodash = __webpack_require__(2);
+
+var _getFrameUrl = __webpack_require__(3606);
+
+var _getLibraryFromUrl = __webpack_require__(3607);
+
+function annotateFrames(frames) {
+  const annotatedFrames = frames.map(annotateFrame);
+  return annotateBabelAsyncFrames(annotatedFrames);
+}
+
+function annotateFrame(frame) {
+  const library = (0, _getLibraryFromUrl.getLibraryFromUrl)(frame);
+  if (library) {
+    return _extends({}, frame, { library });
+  }
+
+  return frame;
+}
+
+function annotateBabelAsyncFrames(frames) {
+  const babelFrameIndexes = getBabelFrameIndexes(frames);
+  const isBabelFrame = frameIndex => babelFrameIndexes.includes(frameIndex);
+
+  return frames.map((frame, frameIndex) => isBabelFrame(frameIndex) ? _extends({}, frame, { library: "Babel" }) : frame);
+}
+
+// Receives an array of frames and looks for babel async
+// call stack groups.
+function getBabelFrameIndexes(frames) {
+  const startIndexes = getFrameIndices(frames, (displayName, url) => url.match(/regenerator-runtime/i) && displayName === "tryCatch");
+
+  const endIndexes = getFrameIndices(frames, (displayName, url) => displayName === "_asyncToGenerator/<" || url.match(/_microtask/i) && displayName === "flush");
+
+  if (startIndexes.length != endIndexes.length || startIndexes.length === 0) {
+    return frames;
+  }
+
+  // Receives an array of start and end index tuples and returns
+  // an array of async call stack index ranges.
+  // e.g. [[1,3], [5,7]] => [[1,2,3], [5,6,7]]
+  return (0, _lodash.flatMap)((0, _lodash.zip)(startIndexes, endIndexes), ([startIndex, endIndex]) => (0, _lodash.range)(startIndex, endIndex + 1));
+}
+
+function getFrameIndices(frames, predicate) {
+  return frames.reduce((accumulator, frame, index) => predicate(frame.displayName, (0, _getFrameUrl.getFrameUrl)(frame)) ? [...accumulator, index] : accumulator, []);
+}
+
+/***/ }),
+
+/***/ 3609:
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.collapseFrames = collapseFrames;
+
+var _lodash = __webpack_require__(2);
+
+var _getFrameUrl = __webpack_require__(3606);
+
+/* 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 collapseLastFrames(frames) {
+  const index = (0, _lodash.findIndex)(frames, frame => (0, _getFrameUrl.getFrameUrl)(frame).match(/webpack\/bootstrap/i));
+
+  if (index == -1) {
+    return { newFrames: frames, lastGroup: [] };
+  }
+
+  const newFrames = frames.slice(0, index);
+  const lastGroup = frames.slice(index);
+  return { newFrames, lastGroup };
+}
+
+// eslint-disable-next-line max-len
+function collapseFrames(frames) {
+  // We collapse groups of one so that user frames
+  // are not in a group of one
+  function addGroupToList(group, list) {
+    if (!group) {
+      return list;
+    }
+
+    if (group.length > 1) {
+      list.push(group);
+    } else {
+      list = list.concat(group);
+    }
+
+    return list;
+  }
+  const { newFrames, lastGroup } = collapseLastFrames(frames);
+  frames = newFrames;
+  let items = [];
+  let currentGroup = null;
+  let prevItem = null;
+  for (const frame of frames) {
+    const prevLibrary = (0, _lodash.get)(prevItem, "library");
+
+    if (!currentGroup) {
+      currentGroup = [frame];
+    } else if (prevLibrary && prevLibrary == frame.library) {
+      currentGroup.push(frame);
+    } else {
+      items = addGroupToList(currentGroup, items);
+      currentGroup = [frame];
+    }
+
+    prevItem = frame;
+  }
+
+  items = addGroupToList(currentGroup, items);
+  items = addGroupToList(lastGroup, items);
+  return items;
+}
+
+/***/ }),
+
 /***/ 361:
 /***/ (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\"><path d=\"M4.525 13.21h-.472c-.574 0-.987-.154-1.24-.463-.253-.31-.38-.882-.38-1.719v-.573c0-.746-.097-1.265-.292-1.557-.196-.293-.51-.44-.945-.44v-.974c.435 0 .75-.146.945-.44.195-.292.293-.811.293-1.556v-.58c0-.833.126-1.404.379-1.712.253-.31.666-.464 1.24-.464h.472v.783h-.179c-.37 0-.628.08-.774.24-.145.159-.218.54-.218 1.141v.383c0 .824-.096 1.432-.287 1.823-.191.39-.516.679-.974.866.458.191.783.482.974.873.191.39.287.998.287 1.823v.382c0 .602.073.982.218 1.142.146.16.404.239.774.239h.18v.783zm9.502-4.752c-.43 0-.744.147-.942.44-.197.292-.296.811-.296 1.557v.573c0 .837-.125 1.41-.376 1.719-.251.309-.664.463-1.237.463h-.478v-.783h.185c.37 0 .628-.08.774-.24.145-.159.218-.539.218-1.14v-.383c0-.825.096-1.433.287-1.823.191-.39.516-.682.974-.873-.458-.187-.783-.476-.974-.866-.191-.391-.287-.999-.287-1.823v-.383c0-.602-.073-.982-.218-1.142-.146-.159-.404-.239-.774-.239h-.185v-.783h.478c.573 0 .986.155 1.237.464.25.308.376.88.376 1.712v.58c0 .673.088 1.174.263 1.503.176.329.5.493.975.493v.974z\" fill-rule=\"evenodd\"></path></svg>"
 
 /***/ }),
 
+/***/ 3610:
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.simplifyDisplayName = simplifyDisplayName;
+exports.formatDisplayName = formatDisplayName;
+exports.formatCopyName = formatCopyName;
+
+var _source = __webpack_require__(1356);
+
+var _utils = __webpack_require__(1366);
+
+// Decodes an anonymous naming scheme that
+// spider monkey implements based on "Naming Anonymous JavaScript Functions"
+// http://johnjbarton.github.io/nonymous/index.html
+const objectProperty = /([\w\d]+)$/; /* 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/>. */
+
+// eslint-disable-next-line max-len
+
+const arrayProperty = /\[(.*?)\]$/;
+const functionProperty = /([\w\d]+)[\/\.<]*?$/;
+const annonymousProperty = /([\w\d]+)\(\^\)$/;
+
+function simplifyDisplayName(displayName) {
+  // if the display name has a space it has already been mapped
+  if (/\s/.exec(displayName)) {
+    return displayName;
+  }
+
+  const scenarios = [objectProperty, arrayProperty, functionProperty, annonymousProperty];
+
+  for (const reg of scenarios) {
+    const match = reg.exec(displayName);
+    if (match) {
+      return match[1];
+    }
+  }
+
+  return displayName;
+}
+
+const displayNameMap = {
+  Babel: {
+    tryCatch: "Async"
+  },
+  Backbone: {
+    "extend/child": "Create Class",
+    ".create": "Create Model"
+  },
+  jQuery: {
+    "jQuery.event.dispatch": "Dispatch Event"
+  },
+  React: {
+    // eslint-disable-next-line max-len
+    "ReactCompositeComponent._renderValidatedComponentWithoutOwnerOrContext/renderedElement<": "Render",
+    _renderValidatedComponentWithoutOwnerOrContext: "Render"
+  },
+  VueJS: {
+    "renderMixin/Vue.prototype._render": "Render"
+  },
+  Webpack: {
+    // eslint-disable-next-line camelcase
+    __webpack_require__: "Bootstrap"
+  }
+};
+
+function mapDisplayNames(frame, library) {
+  const { displayName } = frame;
+  return displayNameMap[library] && displayNameMap[library][displayName] || displayName;
+}
+
+function formatDisplayName(frame, { shouldMapDisplayName = true } = {}) {
+  let { displayName, library } = frame;
+  if (library && shouldMapDisplayName) {
+    displayName = mapDisplayNames(frame, library);
+  }
+
+  displayName = simplifyDisplayName(displayName);
+  return (0, _utils.endTruncateStr)(displayName, 25);
+}
+
+function formatCopyName(frame) {
+  const displayName = formatDisplayName(frame);
+  const fileName = (0, _source.getFilename)(frame.source);
+  const frameLocation = frame.location.line;
+
+  return `${displayName} (${fileName}#${frameLocation})`;
+}
+
+/***/ }),
+
 /***/ 362:
 /***/ (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 20 16\" stroke=\"none\" fillrule=\"evenodd\"><rect x=\"3\" y=\"10\" width=\"3\" height=\"3\" rx=\"1\"></rect><rect x=\"12\" y=\"3\" width=\"2\" height=\"9\" rx=\"1\"></rect><rect transform=\"translate(13.000000, 7.500000) rotate(60.000000) translate(-13.000000, -7.500000) \" x=\"12\" y=\"3\" width=\"2\" height=\"9\" rx=\"1\"></rect><rect transform=\"translate(13.000000, 7.500000) rotate(-60.000000) translate(-13.000000, -7.500000) \" x=\"12\" y=\"3\" width=\"2\" height=\"9\" rx=\"1\"></rect></svg>"
 
 /***/ }),
 
 /***/ 363:
@@ -39642,9 +39917,9 @@ module.exports = "<!-- This Source Code 
 /***/ 999:
 /***/ (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 version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" viewBox=\"0 0 32 32\"><path fill=\"#444444\" d=\"M16.232 24.047c-0.15-0.034-0.295-0.081-0.441-0.124-0.037-0.011-0.074-0.022-0.11-0.033-0.143-0.044-0.284-0.090-0.425-0.139-0.019-0.007-0.039-0.014-0.058-0.021-0.126-0.045-0.251-0.091-0.375-0.139-0.035-0.014-0.070-0.027-0.105-0.041-0.136-0.054-0.271-0.11-0.405-0.168-0.027-0.012-0.054-0.024-0.081-0.036-0.115-0.052-0.228-0.105-0.341-0.159-0.033-0.016-0.065-0.031-0.099-0.047-0.089-0.043-0.177-0.090-0.264-0.134-0.059-0.031-0.118-0.060-0.176-0.092-0.107-0.058-0.212-0.117-0.317-0.178-0.035-0.020-0.071-0.038-0.107-0.059-0.139-0.081-0.277-0.166-0.412-0.252-0.037-0.024-0.074-0.050-0.111-0.074-0.099-0.063-0.197-0.128-0.293-0.195-0.032-0.021-0.063-0.045-0.094-0.066-0.093-0.066-0.186-0.132-0.277-0.2-0.042-0.031-0.082-0.062-0.123-0.093-0.084-0.064-0.168-0.129-0.25-0.196-0.037-0.030-0.075-0.060-0.112-0.090-0.105-0.087-0.209-0.173-0.312-0.263-0.011-0.009-0.023-0.018-0.034-0.028-0.111-0.097-0.22-0.197-0.328-0.298-0.031-0.030-0.062-0.059-0.092-0.088-0.080-0.076-0.158-0.153-0.235-0.231-0.031-0.031-0.062-0.061-0.092-0.092-0.098-0.101-0.194-0.203-0.289-0.306-0.005-0.005-0.010-0.010-0.014-0.015-0.1-0.109-0.197-0.221-0.293-0.334-0.026-0.031-0.051-0.060-0.077-0.091-0.071-0.086-0.142-0.173-0.211-0.261-0.026-0.031-0.052-0.064-0.077-0.096-0.083-0.108-0.164-0.215-0.243-0.324-2.197-2.996-2.986-7.129-1.23-10.523l-1.556 1.974c-1.994 2.866-1.746 6.595-0.223 9.64 0.036 0.073 0.074 0.145 0.112 0.217 0.024 0.045 0.046 0.092 0.071 0.137 0.014 0.027 0.030 0.053 0.044 0.079 0.026 0.049 0.053 0.095 0.079 0.142 0.047 0.083 0.096 0.166 0.145 0.249 0.027 0.045 0.055 0.091 0.083 0.136 0.055 0.089 0.111 0.176 0.169 0.264 0.024 0.037 0.047 0.075 0.072 0.111 0.080 0.118 0.161 0.236 0.244 0.353 0.002 0.003 0.005 0.006 0.007 0.009 0.013 0.018 0.028 0.037 0.041 0.056 0.072 0.1 0.147 0.199 0.223 0.296 0.028 0.036 0.056 0.072 0.084 0.107 0.067 0.085 0.136 0.169 0.206 0.253 0.026 0.031 0.052 0.063 0.079 0.094 0.094 0.11 0.189 0.22 0.287 0.328 0.002 0.002 0.004 0.004 0.006 0.005 0.004 0.005 0.008 0.008 0.011 0.013 0.095 0.104 0.193 0.206 0.291 0.307 0.031 0.032 0.062 0.063 0.093 0.094 0.076 0.077 0.154 0.153 0.233 0.228 0.032 0.030 0.063 0.061 0.095 0.091 0.105 0.099 0.211 0.196 0.319 0.291 0.002 0.001 0.003 0.003 0.005 0.004 0.018 0.016 0.038 0.032 0.056 0.047 0.095 0.082 0.192 0.164 0.29 0.245 0.040 0.032 0.080 0.064 0.12 0.096 0.080 0.064 0.16 0.127 0.241 0.189 0.043 0.033 0.086 0.066 0.129 0.098 0.089 0.066 0.18 0.131 0.271 0.194 0.033 0.024 0.065 0.047 0.099 0.070 0.009 0.006 0.018 0.013 0.027 0.019 0.086 0.060 0.175 0.116 0.263 0.174 0.038 0.025 0.075 0.051 0.114 0.076 0.136 0.086 0.273 0.171 0.412 0.253 0.038 0.022 0.076 0.043 0.114 0.064 0.102 0.059 0.205 0.117 0.309 0.174 0.056 0.030 0.114 0.059 0.171 0.088 0.073 0.038 0.147 0.078 0.221 0.115 0.017 0.009 0.035 0.017 0.051 0.025 0.030 0.014 0.060 0.028 0.091 0.044 0.116 0.055 0.233 0.11 0.351 0.163 0.025 0.011 0.049 0.022 0.074 0.033 0.135 0.059 0.271 0.116 0.409 0.17 0.033 0.014 0.066 0.026 0.1 0.039 0.127 0.049 0.256 0.098 0.386 0.143 0.016 0.006 0.032 0.012 0.049 0.017 0.142 0.050 0.286 0.096 0.43 0.141 0.034 0.010 0.069 0.021 0.104 0.031 0.147 0.044 0.293 0.097 0.445 0.125 9.643 1.759 12.444-5.795 12.444-5.795-2.352 3.065-6.528 3.873-10.485 2.974zM12.758 16.231c0.216 0.31 0.456 0.678 0.742 0.927 0.104 0.114 0.213 0.226 0.324 0.336 0.028 0.029 0.057 0.056 0.085 0.084 0.108 0.105 0.217 0.207 0.33 0.307 0.005 0.003 0.009 0.008 0.014 0.012 0.001 0.001 0.002 0.002 0.003 0.003 0.125 0.11 0.255 0.216 0.386 0.319 0.029 0.022 0.058 0.046 0.088 0.069 0.132 0.101 0.266 0.2 0.404 0.295 0.004 0.003 0.008 0.006 0.012 0.009 0.061 0.042 0.123 0.081 0.184 0.122 0.030 0.019 0.058 0.040 0.088 0.058 0.098 0.063 0.198 0.125 0.299 0.183 0.014 0.009 0.028 0.016 0.042 0.024 0.087 0.051 0.176 0.1 0.265 0.148 0.031 0.018 0.063 0.033 0.094 0.049 0.061 0.032 0.123 0.064 0.185 0.096 0.009 0.004 0.019 0.009 0.028 0.012 0.127 0.063 0.255 0.123 0.386 0.18 0.028 0.012 0.057 0.023 0.085 0.035 0.105 0.045 0.21 0.088 0.316 0.129 0.045 0.017 0.091 0.033 0.135 0.050 0.097 0.036 0.193 0.069 0.291 0.101 0.044 0.014 0.087 0.028 0.131 0.042 0.139 0.043 0.276 0.098 0.42 0.122 7.445 1.233 9.164-4.499 9.164-4.499-1.549 2.232-4.55 3.296-7.752 2.465-0.142-0.038-0.282-0.078-0.422-0.122-0.043-0.013-0.084-0.027-0.127-0.041-0.099-0.032-0.197-0.066-0.295-0.102-0.045-0.017-0.089-0.033-0.133-0.050-0.107-0.041-0.213-0.084-0.317-0.128-0.029-0.013-0.058-0.024-0.086-0.036-0.131-0.057-0.261-0.117-0.389-0.18-0.066-0.032-0.13-0.066-0.195-0.099-0.037-0.019-0.075-0.038-0.112-0.058-0.083-0.045-0.165-0.092-0.246-0.139-0.019-0.011-0.040-0.022-0.059-0.033-0.101-0.059-0.2-0.12-0.299-0.182-0.030-0.019-0.060-0.040-0.090-0.060-0.065-0.042-0.13-0.085-0.193-0.128-0.137-0.095-0.271-0.194-0.402-0.294-0.030-0.024-0.061-0.047-0.091-0.071-1.401-1.107-2.512-2.619-3.041-4.334-0.554-1.778-0.434-3.775 0.525-5.395l-1.178 1.663c-1.442 2.075-1.364 4.853-0.239 7.048 0.189 0.368 0.401 0.725 0.638 1.065zM20.606 13.664c0.061 0.023 0.123 0.043 0.185 0.064 0.027 0.008 0.054 0.018 0.082 0.026 0.088 0.027 0.175 0.060 0.265 0.076 4.111 0.794 5.226-2.11 5.523-2.537-0.977 1.406-2.618 1.744-4.632 1.255-0.159-0.039-0.334-0.096-0.488-0.151-0.197-0.070-0.39-0.15-0.579-0.24-0.358-0.172-0.699-0.38-1.015-0.619-1.802-1.367-2.922-3.976-1.746-6.101l-0.637 0.877c-0.85 1.251-0.933 2.805-0.344 4.186 0.622 1.467 1.897 2.617 3.384 3.163z\"></path></svg>"
 
 /***/ })
 
 /******/ });
-});
+});
\ No newline at end of file
--- a/devtools/client/debugger/new/parser-worker.js
+++ b/devtools/client/debugger/new/parser-worker.js
@@ -1348,16 +1348,17 @@ module.exports = isObjectLike;
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
 exports.isFunction = isFunction;
 exports.isAwaitExpression = isAwaitExpression;
 exports.isYieldExpression = isYieldExpression;
 exports.isVariable = isVariable;
+exports.isComputedExpression = isComputedExpression;
 exports.getMemberExpression = getMemberExpression;
 exports.getVariables = getVariables;
 
 var _types = __webpack_require__(2268);
 
 var t = _interopRequireWildcard(_types);
 
 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; } }
@@ -1378,16 +1379,21 @@ function isYieldExpression(path) {
   return t.isYieldExpression(node) || t.isYieldExpression(parent.init) || t.isYieldExpression(parent);
 }
 
 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)) {
       expr = [node.property.name].concat(expr);
       return _getMemberExpression(node.object, expr);
     }
 
     if (t.isCallExpression(node)) {
@@ -1581,17 +1587,17 @@ Object.defineProperty(exports, "__esModu
   value: true
 });
 
 var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; /* 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/>. */
 
 exports.clearSymbols = clearSymbols;
-exports.default = getSymbols;
+exports.getSymbols = getSymbols;
 
 var _flatten = __webpack_require__(706);
 
 var _flatten2 = _interopRequireDefault(_flatten);
 
 var _types = __webpack_require__(2268);
 
 var t = _interopRequireWildcard(_types);
@@ -1843,54 +1849,62 @@ function extractSymbols(sourceId) {
   return symbols;
 }
 
 function extendSnippet(name, expression, path = null, prevPath = null) {
   const computed = path && path.node.computed;
   const prevComputed = prevPath && prevPath.node.computed;
   const prevArray = t.isArrayExpression(prevPath);
   const array = t.isArrayExpression(path);
+  const value = path && path.node.property && path.node.property.extra && path.node.property.extra.raw || "";
 
   if (expression === "") {
     if (computed) {
-      return `[${name}]`;
+      return name === undefined ? `[${value}]` : `[${name}]`;
     }
     return name;
   }
 
   if (computed || array) {
     if (prevComputed || prevArray) {
       return `[${name}]${expression}`;
     }
-    return `[${name}].${expression}`;
+    return `[${name === undefined ? value : name}].${expression}`;
   }
 
   if (prevComputed || prevArray) {
     return `${name}${expression}`;
   }
 
+  if ((0, _helpers.isComputedExpression)(expression) && name !== undefined) {
+    return `${name}${expression}`;
+  }
+
   return `${name}.${expression}`;
 }
 
 function getMemberSnippet(node, expression = "") {
   if (t.isMemberExpression(node)) {
     const name = node.property.name;
-
-    return getMemberSnippet(node.object, extendSnippet(name, expression));
+    const snippet = getMemberSnippet(node.object, extendSnippet(name, expression, { node }));
+    return snippet;
   }
 
   if (t.isCallExpression(node)) {
     return "";
   }
 
   if (t.isThisExpression(node)) {
     return `this.${expression}`;
   }
 
   if (t.isIdentifier(node)) {
+    if ((0, _helpers.isComputedExpression)(expression)) {
+      return `${node.name}${expression}`;
+    }
     return `${node.name}.${expression}`;
   }
 
   return expression;
 }
 
 function getObjectSnippet(path, prevPath, expression = "") {
   if (!path) {
@@ -2048,18 +2062,16 @@ function clearSources() {
 
 "use strict";
 
 
 var _closest = __webpack_require__(1455);
 
 var _getSymbols = __webpack_require__(1457);
 
-var _getSymbols2 = _interopRequireDefault(_getSymbols);
-
 var _ast = __webpack_require__(1375);
 
 var _getScopes = __webpack_require__(2413);
 
 var _getScopes2 = _interopRequireDefault(_getScopes);
 
 var _sources = __webpack_require__(1458);
 
@@ -2087,17 +2099,17 @@ function _interopRequireDefault(obj) { r
  * 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 { workerHandler } = _devtoolsUtils.workerUtils;
 
 self.onmessage = workerHandler({
   getClosestExpression: _closest.getClosestExpression,
   findOutOfScopeLocations: _findOutOfScopeLocations2.default,
-  getSymbols: _getSymbols2.default,
+  getSymbols: _getSymbols.getSymbols,
   getScopes: _getScopes2.default,
   clearSymbols: _getSymbols.clearSymbols,
   clearScopes: _getScopes.clearScopes,
   clearASTs: _ast.clearASTs,
   hasSource: _sources.hasSource,
   setSource: _sources.setSource,
   clearSources: _sources.clearSources,
   isInvalidPauseLocation: _pauseLocation.isInvalidPauseLocation,
@@ -2322,22 +2334,20 @@ var _findIndex2 = _interopRequireDefault
 var _findLastIndex = __webpack_require__(1686);
 
 var _findLastIndex2 = _interopRequireDefault(_findLastIndex);
 
 var _contains = __webpack_require__(1456);
 
 var _getSymbols = __webpack_require__(1457);
 
-var _getSymbols2 = _interopRequireDefault(_getSymbols);
-
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
 function findSymbols(source) {
-  const { functions, comments } = (0, _getSymbols2.default)(source);
+  const { functions, comments } = (0, _getSymbols.getSymbols)(source);
   return { functions, comments };
 }
 
 /**
  * Returns the location for a given function path. If the path represents a
  * function declaration, the location will begin after the function identifier
  * but before the function parameters.
  */
@@ -2680,22 +2690,18 @@ module.exports = findLastIndex;
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
 exports.getFramework = getFramework;
 
 var _getSymbols = __webpack_require__(1457);
 
-var _getSymbols2 = _interopRequireDefault(_getSymbols);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
 function getFramework(sourceId) {
-  const sourceSymbols = (0, _getSymbols2.default)(sourceId);
+  const sourceSymbols = (0, _getSymbols.getSymbols)(sourceId);
 
   if (isReactComponent(sourceSymbols)) {
     return "React";
   }
   if (isAngularComponent(sourceSymbols)) {
     return "Angular";
   }
   if (isVueComponent(sourceSymbols)) {
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-content-script-sources.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-content-script-sources.js
@@ -1,13 +1,10 @@
 "use strict";
 
-/* global ExtensionTestUtils, closeTab, openToolboxForTab, assertDebugLine,
-          waitForSelectedSource */
-
 // Tests that the content scripts are listed in the source tree.
 
 async function selectContentScriptSources(dbg) {
   await waitForSources(dbg, "content_script.js");
 
   // Select a source.
   await selectSource(dbg, "content_script.js");
 
@@ -62,18 +59,18 @@ add_task(async function() {
   await selectContentScriptSources(dbg);
 
   await addBreakpoint(dbg, "content_script.js", 2);
 
   for (let i = 1; i < 3; i++) {
     info(`Reloading tab (${i} time)`);
     gBrowser.reloadTab(gBrowser.selectedTab);
     await waitForPaused(dbg);
-    await waitForSources(dbg, "content_script.js");
     await waitForSelectedSource(dbg, "content_script.js");
+
     ok(
       findElementWithSelector(dbg, ".sources-list .focused"),
       "Source is focused"
     );
     assertPausedLocation(dbg);
     assertDebugLine(dbg, 2);
     await resume(dbg);
   }
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-editor-highlight.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-editor-highlight.js
@@ -8,22 +8,18 @@
 add_task(async function() {
   const dbg = await initDebugger("doc-scripts.html");
   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
   // and highlight a specific line.
-  dbg.actions.selectSourceURL(sourceUrl, { location: { line: 66 } });
 
-  // Wait for the source text to load and make sure we're in the right
-  // place.
-  await waitForSelectedSource(dbg, sourceUrl);
-  log(`loaded source`);
+  await selectSource(dbg, sourceUrl, 66)
 
   // TODO: revisit highlighting lines when the debugger opens
   // assertHighlightLocation(dbg, "long.js", 66);
 
   log(`Select line 16 and make sure the editor scrolled.`);
   await selectSource(dbg, "long.js", 16);
   await waitForElementWithSelector(dbg, ".CodeMirror-code > .highlight-line");
   assertHighlightLocation(dbg, "long.js", 16);
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-outline.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-outline.js
@@ -12,17 +12,16 @@ function getNthItem(dbg, index) {
   return findElement(dbg, "outlineItem", index);
 }
 
 add_task(async function() {
   const dbg = await initDebugger("doc-scripts.html");
   const { selectors: { getSelectedSource }, getState } = dbg;
 
   await selectSource(dbg, "simple1", 1);
-  await waitForSelectedSource(dbg, "simple1");
 
   findElementWithSelector(dbg, ".outline-tab").click();
   is(getItems(dbg).length, 5, "5 items in the list");
 
   // click on an element
   const item = getNthItem(dbg, 3);
   is(item.innerText, "evaledFunc()", "got evaled func");
   item.click();
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-pretty-print-console.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-pretty-print-console.js
@@ -1,48 +1,45 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
+async function waitForConsoleLink(dbg, text) {
+  const toolbox = dbg.toolbox;
+  const console = await toolbox.selectTool("webconsole");
+  const hud = console.hud;
+
+  return waitFor(() => {
+    // Wait until the message updates.
+    const found = hud.ui.outputNode.querySelector(".frame-link-source");
+    if (!found) {
+      return false;
+    }
+
+    const linkText = found.textContent;
+    if (!text) {
+      return linkText;
+    }
+
+    return linkText == text ? linkText : null;
+  });
+}
+
 // Tests that pretty-printing updates console messages.
-
 add_task(async function() {
   const dbg = await initDebugger("doc-minified.html");
   invokeInTab("arithmetic");
 
   info("Switch to console and check message");
-  const toolbox = dbg.toolbox;
-  const console = await toolbox.selectTool("webconsole");
-  const hud = console.hud;
-
-  let node = await waitFor(() =>
-    hud.ui.outputNode.querySelector(".frame-link-source")
-  );
-  const initialLocation = "math.min.js:3:65";
-  is(node.textContent, initialLocation, "location is correct in minified code");
+  await waitForConsoleLink(dbg,  "math.min.js:3:65");
 
   info("Switch back to debugger and pretty-print");
-  await toolbox.selectTool("jsdebugger");
+  await dbg.toolbox.selectTool("jsdebugger");
   await selectSource(dbg, "math.min.js", 2);
-  clickElement(dbg, "prettyPrintButton");
 
-  await waitForSource(dbg, "math.min.js:formatted");
-  const ppSrc = findSource(dbg, "math.min.js:formatted");
-
-  ok(ppSrc, "Pretty-printed source exists");
+  clickElement(dbg, "prettyPrintButton");
+  await waitForSelectedSource(dbg, "math.min.js:formatted");
 
   info("Switch back to console and check message");
-  node = await waitFor(() => {
-    // Wait until the message updates.
-    const found = hud.ui.outputNode.querySelector(".frame-link-source");
-    if (found.textContent == initialLocation) {
-      return null;
-    }
-    return found;
-  });
-
-  is(
-    node.textContent,
-    "math.min.js:formatted:22",
-    "location is correct in minified code"
-  );
+  await waitForConsoleLink(dbg, "math.min.js:formatted:22");
+  ok(true);
 });
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-reload.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-reload.js
@@ -16,18 +16,16 @@ async function waitForBreakpoint(dbg, lo
     "Waiting for breakpoint"
   );
 }
 
 add_task(async function() {
   const dbg = await initDebugger("reload/doc-reload.html");
   await waitForSource(dbg, "sjs_code_reload");
   await selectSource(dbg, "sjs_code_reload");
-  await waitForSelectedSource(dbg, "sjs_code_reload");
-
   await addBreakpoint(dbg, "sjs_code_reload", 2);
 
   await reload(dbg, "sjs_code_reload");
 
   const source = findSource(dbg, "sjs_code_reload");
   const location = { sourceId: source.id, line: 6 };
 
   await waitForBreakpoint(dbg, location);
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-sourcemaps2.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-sourcemaps2.js
@@ -40,13 +40,14 @@ add_task(async function() {
   invokeInTab("logMessage");
 
   await waitForPaused(dbg);
   assertPausedLocation(dbg);
 
   // Tests the existence of the sourcemap link in the original source.
   ok(findElement(dbg, "sourceMapLink"), "Sourcemap link in original source");
   await selectSource(dbg, "main.min.js");
+
   ok(
     !findElement(dbg, "sourceMapLink"),
     "No Sourcemap link exists in generated source"
   );
 });
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-stepping.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-stepping.js
@@ -21,17 +21,16 @@ add_task(async function test() {
   await stepIn(dbg);
   await stepIn(dbg);
   await stepIn(dbg);
   await stepIn(dbg);
   await stepIn(dbg);
   await stepIn(dbg);
   await stepIn(dbg);
   await stepIn(dbg);
-
   await stepIn(dbg);
   await stepIn(dbg);
   await stepIn(dbg);
   await stepIn(dbg);
   await stepIn(dbg);
 
   assertSelectedFile(dbg, "bundle.js");
   assertDebugLine(dbg, 42309);
--- a/devtools/client/debugger/new/test/mochitest/head.js
+++ b/devtools/client/debugger/new/test/mochitest/head.js
@@ -172,20 +172,21 @@ function waitForThreadEvents(dbg, eventN
 function waitForState(dbg, predicate, msg) {
   return new Promise(resolve => {
     info(`Waiting for state change: ${msg || ""}`);
     if (predicate(dbg.store.getState())) {
       return resolve();
     }
 
     const unsubscribe = dbg.store.subscribe(() => {
-      if (predicate(dbg.store.getState())) {
+      const result = predicate(dbg.store.getState())
+      if (result) {
         info(`Finished waiting for state change: ${msg || ""}`);
         unsubscribe();
-        resolve();
+        resolve(result);
       }
     });
   });
 }
 
 /**
  * Waits for sources to be loaded.
  *
@@ -225,17 +226,17 @@ function waitForSources(dbg, ...sources)
  * @param {String} source
  * @return {Promise}
  * @static
  */
 function waitForSource(dbg, url) {
   return waitForState(dbg, state => {
     const sources = dbg.selectors.getSources(state);
     return sources.find(s => (s.get("url") || "").includes(url));
-  });
+  }, `source exists`);
 }
 
 async function waitForElement(dbg, name) {
   await waitUntil(() => findElement(dbg, name));
   return findElement(dbg, name);
 }
 
 async function waitForElementWithSelector(dbg, selector) {
@@ -587,19 +588,20 @@ function waitForLoadedSources(dbg) {
  *
  * @memberof mochitest/actions
  * @param {Object} dbg
  * @param {String} url
  * @param {Number} line
  * @return {Promise}
  * @static
  */
-function selectSource(dbg, url, line) {
+async function selectSource(dbg, url, line) {
   const source = findSource(dbg, url);
-  return dbg.actions.selectLocation({ sourceId: source.id, line });
+  await dbg.actions.selectLocation({ sourceId: source.id, line });
+  return waitForSelectedSource(dbg, url);
 }
 
 function closeTab(dbg, url) {
   const source = findSource(dbg, url);
   return dbg.actions.closeTab(source.url);
 }
 
 /**