Merge mozilla-central to autoland. a=merge CLOSED TREE
authorshindli <shindli@mozilla.com>
Thu, 31 Jan 2019 23:51:27 +0200
changeset 456326 83598ee9b2e8378bc141c46bd9acdce730dc4fd9
parent 456325 85614e63fa3b0f8ac55bb267061af8c44b52ef03 (current diff)
parent 456266 afa70e5e516abf11f4760b915c0b3d09bc351147 (diff)
child 456327 9f49631d6536a27752469cac43e60ed1f19eed55
push id35478
push usershindli@mozilla.com
push dateFri, 01 Feb 2019 03:55:46 +0000
treeherdermozilla-central@945770882765 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone67.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
Merge mozilla-central to autoland. a=merge CLOSED TREE
devtools/client/webconsole/test/mochitest/browser.ini
--- 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 121
+Version 122
 
-Comparison: https://github.com/devtools-html/debugger.html/compare/release-120...release-121
+Comparison: https://github.com/devtools-html/debugger.html/compare/release-121...release-122
 
 Packages:
 - babel-plugin-transform-es2015-modules-commonjs @6.26.2
 - babel-preset-react @6.24.1
 - react @16.4.1
 - react-dom @16.4.1
 - webpack @3.12.0
--- a/devtools/client/debugger/new/dist/debugger.css
+++ b/devtools/client/debugger/new/dist/debugger.css
@@ -1058,24 +1058,16 @@ html[dir="rtl"] .managed-tree .tree .nod
   /* default height an width which will likely be overrode */
   width: 12px;
   height: 12px;
   /* makes span appear like an image */
   display: inline-block;
   background: var(--theme-body-color);
   mask-size: 100%;
 }
-
-.img.arrow.arrow.expanded {
-  transform: rotate(0deg);
-}
-
-.img.arrow.arrow.expanded {
-  transform: rotate(0deg);
-}
 /* 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/>. */
 
 .close-btn {
   width: 14px;
   height: 14px;
   border: 1px solid transparent;
@@ -2193,21 +2185,27 @@ menuseparator {
   margin-inline-end: 5px;
 }
 
 .source-icon {
   width: 15px;
   height: 15px;
 }
 
-.source-icon.prettyPrint {
+.img.prettyPrint {
   mask: url("resource://devtools/client/debugger/new/images/prettyPrint.svg") no-repeat;
   mask-size: 100%;
   background: var(--theme-highlight-blue);
   fill: var(--theme-textbox-box-shadow);
+  position: relative;
+}
+
+.sources-list .img.prettyPrint {
+  top: 2px;
+  margin-inline-start: 3px;
 }
 
 .source-icon.vue {
   background: url("resource://devtools/client/debugger/new/images/vuejs.svg") 1px 1px no-repeat;
   background-size: 15px;
 }
 
 .source-icon.angular {
@@ -2321,16 +2319,20 @@ menuseparator {
   font-size: 0.9em;
   color: var(--theme-comment);
 }
 
 .sources-list .tree .focused .label .suffix {
   color: inherit;
 }
 
+.sources-list .tree .img.arrow.expanded {
+  transform: rotate(0deg);
+}
+
 .theme-dark .source-list .tree .node.focused {
   background-color: var(--theme-tab-toolbar-background);
 }
 
 .sources-list .tree .focused .label {
   background-color: var(--theme-selection-background);
 }
 
@@ -3793,17 +3795,16 @@ html[dir="rtl"] .breakpoints-list .break
 
 .frames [role="list"] .frames-group.expanded .badge {
   color: var(--theme-highlight-blue);
 }
 
 .group-description-name {
   padding-left: 5px;
 }
-
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 .badge {
   --size: 17px;
   --radius: calc(var(--size) / 2);
   height: var(--size);
@@ -3853,17 +3854,17 @@ html[dir="rtl"] .breakpoints-list .break
   font-style: normal;
 }
 /* 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/>. */
 
 .frames [role="list"] {
   list-style: none;
-  margin-top: 4px;
+  margin: 0;
   padding: 0;
 }
 
 .frames [role="list"] [role="listitem"] {
   padding: 2px 10px 2px 20px;
   overflow: hidden;
   display: flex;
   justify-content: space-between;
@@ -3955,16 +3956,17 @@ html[dir="rtl"] .breakpoints-list .break
 .show-more:hover {
   background-color: var(--theme-toolbar-background-hover);
 }
 
 .annotation-logo {
   mask-size: 100%;
   display: inline-block;
   width: 12px;
+  margin-inline-start: 4px;
 }
 
 :root.theme-dark .annotation-logo:not(.angular) svg path {
   fill: var(--theme-highlight-blue);
 }
 
 /* Some elements are added to the DOM only to be printed into the clipboard
    when the user copy some elements. We don't want those elements to mess with
--- a/devtools/client/debugger/new/dist/parser-worker.js
+++ b/devtools/client/debugger/new/dist/parser-worker.js
@@ -775,16 +775,17 @@ Object.defineProperty(exports, "__esModu
 });
 exports.parse = parse;
 exports.parseConsoleScript = parseConsoleScript;
 exports.parseScript = parseScript;
 exports.getAst = getAst;
 exports.clearASTs = clearASTs;
 exports.traverseAst = traverseAst;
 exports.hasNode = hasNode;
+exports.replaceNode = replaceNode;
 
 var _parseScriptTags = __webpack_require__(1023);
 
 var _parseScriptTags2 = _interopRequireDefault(_parseScriptTags);
 
 var _parser = __webpack_require__(3773);
 
 var babelParser = _interopRequireWildcard(_parser);
@@ -954,16 +955,30 @@ function hasNode(rootNode, predicate) {
   } catch (e) {
     if (e.message === "MATCH") {
       return true;
     }
   }
   return false;
 }
 
+function replaceNode(ancestors, node) {
+  const parent = ancestors[ancestors.length - 1];
+
+  if (typeof parent.index === "number") {
+    if (Array.isArray(node)) {
+      parent.node[parent.key].splice(parent.index, 1, ...node);
+    } else {
+      parent.node[parent.key][parent.index] = node;
+    }
+  } else {
+    parent.node[parent.key] = node;
+  }
+}
+
 /***/ }),
 
 /***/ 14:
 /***/ (function(module, exports) {
 
 /**
  * Checks if `value` is object-like. A value is object-like if it's not `null`
  * and has a `typeof` result of "object".
@@ -23129,16 +23144,17 @@ const {
 const {
   workerUtils: { WorkerDispatcher }
 } = __webpack_require__(3651);
 
 const dispatcher = new WorkerDispatcher();
 
 const setAssetRootURL = dispatcher.task("setAssetRootURL");
 const getOriginalURLs = dispatcher.task("getOriginalURLs");
+const hasOriginalURL = dispatcher.task("hasOriginalURL");
 const getOriginalRanges = dispatcher.task("getOriginalRanges");
 const getGeneratedRanges = dispatcher.task("getGeneratedRanges", {
   queue: true
 });
 const getGeneratedLocation = dispatcher.task("getGeneratedLocation", {
   queue: true
 });
 const getAllGeneratedLocations = dispatcher.task("getAllGeneratedLocations", {
@@ -23155,16 +23171,17 @@ const getOriginalStackFrames = dispatche
 
 module.exports = {
   originalToGeneratedId,
   generatedToOriginalId,
   isGeneratedId,
   isOriginalId,
   hasMappedSource,
   getOriginalURLs,
+  hasOriginalURL,
   getOriginalRanges,
   getGeneratedRanges,
   getGeneratedLocation,
   getAllGeneratedLocations,
   getOriginalLocation,
   getFileGeneratedRange,
   getLocationScopes,
   getOriginalSourceText,
@@ -23214,17 +23231,18 @@ function originalToGeneratedId(originalI
   return match ? match[1] : "";
 }
 
 function generatedToOriginalId(generatedId, url) {
   return `${generatedId}/originalSource-${md5(url)}`;
 }
 
 function isOriginalId(id) {
-  return !!id.match(/\/originalSource/);
+  return (/\/originalSource/.test(id)
+  );
 }
 
 function isGeneratedId(id) {
   return !isOriginalId(id);
 }
 
 /**
  * Trims the query part or reference identifier of a URL string, if necessary.
@@ -23565,30 +23583,36 @@ function mapExpression(expression, mappi
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
 exports.default = mapExpressionBindings;
 
+var _ast = __webpack_require__(1375);
+
 var _helpers = __webpack_require__(1411);
 
 var _generator = __webpack_require__(2365);
 
 var _generator2 = _interopRequireDefault(_generator);
 
 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; } }
 
 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 getAssignmentTarget(node, bindings) {
   if (t.isObjectPattern(node)) {
     for (const property of node.properties) {
       if (t.isRestElement(property)) {
         property.argument = getAssignmentTarget(property.argument, bindings);
       } else {
         property.value = getAssignmentTarget(property.value, bindings);
       }
@@ -23621,44 +23645,26 @@ function getAssignmentTarget(node, bindi
     return bindings.includes(node.name) ? node : t.memberExpression(t.identifier("self"), node);
   }
 
   return node;
 }
 
 // translates new bindings `var a = 3` into `self.a = 3`
 // and existing bindings `var a = 3` into `a = 3` for re-assignments
-/* 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 globalizeDeclaration(node, bindings) {
   return node.declarations.map(declaration => t.expressionStatement(t.assignmentExpression("=", getAssignmentTarget(declaration.id, bindings), declaration.init)));
 }
 
 // translates new bindings `a = 3` into `self.a = 3`
 // and keeps assignments the same for existing bindings.
 function globalizeAssignment(node, bindings) {
   return t.assignmentExpression(node.operator, getAssignmentTarget(node.left, bindings), node.right);
 }
 
-function replaceNode(ancestors, node) {
-  const parent = ancestors[ancestors.length - 1];
-
-  if (typeof parent.index === "number") {
-    if (Array.isArray(node)) {
-      parent.node[parent.key].splice(parent.index, 1, ...node);
-    } else {
-      parent.node[parent.key][parent.index] = node;
-    }
-  } else {
-    parent.node[parent.key] = node;
-  }
-}
-
 function mapExpressionBindings(expression, ast, bindings = []) {
   let isMapped = false;
   let shouldUpdate = true;
 
   t.traverse(ast, (node, ancestors) => {
     const parent = ancestors[ancestors.length - 1];
 
     if (t.isWithStatement(node)) {
@@ -23669,30 +23675,30 @@ function mapExpressionBindings(expressio
     if (!(0, _helpers.isTopLevel)(ancestors)) {
       return;
     }
 
     if (t.isAssignmentExpression(node)) {
       if (t.isIdentifier(node.left) || t.isPattern(node.left)) {
         const newNode = globalizeAssignment(node, bindings);
         isMapped = true;
-        return replaceNode(ancestors, newNode);
+        return (0, _ast.replaceNode)(ancestors, newNode);
       }
 
       return;
     }
 
     if (!t.isVariableDeclaration(node)) {
       return;
     }
 
     if (!t.isForStatement(parent.node)) {
       const newNodes = globalizeDeclaration(node, bindings);
       isMapped = true;
-      replaceNode(ancestors, newNodes);
+      (0, _ast.replaceNode)(ancestors, newNodes);
     }
   });
 
   if (!shouldUpdate || !isMapped) {
     return expression;
   }
 
   return (0, _generator2.default)(ast).code;
@@ -44376,22 +44382,137 @@ function _interopRequireDefault(obj) { r
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 function hasTopLevelAwait(ast) {
   const hasAwait = (0, _ast.hasNode)(ast, (node, ancestors, b) => t.isAwaitExpression(node) && (0, _helpers.isTopLevel)(ancestors));
 
   return hasAwait;
 }
 
-function wrapExpressionFromAst(ast) {
+// translates new bindings `var a = 3` into `a = 3`.
+function translateDeclarationIntoAssignment(node) {
+  return node.declarations.reduce((acc, declaration) => {
+    // Don't translate declaration without initial assignment (e.g. `var a;`)
+    if (!declaration.init) {
+      return acc;
+    }
+    acc.push(t.expressionStatement(t.assignmentExpression("=", declaration.id, declaration.init)));
+    return acc;
+  }, []);
+}
+
+/**
+ * Given an AST, compute its last statement and replace it with a
+ * return statement.
+ */
+function addReturnNode(ast) {
   const statements = ast.program.body;
   const lastStatement = statements[statements.length - 1];
-  const body = statements.slice(0, -1).concat(t.returnStatement(lastStatement.expression));
-
-  const newAst = t.expressionStatement(t.callExpression(t.arrowFunctionExpression([], t.blockStatement(body), true), []));
+  return statements.slice(0, -1).concat(t.returnStatement(lastStatement.expression));
+}
+
+function getDeclarations(node) {
+  const { kind, declarations } = node;
+  const declaratorNodes = declarations.reduce((acc, d) => {
+    const declarators = getVariableDeclarators(d.id);
+    return acc.concat(declarators);
+  }, []);
+
+  // We can't declare const variables outside of the async iife because we
+  // wouldn't be able to re-assign them. As a workaround, we transform them
+  // to `let` which should be good enough for those case.
+  return t.variableDeclaration(kind === "const" ? "let" : kind, declaratorNodes);
+}
+
+function getVariableDeclarators(node) {
+  if (t.isIdentifier(node)) {
+    return t.variableDeclarator(t.identifier(node.name));
+  }
+
+  if (t.isObjectProperty(node)) {
+    return getVariableDeclarators(node.value);
+  }
+  if (t.isRestElement(node)) {
+    return getVariableDeclarators(node.argument);
+  }
+
+  if (t.isAssignmentPattern(node)) {
+    return getVariableDeclarators(node.left);
+  }
+
+  if (t.isArrayPattern(node)) {
+    return node.elements.reduce((acc, element) => acc.concat(getVariableDeclarators(element)), []);
+  }
+  if (t.isObjectPattern(node)) {
+    return node.properties.reduce((acc, property) => acc.concat(getVariableDeclarators(property)), []);
+  }
+  return [];
+}
+
+/**
+ * Given an AST and an array of variableDeclaration nodes, return a new AST with
+ * all the declarations at the top of the AST.
+ */
+function addTopDeclarationNodes(ast, declarationNodes) {
+  const statements = [];
+  declarationNodes.forEach(declarationNode => {
+    statements.push(getDeclarations(declarationNode));
+  });
+  statements.push(ast);
+  return t.program(statements);
+}
+
+/**
+ * Given an AST, return an object of the following shape:
+ *   - newAst: {AST} the AST where variable declarations were transformed into
+ *             variable assignments
+ *   - declarations: {Array<Node>} An array of all the declaration nodes needed
+ *                   outside of the async iife.
+ */
+function translateDeclarationsIntoAssignment(ast) {
+  const declarations = [];
+  t.traverse(ast, (node, ancestors) => {
+    const parent = ancestors[ancestors.length - 1];
+
+    if (t.isWithStatement(node) || !(0, _helpers.isTopLevel)(ancestors) || t.isAssignmentExpression(node) || !t.isVariableDeclaration(node) || t.isForStatement(parent.node) || !Array.isArray(node.declarations) || node.declarations.length === 0) {
+      return;
+    }
+
+    const newNodes = translateDeclarationIntoAssignment(node);
+    (0, _ast.replaceNode)(ancestors, newNodes);
+    declarations.push(node);
+  });
+
+  return {
+    newAst: ast,
+    declarations
+  };
+}
+
+/**
+ * Given an AST, wrap its body in an async iife, transform variable declarations
+ * in assignments and move the variable declarations outside of the async iife.
+ * Example: With the AST for the following expression: `let a = await 123`, the
+ * function will return:
+ * let a;
+ * (async => {
+ *   return a = await 123;
+ * })();
+ */
+function wrapExpressionFromAst(ast) {
+  // Transform let and var declarations into assignments, and get back an array
+  // of variable declarations.
+  let { newAst, declarations } = translateDeclarationsIntoAssignment(ast);
+  const body = addReturnNode(newAst);
+
+  // Create the async iife.
+  newAst = t.expressionStatement(t.callExpression(t.arrowFunctionExpression([], t.blockStatement(body), true), []));
+
+  // Now let's put all the variable declarations at the top of the async iife.
+  newAst = addTopDeclarationNodes(newAst, declarations);
 
   return (0, _generator2.default)(newAst).code;
 }
 
 function mapTopLevelAwait(expression, ast) {
   if (!ast) {
     // If there's no ast this means the expression is malformed. And if the
     // expression contains the await keyword, we still want to wrap it in an
--- a/devtools/client/debugger/new/panel.js
+++ b/devtools/client/debugger/new/panel.js
@@ -1,20 +1,26 @@
 /* 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/. */
-"use strict";
+ * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 const { Task } = require("devtools/shared/task");
 const { LocalizationHelper } = require("devtools/shared/l10n");
 const { gDevTools } = require("devtools/client/framework/devtools");
-const { gDevToolsBrowser } = require("devtools/client/framework/devtools-browser");
+const {
+  gDevToolsBrowser
+} = require("devtools/client/framework/devtools-browser");
 const { TargetFactory } = require("devtools/client/framework/target");
 const { Toolbox } = require("devtools/client/framework/toolbox");
-loader.lazyRequireGetter(this, "openContentLink", "devtools/client/shared/link", true);
+loader.lazyRequireGetter(
+  this,
+  "openContentLink",
+  "devtools/client/shared/link",
+  true
+);
 
 const DBG_STRINGS_URI = "devtools/client/locales/debugger.properties";
 const L10N = new LocalizationHelper(DBG_STRINGS_URI);
 
 function DebuggerPanel(iframeWindow, toolbox) {
   this.panelWin = iframeWindow;
   this.panelWin.L10N = L10N;
   this.toolbox = toolbox;
@@ -27,52 +33,33 @@ DebuggerPanel.prototype = {
       store,
       selectors,
       client
     } = await this.panelWin.Debugger.bootstrap({
       threadClient: this.toolbox.threadClient,
       tabTarget: this.toolbox.target,
       debuggerClient: this.toolbox.target.client,
       sourceMaps: this.toolbox.sourceMapService,
-      toolboxActions: {
-        // Open a link in a new browser tab.
-        openLink: this.openLink.bind(this),
-        openWorkerToolbox: this.openWorkerToolbox.bind(this),
-        openElementInInspector: async function(grip) {
-          await this.toolbox.initInspector();
-          const onSelectInspector = this.toolbox.selectTool("inspector");
-          const onGripNodeToFront = this.toolbox.walker.gripToNodeFront(grip);
-          const [
-            front,
-            inspector,
-          ] = await Promise.all([onGripNodeToFront, onSelectInspector]);
-
-          const onInspectorUpdated = inspector.once("inspector-updated");
-          const onNodeFrontSet = this.toolbox.selection
-            .setNodeFront(front, { reason: "debugger" });
-
-          return Promise.all([onNodeFrontSet, onInspectorUpdated]);
-        }.bind(this),
-        openConsoleAndEvaluate: async function(input) {
-          const webconsolePanel = await this.toolbox.selectTool("webconsole");
-          const jsterm = webconsolePanel.hud.jsterm;
-          jsterm.execute(input);
-        }.bind(this),
-        onReload: this.onReload.bind(this)
-      }
+      panel: this
     });
 
     this._actions = actions;
     this._store = store;
     this._selectors = selectors;
     this._client = client;
     this.isReady = true;
 
-    this.panelWin.document.addEventListener("drag:start", this.toolbox.toggleDragging);
-    this.panelWin.document.addEventListener("drag:end", this.toolbox.toggleDragging);
+    this.panelWin.document.addEventListener(
+      "drag:start",
+      this.toolbox.toggleDragging
+    );
+    this.panelWin.document.addEventListener(
+      "drag:end",
+      this.toolbox.toggleDragging
+    );
 
     return this;
   },
 
   getVarsForTests() {
     return {
       store: this._store,
       selectors: this._selectors,
@@ -88,18 +75,41 @@ DebuggerPanel.prototype = {
   openLink: function(url) {
     openContentLink(url);
   },
 
   openWorkerToolbox: function(workerTargetFront) {
     return gDevToolsBrowser.openWorkerToolbox(workerTargetFront, "jsdebugger");
   },
 
+  openConsoleAndEvaluate: async function(input) {
+    const webconsolePanel = await this.toolbox.selectTool("webconsole");
+    const jsterm = webconsolePanel.hud.jsterm;
+    jsterm.execute(input);
+  },
+
+  openElementInInspector: async function(grip) {
+    await this.toolbox.initInspector();
+    const onSelectInspector = this.toolbox.selectTool("inspector");
+    const onGripNodeToFront = this.toolbox.walker.gripToNodeFront(grip);
+    const [front, inspector] = await Promise.all([
+      onGripNodeToFront,
+      onSelectInspector
+    ]);
+
+    const onInspectorUpdated = inspector.once("inspector-updated");
+    const onNodeFrontSet = this.toolbox.selection.setNodeFront(front, {
+      reason: "debugger"
+    });
+
+    return Promise.all([onNodeFrontSet, onInspectorUpdated]);
+  },
+
   getFrames: function() {
-    let frames = this._selectors.getFrames(this._getState());
+    const frames = this._selectors.getFrames(this._getState());
 
     // Frames is null when the debugger is not paused.
     if (!frames) {
       return {
         frames: [],
         selected: -1
       };
     }
@@ -125,19 +135,15 @@ DebuggerPanel.prototype = {
   selectSource(url, line) {
     this._actions.selectSourceURL(url, { line });
   },
 
   getSource(sourceURL) {
     return this._selectors.getSourceByURL(this._getState(), sourceURL);
   },
 
-  onReload: function() {
-    this.emit("reloaded");
-  },
-
   destroy: function() {
     this.panelWin.Debugger.destroy();
     this.emit("destroyed");
   }
 };
 
 exports.DebuggerPanel = DebuggerPanel;
--- a/devtools/client/debugger/new/src/actions/ast/setPausePoints.js
+++ b/devtools/client/debugger/new/src/actions/ast/setPausePoints.js
@@ -1,15 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
-import { getSourceFromId } from "../../selectors";
+import { getSourceFromId, getSourceActors } from "../../selectors";
 import * as parser from "../../workers/parser";
 import { isGenerated } from "../../utils/source";
 import { convertToList } from "../../utils/pause/pausePoints";
 import { features } from "../../utils/prefs";
 import { getGeneratedLocation } from "../../utils/source-maps";
 
 import type { SourceId } from "../../types";
 import type { ThunkArgs, Action } from "../types";
@@ -57,17 +57,19 @@ export function setPausePoints(sourceId:
     if (source.isWasm) {
       return;
     }
 
     let pausePoints = await parser.getPausePoints(sourceId);
 
     if (isGenerated(source)) {
       const compressed = compressPausePoints(pausePoints);
-      await client.setPausePoints(sourceId, compressed);
+      for (const sourceActor of getSourceActors(getState(), sourceId)) {
+        await client.setPausePoints(sourceActor, compressed);
+      }
     }
 
     pausePoints = await mapLocations(
       pausePoints,
       getState(),
       source,
       sourceMaps
     );
--- a/devtools/client/debugger/new/src/actions/breakpoints/addBreakpoint.js
+++ b/devtools/client/debugger/new/src/actions/breakpoints/addBreakpoint.js
@@ -6,21 +6,26 @@
 
 import { isOriginalId } from "devtools-source-map";
 import {
   locationMoved,
   breakpointExists,
   assertBreakpoint,
   createBreakpoint,
   getASTLocation,
-  assertLocation
+  assertLocation,
+  makeBreakpointId,
+  makeSourceActorLocation
 } from "../../utils/breakpoint";
 import { PROMISE } from "../utils/middleware/promise";
-import { getSource, getSymbols } from "../../selectors";
-
+import {
+  getSource,
+  getSourceActors,
+  getSymbols,
+} from "../../selectors";
 import { getGeneratedLocation } from "../../utils/source-maps";
 import { getTextAtPosition } from "../../utils/source";
 import { recordEvent } from "../../utils/telemetry";
 
 import type {
   BreakpointOptions,
   Breakpoint,
   SourceLocation
@@ -45,44 +50,60 @@ async function addBreakpointPromise(getS
     state,
     source,
     location,
     sourceMaps
   );
 
   const generatedSource = getSource(state, generatedLocation.sourceId);
 
+  if (!generatedSource) {
+    throw new Error(
+      `Unable to find generated source: ${generatedLocation.sourceId}`
+    );
+  }
+
   assertLocation(location);
   assertLocation(generatedLocation);
 
   if (breakpointExists(state, location)) {
     const newBreakpoint = { ...breakpoint, location, generatedLocation };
     assertBreakpoint(newBreakpoint);
     return { breakpoint: newBreakpoint };
   }
 
-  const { id, actualLocation } = await client.setBreakpoint(
-    generatedLocation,
-    breakpoint.options,
-    isOriginalId(location.sourceId)
-  );
+  const sourceActors = getSourceActors(state, generatedSource.id);
+  const newGeneratedLocation = { ...generatedLocation };
 
-  const newGeneratedLocation = actualLocation || generatedLocation;
+  for (const sourceActor of sourceActors) {
+    const sourceActorLocation = makeSourceActorLocation(
+      sourceActor,
+      generatedLocation
+    );
+    const { actualLocation } = await client.setBreakpoint(
+      sourceActorLocation,
+      breakpoint.options,
+      isOriginalId(location.sourceId)
+    );
+    newGeneratedLocation.line = actualLocation.line;
+    newGeneratedLocation.column = actualLocation.column;
+  }
+
   const newLocation = await sourceMaps.getOriginalLocation(
     newGeneratedLocation
   );
 
   const symbols = getSymbols(getState(), source);
   const astLocation = await getASTLocation(source, symbols, newLocation);
 
   const originalText = getTextAtPosition(source, location);
-  const text = getTextAtPosition(generatedSource, actualLocation);
+  const text = getTextAtPosition(generatedSource, newGeneratedLocation);
 
   const newBreakpoint = {
-    id,
+    id: makeBreakpointId(generatedLocation),
     disabled: false,
     loading: false,
     options: breakpoint.options,
     location: newLocation,
     astLocation,
     generatedLocation: newGeneratedLocation,
     text,
     originalText
--- a/devtools/client/debugger/new/src/actions/breakpoints/index.js
+++ b/devtools/client/debugger/new/src/actions/breakpoints/index.js
@@ -12,19 +12,24 @@
 import { PROMISE } from "../utils/middleware/promise";
 import {
   getBreakpoint,
   getBreakpointsList,
   getXHRBreakpoints,
   getSelectedSource,
   getBreakpointAtLocation,
   getConditionalPanelLocation,
-  getBreakpointsForSource
+  getBreakpointsForSource,
+  getSourceActors
 } from "../../selectors";
-import { assertBreakpoint, createXHRBreakpoint } from "../../utils/breakpoint";
+import {
+  assertBreakpoint,
+  createXHRBreakpoint,
+  makeSourceActorLocation
+} from "../../utils/breakpoint";
 import {
   addBreakpoint,
   addHiddenBreakpoint,
   enableBreakpoint
 } from "./addBreakpoint";
 import remapLocations from "./remapLocations";
 import { syncBreakpoint } from "./syncBreakpoint";
 import { closeConditionalPanel } from "../ui";
@@ -38,16 +43,32 @@ import type {
   BreakpointOptions,
   Source,
   SourceLocation,
   XHRBreakpoint
 } from "../../types";
 
 import { recordEvent } from "../../utils/telemetry";
 
+async function removeBreakpointsPromise(client, state, breakpoint) {
+  const sourceActors = getSourceActors(
+    state,
+    breakpoint.generatedLocation.sourceId
+  );
+  for (const sourceActor of sourceActors) {
+    const sourceActorLocation = makeSourceActorLocation(
+      sourceActor,
+      breakpoint.generatedLocation
+    );
+    if (client.getBreakpointByLocation(sourceActorLocation)) {
+      await client.removeBreakpoint(sourceActorLocation);
+    }
+  }
+}
+
 /**
  * Remove a single breakpoint
  *
  * @memberof actions/breakpoints
  * @static
  */
 export function removeBreakpoint(breakpoint: Breakpoint) {
   return ({ dispatch, getState, client }: ThunkArgs) => {
@@ -65,34 +86,35 @@ export function removeBreakpoint(breakpo
         ({ type: "REMOVE_BREAKPOINT", breakpoint, status: "done" }: Action)
       );
     }
 
     return dispatch({
       type: "REMOVE_BREAKPOINT",
       breakpoint,
       disabled: false,
-      [PROMISE]: client.removeBreakpoint(breakpoint.generatedLocation)
+      [PROMISE]: removeBreakpointsPromise(client, getState(), breakpoint)
     });
   };
 }
 
 /**
  * Disable a single breakpoint
  *
  * @memberof actions/breakpoints
  * @static
  */
 export function disableBreakpoint(breakpoint: Breakpoint) {
   return async ({ dispatch, getState, client }: ThunkArgs) => {
     if (breakpoint.loading) {
       return;
     }
 
-    await client.removeBreakpoint(breakpoint.generatedLocation);
+    await removeBreakpointsPromise(client, getState(), breakpoint);
+
     const newBreakpoint: Breakpoint = { ...breakpoint, disabled: true };
 
     return dispatch(
       ({ type: "DISABLE_BREAKPOINT", breakpoint: newBreakpoint }: Action)
     );
   };
 }
 
@@ -139,17 +161,17 @@ export function enableBreakpointsInSourc
 export function toggleAllBreakpoints(shouldDisableBreakpoints: boolean) {
   return async ({ dispatch, getState, client }: ThunkArgs) => {
     const breakpoints = getBreakpointsList(getState());
 
     const modifiedBreakpoints = [];
 
     for (const breakpoint of breakpoints) {
       if (shouldDisableBreakpoints) {
-        await client.removeBreakpoint(breakpoint.generatedLocation);
+        await removeBreakpointsPromise(client, getState(), breakpoint);
         const newBreakpoint: Breakpoint = { ...breakpoint, disabled: true };
         modifiedBreakpoints.push(newBreakpoint);
       } else {
         const newBreakpoint: Breakpoint = { ...breakpoint, disabled: false };
         modifiedBreakpoints.push(newBreakpoint);
       }
     }
 
@@ -277,17 +299,29 @@ export function setBreakpointOptions(
     if (bp.loading) {
       return;
     }
 
     if (bp.disabled) {
       await dispatch(enableBreakpoint(bp));
     }
 
-    await client.setBreakpointOptions(bp.id, location, options);
+    const sourceActors = getSourceActors(
+      getState(),
+      bp.generatedLocation.sourceId
+    );
+    for (const sourceActor of sourceActors) {
+      const sourceActorLocation = makeSourceActorLocation(
+        sourceActor,
+        bp.generatedLocation
+      );
+      if (client.getBreakpointByLocation(sourceActorLocation)) {
+        await client.setBreakpointOptions(sourceActorLocation, options);
+      }
+    }
 
     const newBreakpoint = { ...bp, disabled: false, options };
 
     assertBreakpoint(newBreakpoint);
 
     return dispatch(
       ({
         type: "SET_BREAKPOINT_OPTIONS",
--- a/devtools/client/debugger/new/src/actions/breakpoints/syncBreakpoint.js
+++ b/devtools/client/debugger/new/src/actions/breakpoints/syncBreakpoint.js
@@ -3,23 +3,24 @@
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 import {
   locationMoved,
   createBreakpoint,
   assertBreakpoint,
   assertPendingBreakpoint,
-  findScopeByName
+  findScopeByName,
+  makeSourceActorLocation
 } from "../../utils/breakpoint";
 
 import { getGeneratedLocation } from "../../utils/source-maps";
 import { getTextAtPosition } from "../../utils/source";
 import { originalToGeneratedId, isOriginalId } from "devtools-source-map";
-import { getSource } from "../../selectors";
+import { getSource, getSourceActors } from "../../selectors";
 import type { ThunkArgs, Action } from "../types";
 
 import type {
   SourceLocation,
   ASTLocation,
   PendingBreakpoint,
   SourceId,
   Breakpoint
@@ -44,28 +45,26 @@ async function makeScopedLocation(
     line,
     column: location.column,
     sourceUrl: source.url,
     sourceId: source.id
   };
 }
 
 function createSyncData(
-  id: SourceId,
   pendingBreakpoint: PendingBreakpoint,
   location: SourceLocation,
   generatedLocation: SourceLocation,
   previousLocation: SourceLocation,
   text: string,
   originalText: string
 ): BreakpointSyncData {
   const overrides = {
     ...pendingBreakpoint,
     generatedLocation,
-    id,
     text,
     originalText
   };
   const breakpoint = createBreakpoint(location, overrides);
 
   assertBreakpoint(breakpoint);
   return { breakpoint, previousLocation };
 }
@@ -116,68 +115,95 @@ export async function syncBreakpointProm
     sourceId: generatedSourceId
   };
 
   const isSameLocation = !locationMoved(
     generatedLocation,
     scopedGeneratedLocation
   );
 
-  const existingClient = client.getBreakpointByLocation(generatedLocation);
+  const sourceActors = getSourceActors(getState(), sourceId);
 
   /** ******* CASE 1: No server change ***********/
   // early return if breakpoint is disabled or we are in the sameLocation
-  // send update only to redux
-  if (pendingBreakpoint.disabled || (existingClient && isSameLocation)) {
-    const id = pendingBreakpoint.disabled ? "" : existingClient.id;
+  if (pendingBreakpoint.disabled || isSameLocation) {
+    // Make sure the breakpoint is installed on all source actors.
+    if (!pendingBreakpoint.disabled) {
+      for (const sourceActor of sourceActors) {
+        const sourceActorLocation = makeSourceActorLocation(
+          sourceActor,
+          generatedLocation
+        );
+        if (!client.getBreakpointByLocation(sourceActorLocation)) {
+          await client.setBreakpoint(
+            sourceActorLocation,
+            pendingBreakpoint.options,
+            isOriginalId(sourceId)
+          );
+        }
+      }
+    }
+
     const originalText = getTextAtPosition(source, previousLocation);
     const text = getTextAtPosition(generatedSource, generatedLocation);
 
     return createSyncData(
-      id,
       pendingBreakpoint,
       scopedLocation,
       scopedGeneratedLocation,
       previousLocation,
       text,
       originalText
     );
   }
 
   // clear server breakpoints if they exist and we have moved
-  if (existingClient) {
-    await client.removeBreakpoint(generatedLocation);
+  for (const sourceActor of sourceActors) {
+    const sourceActorLocation = makeSourceActorLocation(
+      sourceActor,
+      generatedLocation
+    );
+    if (client.getBreakpointByLocation(sourceActorLocation)) {
+      await client.removeBreakpoint(sourceActorLocation);
+    }
   }
 
   /** ******* Case 2: Add New Breakpoint ***********/
   // If we are not disabled, set the breakpoint on the server and get
   // that info so we can set it on our breakpoints.
 
   if (!scopedGeneratedLocation.line) {
     return { previousLocation, breakpoint: null };
   }
 
-  const { id, actualLocation } = await client.setBreakpoint(
-    scopedGeneratedLocation,
-    isOriginalId(sourceId),
-    pendingBreakpoint.options
-  );
+  const newGeneratedLocation = { ...scopedGeneratedLocation };
+  for (const sourceActor of sourceActors) {
+    const sourceActorLocation = makeSourceActorLocation(
+      sourceActor,
+      scopedGeneratedLocation
+    );
+    const { actualLocation } = await client.setBreakpoint(
+      sourceActorLocation,
+      pendingBreakpoint.options,
+      isOriginalId(sourceId)
+    );
+    newGeneratedLocation.line = actualLocation.line;
+    newGeneratedLocation.column = actualLocation.column;
+  }
 
   // the breakpoint might have slid server side, so we want to get the location
   // based on the server's return value
-  const newGeneratedLocation = actualLocation;
   const newLocation = await sourceMaps.getOriginalLocation(
     newGeneratedLocation
   );
 
   const originalText = getTextAtPosition(source, newLocation);
   const text = getTextAtPosition(generatedSource, newGeneratedLocation);
 
   return createSyncData(
-    id,
     pendingBreakpoint,
     newLocation,
     newGeneratedLocation,
     previousLocation,
     text,
     originalText
   );
 }
--- a/devtools/client/debugger/new/src/actions/breakpoints/tests/__snapshots__/breakpoints.spec.js.snap
+++ b/devtools/client/debugger/new/src/actions/breakpoints/tests/__snapshots__/breakpoints.spec.js.snap
@@ -8,16 +8,17 @@ Object {
     "offset": Object {
       "line": 7,
       "sourceId": "a",
       "sourceUrl": "http://localhost:8000/examples/a",
     },
   },
   "disabled": false,
   "generatedLocation": Object {
+    "column": undefined,
     "line": 7,
     "sourceId": "a",
     "sourceUrl": "http://localhost:8000/examples/a",
   },
   "id": "a:5:",
   "loading": false,
   "location": Object {
     "line": 7,
@@ -45,21 +46,22 @@ Array [
           "offset": Object {
             "line": 2,
             "sourceId": "a",
             "sourceUrl": "http://localhost:8000/examples/a",
           },
         },
         "disabled": false,
         "generatedLocation": Object {
+          "column": undefined,
           "line": 2,
           "sourceId": "a",
           "sourceUrl": "http://localhost:8000/examples/a",
         },
-        "id": "hi",
+        "id": "a:2:",
         "loading": false,
         "location": Object {
           "line": 2,
           "sourceId": "a",
           "sourceUrl": "http://localhost:8000/examples/a",
         },
         "options": Object {
           "condition": null,
@@ -99,21 +101,22 @@ Object {
     "offset": Object {
       "line": 1,
       "sourceId": "a.js",
       "sourceUrl": "http://localhost:8000/examples/a.js",
     },
   },
   "disabled": false,
   "generatedLocation": Object {
+    "column": undefined,
     "line": 1,
     "sourceId": "a.js",
     "sourceUrl": "http://localhost:8000/examples/a.js",
   },
-  "id": "hi",
+  "id": "a.js:1:",
   "loading": false,
   "location": Object {
     "column": 0,
     "line": 1,
     "sourceId": "a.js/originalSource-d6d70368d5c252598541e693a7ad6c27",
     "sourceUrl": "http://localhost:8000/examples/a.js:formatted",
   },
   "options": Object {
@@ -137,21 +140,22 @@ Array [
           "offset": Object {
             "line": 5,
             "sourceId": "a",
             "sourceUrl": "http://localhost:8000/examples/a",
           },
         },
         "disabled": true,
         "generatedLocation": Object {
+          "column": undefined,
           "line": 5,
           "sourceId": "a",
           "sourceUrl": "http://localhost:8000/examples/a",
         },
-        "id": "hi",
+        "id": "a:5:",
         "loading": false,
         "location": Object {
           "line": 5,
           "sourceId": "a",
           "sourceUrl": "http://localhost:8000/examples/a",
         },
         "options": Object {
           "condition": null,
--- a/devtools/client/debugger/new/src/actions/breakpoints/tests/__snapshots__/syncing.spec.js.snap
+++ b/devtools/client/debugger/new/src/actions/breakpoints/tests/__snapshots__/syncing.spec.js.snap
@@ -1,27 +1,28 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
 exports[`loading the debugger loads the initial breakpoint state 1`] = `
 Object {
   "breakpoint": Object {
     "astLocation": Object {
+      "index": 0,
       "name": undefined,
       "offset": Object {
         "line": 3,
       },
     },
     "disabled": false,
     "generatedLocation": Object {
       "column": undefined,
       "line": 3,
       "sourceId": "gen.js",
       "sourceUrl": "http://localhost:8000/gen.js",
     },
-    "id": "foo",
+    "id": "magic.js:3:",
     "loading": false,
     "location": Object {
       "column": undefined,
       "line": 3,
       "sourceId": "magic.js",
       "sourceUrl": "http://localhost:8000/examples/magic.js",
     },
     "options": Object {
@@ -40,29 +41,30 @@ Object {
   },
 }
 `;
 
 exports[`loading the debugger loads the initial breakpoint state with a changed file 1`] = `
 Object {
   "breakpoint": Object {
     "astLocation": Object {
+      "index": 0,
       "name": undefined,
       "offset": Object {
         "line": 3,
       },
     },
     "disabled": false,
     "generatedLocation": Object {
       "column": undefined,
       "line": 3,
       "sourceId": "gen.js",
       "sourceUrl": "http://localhost:8000/gen.js",
     },
-    "id": "foo",
+    "id": "magic.js:12:",
     "loading": false,
     "location": Object {
       "column": undefined,
       "line": 12,
       "sourceId": "magic.js",
       "sourceUrl": "http://localhost:8000/examples/magic.js",
     },
     "options": Object {
@@ -81,16 +83,17 @@ Object {
   },
 }
 `;
 
 exports[`reloading debuggee syncs with changed source and an existing disabled BP 1`] = `
 Object {
   "http://localhost:8000/examples/magic.js:3:": Object {
     "astLocation": Object {
+      "index": 0,
       "name": undefined,
       "offset": Object {
         "line": 3,
       },
     },
     "disabled": true,
     "generatedLocation": Object {
       "column": undefined,
@@ -110,29 +113,30 @@ Object {
   },
 }
 `;
 
 exports[`reloading debuggee syncs with unchanged source with an existing BP 1`] = `
 Object {
   "breakpoint": Object {
     "astLocation": Object {
+      "index": 0,
       "name": undefined,
       "offset": Object {
         "line": 3,
       },
     },
     "disabled": false,
     "generatedLocation": Object {
       "column": undefined,
       "line": 3,
       "sourceId": "gen.js",
       "sourceUrl": "http://localhost:8000/gen.js",
     },
-    "id": "foo",
+    "id": "magic.js:3:",
     "loading": false,
     "location": Object {
       "column": undefined,
       "line": 3,
       "sourceId": "magic.js",
       "sourceUrl": "http://localhost:8000/examples/magic.js",
     },
     "options": Object {
@@ -151,29 +155,30 @@ Object {
   },
 }
 `;
 
 exports[`reloading debuggee updates a corresponding breakpoint for a changed source 1`] = `
 Object {
   "breakpoint": Object {
     "astLocation": Object {
+      "index": 0,
       "name": undefined,
       "offset": Object {
         "line": 3,
       },
     },
     "disabled": false,
     "generatedLocation": Object {
       "column": undefined,
       "line": 5,
       "sourceId": "gen.js",
       "sourceUrl": "http://localhost:8000/gen.js",
     },
-    "id": "gen.js:5:",
+    "id": "magic.js:3:",
     "loading": false,
     "location": Object {
       "column": undefined,
       "line": 3,
       "sourceId": "magic.js",
       "sourceUrl": "http://localhost:8000/magic.js",
     },
     "options": Object {
--- a/devtools/client/debugger/new/src/actions/breakpoints/tests/breakpoints.spec.js
+++ b/devtools/client/debugger/new/src/actions/breakpoints/tests/breakpoints.spec.js
@@ -1,12 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
+// @flow
+
 import {
   createStore,
   selectors,
   actions,
   makeSource,
   getTelemetryEvents
 } from "../../../utils/test-head";
 
@@ -19,99 +21,104 @@ describe("breakpoints", () => {
   it("should add a breakpoint", async () => {
     const { dispatch, getState } = createStore(simpleMockThreadClient);
     const loc1 = {
       sourceId: "a",
       line: 2,
       sourceUrl: "http://localhost:8000/examples/a"
     };
 
-    await dispatch(actions.newSource(makeSource("a")));
-    await dispatch(actions.loadSourceText(makeSource("a")));
+    const csr = makeSource("a");
+    await dispatch(actions.newSource(csr));
+    await dispatch(actions.loadSourceText(csr.source));
     await dispatch(actions.addBreakpoint(loc1));
 
     expect(selectors.getBreakpointCount(getState())).toEqual(1);
     const bp = selectors.getBreakpoint(getState(), loc1);
-    expect(bp.location).toEqual(loc1);
+    expect(bp && bp.location).toEqual(loc1);
     expect(getTelemetryEvents("add_breakpoint")).toHaveLength(1);
 
     const bpSources = selectors.getBreakpointSources(getState());
     expect(bpSources).toMatchSnapshot();
   });
 
   it("should not show a breakpoint that does not have text", async () => {
     const { dispatch, getState } = createStore(simpleMockThreadClient);
     const loc1 = {
       sourceId: "a",
       line: 5,
       sourceUrl: "http://localhost:8000/examples/a"
     };
-    await dispatch(actions.newSource(makeSource("a")));
-    await dispatch(actions.loadSourceText(makeSource("a")));
+    const csr = makeSource("a");
+    await dispatch(actions.newSource(csr));
+    await dispatch(actions.loadSourceText(csr.source));
     await dispatch(actions.addBreakpoint(loc1));
 
     expect(selectors.getBreakpointCount(getState())).toEqual(1);
     const bp = selectors.getBreakpoint(getState(), loc1);
-    expect(bp.location).toEqual(loc1);
+    expect(bp && bp.location).toEqual(loc1);
     expect(selectors.getBreakpointSources(getState())).toMatchSnapshot();
   });
 
   it("should show a disabled breakpoint that does not have text", async () => {
     const { dispatch, getState } = createStore(simpleMockThreadClient);
     const loc1 = {
       sourceId: "a",
       line: 5,
       sourceUrl: "http://localhost:8000/examples/a"
     };
-    await dispatch(actions.newSource(makeSource("a")));
-    await dispatch(actions.loadSourceText(makeSource("a")));
+    const csr = makeSource("a");
+    await dispatch(actions.newSource(csr));
+    await dispatch(actions.loadSourceText(csr.source));
     const { breakpoint } = await dispatch(actions.addBreakpoint(loc1));
     await dispatch(actions.disableBreakpoint(breakpoint));
 
     expect(selectors.getBreakpointCount(getState())).toEqual(1);
     const bp = selectors.getBreakpoint(getState(), loc1);
-    expect(bp.location).toEqual(loc1);
+    expect(bp && bp.location).toEqual(loc1);
     expect(selectors.getBreakpointSources(getState())).toMatchSnapshot();
   });
 
   it("should not re-add a breakpoint", async () => {
     const { dispatch, getState } = createStore(simpleMockThreadClient);
     const loc1 = {
       sourceId: "a",
       line: 5,
       sourceUrl: "http://localhost:8000/examples/a"
     };
 
-    await dispatch(actions.newSource(makeSource("a")));
-    await dispatch(actions.loadSourceText(makeSource("a")));
+    const csr = makeSource("a");
+    await dispatch(actions.newSource(csr));
+    await dispatch(actions.loadSourceText(csr.source));
 
     await dispatch(actions.addBreakpoint(loc1));
     expect(selectors.getBreakpointCount(getState())).toEqual(1);
     const bp = selectors.getBreakpoint(getState(), loc1);
-    expect(bp.location).toEqual(loc1);
+    expect(bp && bp.location).toEqual(loc1);
 
     await dispatch(actions.addBreakpoint(loc1));
     expect(selectors.getBreakpointCount(getState())).toEqual(1);
   });
 
-  describe("adding a breakpoint to an invalid location", async () => {
+  describe("adding a breakpoint to an invalid location", () => {
     it("adds only one breakpoint with a corrected location", async () => {
       const invalidLocation = {
         sourceId: "a",
         line: 5,
         sourceUrl: "http://localhost:8000/examples/a"
       };
       const {
         correctedThreadClient,
         correctedLocation
       } = simulateCorrectThreadClient(2, invalidLocation);
       const { dispatch, getState } = createStore(correctedThreadClient);
 
-      await dispatch(actions.newSource(makeSource("a")));
-      await dispatch(actions.loadSourceText(makeSource("a")));
+      const csr = makeSource("a");
+      await dispatch(actions.newSource(csr));
+      await dispatch(actions.loadSourceText(csr.source));
 
       await dispatch(actions.addBreakpoint(invalidLocation));
       const state = getState();
       expect(selectors.getBreakpointCount(state)).toEqual(1);
       const bp = selectors.getBreakpoint(state, correctedLocation);
       expect(bp).toMatchSnapshot();
     });
   });
@@ -126,28 +133,32 @@ describe("breakpoints", () => {
     };
 
     const loc2 = {
       sourceId: "b",
       line: 6,
       sourceUrl: "http://localhost:8000/examples/b"
     };
 
-    await dispatch(actions.newSource(makeSource("a")));
-    await dispatch(actions.loadSourceText(makeSource("a")));
+    const aCSR = makeSource("a");
+    await dispatch(actions.newSource(aCSR));
+    await dispatch(actions.loadSourceText(aCSR.source));
 
-    await dispatch(actions.newSource(makeSource("b")));
-    await dispatch(actions.loadSourceText(makeSource("b")));
+    const bCSR = makeSource("b");
+    await dispatch(actions.newSource(bCSR));
+    await dispatch(actions.loadSourceText(bCSR.source));
 
     await dispatch(actions.addBreakpoint(loc1));
     await dispatch(actions.addBreakpoint(loc2));
 
-    await dispatch(
-      actions.removeBreakpoint(selectors.getBreakpoint(getState(), loc1))
-    );
+    const bp = selectors.getBreakpoint(getState(), loc1);
+    if (!bp) {
+      throw new Error("no bp");
+    }
+    await dispatch(actions.removeBreakpoint(bp));
 
     expect(selectors.getBreakpointCount(getState())).toEqual(1);
   });
 
   it("should disable a breakpoint", async () => {
     const { dispatch, getState } = createStore(simpleMockThreadClient);
 
     const loc1 = {
@@ -157,49 +168,55 @@ describe("breakpoints", () => {
     };
 
     const loc2 = {
       sourceId: "b",
       line: 6,
       sourceUrl: "http://localhost:8000/examples/b"
     };
 
-    await dispatch(actions.newSource(makeSource("a")));
-    await dispatch(actions.loadSourceText(makeSource("a")));
+    const aCSR = makeSource("a");
+    await dispatch(actions.newSource(aCSR));
+    await dispatch(actions.loadSourceText(aCSR.source));
 
-    await dispatch(actions.newSource(makeSource("b")));
-    await dispatch(actions.loadSourceText(makeSource("b")));
+    const bCSR = makeSource("b");
+    await dispatch(actions.newSource(bCSR));
+    await dispatch(actions.loadSourceText(bCSR.source));
 
     const { breakpoint } = await dispatch(actions.addBreakpoint(loc1));
     await dispatch(actions.addBreakpoint(loc2));
 
     await dispatch(actions.disableBreakpoint(breakpoint));
 
-    expect(selectors.getBreakpoint(getState(), loc1).disabled).toBe(true);
+    const bp = selectors.getBreakpoint(getState(), loc1);
+    expect(bp && bp.disabled).toBe(true);
   });
 
   it("should enable breakpoint", async () => {
     const { dispatch, getState } = createStore(simpleMockThreadClient);
     const loc = {
       sourceId: "a",
       line: 5,
       sourceUrl: "http://localhost:8000/examples/a"
     };
 
-    await dispatch(actions.newSource(makeSource("a")));
-    await dispatch(actions.loadSourceText(makeSource("a")));
+    const aCSR = makeSource("a");
+    await dispatch(actions.newSource(aCSR));
+    await dispatch(actions.loadSourceText(aCSR.source));
 
     const { breakpoint } = await dispatch(actions.addBreakpoint(loc));
     await dispatch(actions.disableBreakpoint(breakpoint));
 
-    expect(selectors.getBreakpoint(getState(), loc).disabled).toBe(true);
+    let bp = selectors.getBreakpoint(getState(), loc);
+    expect(bp && bp.disabled).toBe(true);
 
     await dispatch(actions.enableBreakpoint(breakpoint));
 
-    expect(selectors.getBreakpoint(getState(), loc).disabled).toBe(false);
+    bp = selectors.getBreakpoint(getState(), loc);
+    expect(bp && !bp.disabled).toBe(true);
   });
 
   it("should toggle all the breakpoints", async () => {
     const { dispatch, getState } = createStore(simpleMockThreadClient);
 
     const loc1 = {
       sourceId: "a",
       line: 5,
@@ -207,143 +224,161 @@ describe("breakpoints", () => {
     };
 
     const loc2 = {
       sourceId: "b",
       line: 6,
       sourceUrl: "http://localhost:8000/examples/b"
     };
 
-    await dispatch(actions.newSource(makeSource("a")));
-    await dispatch(actions.loadSourceText(makeSource("a")));
+    const aCSR = makeSource("a");
+    await dispatch(actions.newSource(aCSR));
+    await dispatch(actions.loadSourceText(aCSR.source));
 
-    await dispatch(actions.newSource(makeSource("b")));
-    await dispatch(actions.loadSourceText(makeSource("b")));
+    const bCSR = makeSource("b");
+    await dispatch(actions.newSource(bCSR));
+    await dispatch(actions.loadSourceText(bCSR.source));
 
     await dispatch(actions.addBreakpoint(loc1));
     await dispatch(actions.addBreakpoint(loc2));
 
     await dispatch(actions.toggleAllBreakpoints(true));
 
-    expect(selectors.getBreakpoint(getState(), loc1).disabled).toBe(true);
-    expect(selectors.getBreakpoint(getState(), loc2).disabled).toBe(true);
+    let bp1 = selectors.getBreakpoint(getState(), loc1);
+    let bp2 = selectors.getBreakpoint(getState(), loc2);
+    expect(bp1 && bp1.disabled).toBe(true);
+    expect(bp2 && bp2.disabled).toBe(true);
 
-    await dispatch(actions.toggleAllBreakpoints());
+    await dispatch(actions.toggleAllBreakpoints(false));
 
-    expect(selectors.getBreakpoint(getState(), loc1).disabled).toBe(false);
-    expect(selectors.getBreakpoint(getState(), loc2).disabled).toBe(false);
+    bp1 = selectors.getBreakpoint(getState(), loc1);
+    bp2 = selectors.getBreakpoint(getState(), loc2);
+    expect(bp1 && bp1.disabled).toBe(false);
+    expect(bp2 && bp2.disabled).toBe(false);
   });
 
   it("should toggle a breakpoint at a location", async () => {
     const location = { sourceId: "foo1", line: 5 };
     const getBp = () => selectors.getBreakpoint(getState(), location);
 
     const { dispatch, getState } = createStore(simpleMockThreadClient);
 
-    await dispatch(actions.newSource(makeSource("foo1")));
-    await dispatch(actions.loadSourceText(makeSource("foo1")));
+    const csr = makeSource("foo1");
+    await dispatch(actions.newSource(csr));
+    await dispatch(actions.loadSourceText(csr.source));
 
     await dispatch(actions.selectLocation({ sourceId: "foo1", line: 1 }));
 
     await dispatch(actions.toggleBreakpointAtLine(5));
-    expect(getBp().disabled).toBe(false);
+    const bp = getBp();
+    expect(bp && !bp.disabled).toBe(true);
 
     await dispatch(actions.toggleBreakpointAtLine(5));
     expect(getBp()).toBe(undefined);
   });
 
   it("should disable/enable a breakpoint at a location", async () => {
     const location = { sourceId: "foo1", line: 5 };
     const getBp = () => selectors.getBreakpoint(getState(), location);
 
     const { dispatch, getState } = createStore(simpleMockThreadClient);
 
-    await dispatch(actions.newSource(makeSource("foo1")));
-    await dispatch(actions.loadSourceText(makeSource("foo1")));
+    const csr = makeSource("foo1");
+    await dispatch(actions.newSource(csr));
+    await dispatch(actions.loadSourceText(csr.source));
 
     await dispatch(actions.selectLocation({ sourceId: "foo1", line: 1 }));
 
     await dispatch(actions.toggleBreakpointAtLine(5));
-    expect(getBp().disabled).toBe(false);
-    await dispatch(actions.toggleDisabledBreakpoint(getBp()));
-    expect(getBp().disabled).toBe(true);
+    let bp = getBp();
+    expect(bp && !bp.disabled).toBe(true);
+    bp = getBp();
+    if (!bp) {
+      throw new Error("no bp");
+    }
+    await dispatch(actions.toggleDisabledBreakpoint(bp));
+    bp = getBp();
+    expect(bp && bp.disabled).toBe(true);
   });
 
   it("should set the breakpoint condition", async () => {
     const { dispatch, getState } = createStore(simpleMockThreadClient);
 
     const loc = {
       sourceId: "a",
       line: 5,
       sourceUrl: "http://localhost:8000/examples/a"
     };
 
-    await dispatch(actions.newSource(makeSource("a")));
-    await dispatch(actions.loadSourceText(makeSource("a")));
+    const csr = makeSource("a");
+    await dispatch(actions.newSource(csr));
+    await dispatch(actions.loadSourceText(csr.source));
 
     await dispatch(actions.addBreakpoint(loc));
 
-    expect(selectors.getBreakpoint(getState(), loc).options.condition).toBe(
-      null
-    );
+    let bp = selectors.getBreakpoint(getState(), loc);
+    expect(bp && bp.options.condition).toBe(null);
 
     await dispatch(
       actions.setBreakpointOptions(loc, {
         condition: "const foo = 0",
         getTextForLine: () => {}
       })
     );
 
-    expect(selectors.getBreakpoint(getState(), loc).options.condition).toBe(
-      "const foo = 0"
-    );
+    bp = selectors.getBreakpoint(getState(), loc);
+    expect(bp && bp.options.condition).toBe("const foo = 0");
   });
 
   it("should set the condition and enable a breakpoint", async () => {
     const { dispatch, getState } = createStore(simpleMockThreadClient);
 
     const loc = {
       sourceId: "a",
       line: 5,
       sourceUrl: "http://localhost:8000/examples/a"
     };
 
     await dispatch(actions.newSource(makeSource("a")));
     const { breakpoint } = await dispatch(actions.addBreakpoint(loc));
     await dispatch(actions.disableBreakpoint(breakpoint));
 
-    expect(selectors.getBreakpoint(getState(), loc).options.condition).toBe(
-      null
-    );
+    const bp = selectors.getBreakpoint(getState(), loc);
+    expect(bp && bp.options.condition).toBe(null);
 
     await dispatch(
       actions.setBreakpointOptions(loc, {
         condition: "const foo = 0",
         getTextForLine: () => {}
       })
     );
     const newBreakpoint = selectors.getBreakpoint(getState(), loc);
-    expect(newBreakpoint.disabled).toBe(false);
-    expect(newBreakpoint.options.condition).toBe("const foo = 0");
+    expect(newBreakpoint && !newBreakpoint.disabled).toBe(true);
+    expect(newBreakpoint && newBreakpoint.options.condition).toBe(
+      "const foo = 0"
+    );
   });
 
   it("should remap breakpoints on pretty print", async () => {
     const { dispatch, getState } = createStore(simpleMockThreadClient);
 
     const loc = {
       sourceId: "a.js",
       line: 1,
       sourceUrl: "http://localhost:8000/examples/a.js"
     };
 
-    const source = makeSource("a.js");
-    await dispatch(actions.newSource(source));
-    await dispatch(actions.loadSourceText(makeSource("a.js")));
+    const csr = makeSource("a.js");
+    await dispatch(actions.newSource(csr));
+    await dispatch(actions.loadSourceText(csr.source));
 
     await dispatch(actions.addBreakpoint(loc));
     await dispatch(actions.togglePrettyPrint("a.js"));
 
     const breakpoint = selectors.getBreakpointsList(getState())[0];
 
-    expect(breakpoint.location.sourceUrl.includes("formatted")).toBe(true);
+    expect(
+      breakpoint.location.sourceUrl &&
+        breakpoint.location.sourceUrl.includes("formatted")
+    ).toBe(true);
     expect(breakpoint).toMatchSnapshot();
   });
 });
--- a/devtools/client/debugger/new/src/actions/breakpoints/tests/syncing.spec.js
+++ b/devtools/client/debugger/new/src/actions/breakpoints/tests/syncing.spec.js
@@ -1,12 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
+// @flow
+
 jest.mock("../../../utils/source-maps", () => ({
   getGeneratedLocation: jest.fn()
 }));
 import { getGeneratedLocation } from "../../../utils/source-maps";
 
 jest.mock("../../../utils/prefs", () => ({
   prefs: {
     expressions: [],
@@ -20,33 +22,33 @@ jest.mock("../../../utils/prefs", () => 
 
 import {
   createStore,
   selectors,
   actions,
   makeSource
 } from "../../../utils/test-head";
 
-import { makeLocationId } from "../../../utils/breakpoint";
+import { makeBreakpointId } from "../../../utils/breakpoint";
 
 jest.mock("../../../utils/breakpoint/astBreakpointLocation", () => ({
   findScopeByName: jest.fn(),
   getASTLocation: jest.fn()
 }));
 
 // eslint-disable-next-line
 import { findScopeByName } from "../../../utils/breakpoint/astBreakpointLocation";
 
 import { syncBreakpointPromise } from "../../breakpoints/syncBreakpoint.js";
 
 function setBreakpoint(location, condition) {
   const actualLocation = { ...location, line: location.line };
 
   return Promise.resolve({
-    id: makeLocationId(location),
+    id: makeBreakpointId(location),
     actualLocation,
     condition
   });
 }
 
 const clientBreakpoint = {
   id: "foo",
   actualLocation: {
@@ -88,119 +90,131 @@ function pendingBreakpoint(overrides) {
       sourceUrl: "http://localhost:8000/gen.js",
       line: 3,
       column: undefined
     },
     astLocation: {
       name: undefined,
       offset: {
         line: 3
-      }
+      },
+      index: 0
     },
     condition: null,
     disabled: false,
     hidden: false,
+    loading: false,
+    options: {},
+    text: "",
     ...overrides
   };
 }
 
 function newGeneratedLocation(line) {
   return {
     sourceUrl: "http://localhost:8000/gen.js",
     sourceId: "gen.js",
     line,
     column: undefined
   };
 }
 
 describe("loading the debugger", () => {
   it("loads the initial breakpoint state", async () => {
-    getGeneratedLocation.mockImplementation(() => newGeneratedLocation(3));
+    (getGeneratedLocation: any).mockImplementation(() =>
+      newGeneratedLocation(3)
+    );
 
     const { dispatch, getState } = createStore(threadClient, {}, sourceMaps);
 
     // add a source without the breakpoints
     const reloadedSource = makeSource("magic.js");
     await dispatch(actions.newSource(reloadedSource));
 
     expect(selectors.getBreakpointCount(getState())).toEqual(0);
     // manually sync
     const update = await syncBreakpointPromise(
       getState,
       threadClient,
       sourceMaps,
-      reloadedSource.id,
+      reloadedSource.source.id,
       pendingBreakpoint()
     );
 
     expect(threadClient.removeBreakpoint.mock.calls).toHaveLength(0);
     expect(update).toMatchSnapshot();
   });
 
   it("loads the initial breakpoint state with a changed file", async () => {
     const location = { line: 9, column: 0 };
     const generated = 3;
-    getGeneratedLocation.mockImplementation(() =>
+    (getGeneratedLocation: any).mockImplementation(() =>
       newGeneratedLocation(generated)
     );
-    findScopeByName.mockImplementation(() => ({
+    (findScopeByName: any).mockImplementation(() => ({
       location: { start: location }
     }));
 
     const { dispatch, getState } = createStore(threadClient, {}, sourceMaps);
 
     // add a source without the breakpoints
     const reloadedSource = makeSource("magic.js");
     await dispatch(actions.newSource(reloadedSource));
 
     expect(selectors.getBreakpointCount(getState())).toEqual(0);
     // manually sync
     const update = await syncBreakpointPromise(
       getState,
       threadClient,
       sourceMaps,
-      reloadedSource.id,
+      reloadedSource.source.id,
       pendingBreakpoint()
     );
 
     expect(threadClient.removeBreakpoint.mock.calls).toHaveLength(0);
-    expect(update.breakpoint.location.line).toBe(location.line + generated);
+    expect(update && update.breakpoint && update.breakpoint.location.line).toBe(
+      location.line + generated
+    );
     expect(update).toMatchSnapshot();
   });
 });
 
 describe("reloading debuggee", () => {
   beforeEach(() => {
     const location = { line: 0, column: 0 };
-    getGeneratedLocation.mockImplementation(() => newGeneratedLocation(3));
-    findScopeByName.mockImplementation(() => ({
+    (getGeneratedLocation: any).mockImplementation(() =>
+      newGeneratedLocation(3)
+    );
+    (findScopeByName: any).mockImplementation(() => ({
       location: { start: location }
     }));
   });
 
   it("syncs with unchanged source with an existing BP", async () => {
     const { dispatch, getState } = createStore(threadClient, {}, sourceMaps);
 
     // add a source without the breakpoints
     const reloadedSource = makeSource("magic.js");
     const loc1 = {
       sourceId: "magic.js",
       sourceUrl: "http://localhost:8000/magic.js",
       line: 3,
       column: undefined
     };
+    const generatedSource = makeSource("gen.js");
     await dispatch(actions.newSource(reloadedSource));
+    await dispatch(actions.newSource(generatedSource));
     await dispatch(actions.addBreakpoint(loc1));
 
     // manually sync
     const update = await syncBreakpointPromise(
       getState,
       threadClient,
       sourceMaps,
-      reloadedSource.id,
+      reloadedSource.source.id,
       pendingBreakpoint({ location: loc1 })
     );
     expect(threadClient.removeBreakpoint.mock.calls).toHaveLength(0);
     expect(update).toMatchSnapshot();
   });
 
   it("updates a corresponding breakpoint for a changed source", async () => {
     /*
@@ -214,58 +228,71 @@ describe("reloading debuggee", () => {
          4.a. the debugger checks to see if gen.js#3 still points to magic.js#3,
               it now points to gen.js#1 so it removes the old
               breakpoint and creates a new one
     */
 
     // here we are mocking out what happens when the source changed, and the
     // new line for originalSource line 3, is the generated Source line 5
 
-    getGeneratedLocation.mockImplementation(() => newGeneratedLocation(5));
+    (getGeneratedLocation: any).mockImplementation(() =>
+      newGeneratedLocation(5)
+    );
     // end mocking out
 
     const { dispatch, getState } = createStore(threadClient, {}, sourceMaps);
 
     // add a source without the breakpoints
     const reloadedSource = makeSource("magic.js");
     await dispatch(actions.newSource(reloadedSource));
 
+    const generatedSource = makeSource("gen.js");
+    await dispatch(actions.newSource(generatedSource));
+
     // manually sync
     const update = await syncBreakpointPromise(
       getState,
       threadClient,
       sourceMaps,
-      reloadedSource.id,
+      reloadedSource.source.id,
       pendingBreakpoint()
     );
     expect(threadClient.removeBreakpoint.mock.calls).toHaveLength(1);
     expect(findScopeByName).toHaveBeenCalled();
     expect(update).toMatchSnapshot();
   });
 
   it("syncs with changed source and an existing disabled BP", async () => {
-    getGeneratedLocation.mockImplementationOnce(() => newGeneratedLocation(5));
+    (getGeneratedLocation: any).mockImplementationOnce(() =>
+      newGeneratedLocation(5)
+    );
 
     const { dispatch, getState } = createStore(threadClient, {}, sourceMaps);
 
     const reloadedSource = makeSource("magic.js");
     await dispatch(actions.newSource(reloadedSource));
+
+    const generatedSource = makeSource("gen.js");
+    await dispatch(actions.newSource(generatedSource));
+
     const location = {
-      sourceId: reloadedSource.id,
+      sourceId: reloadedSource.source.id,
       line: 3,
       column: undefined
     };
 
     const { breakpoint } = await dispatch(actions.addBreakpoint(location));
     await dispatch(actions.disableBreakpoint(breakpoint));
 
-    getGeneratedLocation.mockImplementationOnce(() => newGeneratedLocation(1));
+    (getGeneratedLocation: any).mockImplementationOnce(() =>
+      newGeneratedLocation(1)
+    );
 
     await dispatch(
       actions.syncBreakpoint(
-        reloadedSource.id,
+        reloadedSource.source.id,
         pendingBreakpoint({ disabled: true })
       )
     );
 
     expect(selectors.getPendingBreakpoints(getState())).toMatchSnapshot();
   });
 });
--- a/devtools/client/debugger/new/src/actions/debuggee.js
+++ b/devtools/client/debugger/new/src/actions/debuggee.js
@@ -1,20 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
 import type { Action, ThunkArgs } from "./types";
-import { closeTabsForMissingThreads } from "./tabs";
-import { features } from "../utils/prefs";
 
 export function updateWorkers() {
   return async function({ dispatch, getState, client }: ThunkArgs) {
     const workers = await client.fetchWorkers();
-    dispatch(({ type: "SET_WORKERS", workers }: Action));
-
-    if (features.windowlessWorkers) {
-      dispatch(closeTabsForMissingThreads(workers));
-    }
+    const mainThread = client.getMainThread();
+    dispatch(({ type: "SET_WORKERS", workers, mainThread }: Action));
   };
 }
--- a/devtools/client/debugger/new/src/actions/expressions.js
+++ b/devtools/client/debugger/new/src/actions/expressions.js
@@ -8,17 +8,18 @@ import {
   getCurrentThread,
   getExpression,
   getExpressions,
   getSelectedFrame,
   getSelectedFrameId,
   getSourceFromId,
   getSelectedSource,
   getSelectedScopeMappings,
-  getSelectedFrameBindings
+  getSelectedFrameBindings,
+  isPaused
 } from "../selectors";
 import { PROMISE } from "./utils/middleware/promise";
 import { wrapExpression } from "../utils/expressions";
 import { features } from "../utils/prefs";
 import { isOriginal } from "../utils/source";
 
 import * as parser from "../workers/parser";
 import type { Expression } from "../types";
@@ -167,31 +168,32 @@ function evaluateExpression(expression: 
 }
 
 /**
  * Gets information about original variable names from the source map
  * and replaces all posible generated names.
  */
 export function getMappedExpression(expression: string) {
   return async function({ dispatch, getState, client, sourceMaps }: ThunkArgs) {
-    const mappings = getSelectedScopeMappings(getState());
-    const bindings = getSelectedFrameBindings(getState());
+    const state = getState();
+    const mappings = getSelectedScopeMappings(state);
+    const bindings = getSelectedFrameBindings(state);
 
     // We bail early if we do not need to map the expression. This is important
     // because mapping an expression can be slow if the parser worker is
     // busy doing other work.
     //
     // 1. there are no mappings - we do not need to map original expressions
     // 2. does not contain `await` - we do not need to map top level awaits
     // 3. does not contain `=` - we do not need to map assignments
     if (!mappings && !expression.match(/(await|=)/)) {
       return null;
     }
 
     return parser.mapExpression(
       expression,
       mappings,
       bindings || [],
-      features.mapExpressionBindings,
+      features.mapExpressionBindings && isPaused(state),
       features.mapAwaitExpression
     );
   };
 }
--- a/devtools/client/debugger/new/src/actions/navigation.js
+++ b/devtools/client/debugger/new/src/actions/navigation.js
@@ -70,20 +70,20 @@ export function connect(url: string, act
   };
 }
 
 /**
  * @memberof actions/navigation
  * @static
  */
 export function navigated() {
-  return async function({ dispatch, getState, client, onReload }: ThunkArgs) {
-    // this time out is used to wait for sources. If we have 0 sources, it is likely
-    // that the sources are being loaded from the bfcache, and we should make an explicit
-    // request to the server to load them.
+  return async function({ dispatch, getState, client, panel }: ThunkArgs) {
+    // this time out is used to wait for sources. If we have 0 sources,
+    // it is likely that the sources are being loaded from the bfcache,
+    // and we should make an explicit request to the server to load them.
     await waitForMs(100);
     if (Object.keys(getSources(getState())).length == 0) {
       const sources = await client.fetchSources();
       dispatch(newSources(sources));
     }
-    onReload();
+    panel.emit("reloaded");
   };
 }
--- a/devtools/client/debugger/new/src/actions/pause/mapFrames.js
+++ b/devtools/client/debugger/new/src/actions/pause/mapFrames.js
@@ -131,16 +131,17 @@ async function expandFrames(
     };
 
     originalFrames.forEach((originalFrame, j) => {
       // Keep outer most frame with true actor ID, and generate uniquie
       // one for the nested frames.
       const id = j == 0 ? frame.id : `${frame.id}-originalFrame${j}`;
       result.push({
         id,
+        thread: originalFrame.thread,
         displayName: originalFrame.displayName,
         location: originalFrame.location,
         scope: frame.scope,
         this: frame.this,
         isOriginal: true,
         // More fields that will be added by the mapDisplayNames and
         // updateFrameLocation.
         generatedLocation: frame.generatedLocation,
--- a/devtools/client/debugger/new/src/actions/pause/paused.js
+++ b/devtools/client/debugger/new/src/actions/pause/paused.js
@@ -2,17 +2,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 import {
   getHiddenBreakpoint,
   isEvaluatingExpression,
   getSelectedFrame,
-  getSources
+  getSources,
+  getLastCommand
 } from "../../selectors";
 
 import { mapFrames } from ".";
 import { removeBreakpoint } from "../breakpoints";
 import { evaluateExpressions } from "../expressions";
 import { selectLocation } from "../sources";
 import { loadSourceText } from "../sources/loadSourceText";
 import { togglePaneCollapse } from "../ui";
@@ -45,17 +46,21 @@ export function paused(pauseInfo: Pause)
     if (topFrame && !why.frameFinished && why.type == "resumeLimit") {
       const mappedFrame = await updateFrameLocation(topFrame, sourceMaps);
       const source = await getOriginalSourceForFrame(getState(), mappedFrame);
 
       // Ensure that the original file has loaded if there is one.
       await dispatch(loadSourceText(source));
 
       if (shouldStep(mappedFrame, getState(), sourceMaps)) {
-        dispatch(command("stepOver"));
+        // When stepping past a location we shouldn't pause at according to the
+        // source map, make sure we continue stepping in the same direction we
+        // were going previously.
+        const rewind = getLastCommand(getState(), thread) == "reverseStepOver";
+        dispatch(command(rewind ? "reverseStepOver" : "stepOver"));
         return;
       }
     }
 
     dispatch({
       type: "PAUSED",
       thread,
       why,
--- a/devtools/client/debugger/new/src/actions/pause/tests/pause.spec.js
+++ b/devtools/client/debugger/new/src/actions/pause/tests/pause.spec.js
@@ -1,22 +1,26 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
+// @flow
+
 import {
   actions,
   selectors,
   createStore,
   waitForState,
   makeSource,
   makeOriginalSource,
   makeFrame
 } from "../../../utils/test-head";
 
+import { makeWhyNormal } from "../../../utils/test-mockup";
+
 import * as parser from "../../../workers/parser/index";
 
 const { isStepping } = selectors;
 
 let stepInResolve = null;
 const mockThreadClient = {
   stepIn: () =>
     new Promise(_resolve => {
@@ -25,19 +29,19 @@ const mockThreadClient = {
   stepOver: () => new Promise(_resolve => _resolve),
   evaluate: async () => {},
   evaluateInFrame: async () => {},
   evaluateExpressions: async () => {},
 
   getFrameScopes: async frame => frame.scope,
   setPausePoints: async () => {},
   setBreakpoint: () => new Promise(_resolve => {}),
-  sourceContents: sourceId => {
+  sourceContents: ({ source }) => {
     return new Promise((resolve, reject) => {
-      switch (sourceId) {
+      switch (source) {
         case "foo1":
           return resolve({
             source: "function foo1() {\n  return 5;\n}",
             contentType: "text/javascript"
           });
         case "await":
           return resolve({
             source: "async function aWait() {\n await foo();  return 5;\n}",
@@ -70,42 +74,51 @@ const mockThreadClient = {
 };
 
 const mockFrameId = "1";
 
 function createPauseInfo(
   frameLocation = { sourceId: "foo1", line: 2 },
   frameOpts = {}
 ) {
+  const frames = [
+    makeFrame(
+      { id: mockFrameId, sourceId: frameLocation.sourceId },
+      {
+        location: frameLocation,
+        ...frameOpts
+      }
+    )
+  ];
   return {
     thread: "FakeThread",
-    frames: [
-      makeFrame(
-        { id: mockFrameId, sourceId: frameLocation.sourceId },
-        {
-          location: frameLocation,
-          ...frameOpts
-        }
-      )
-    ],
+    frame: frames[0],
+    frames,
     loadedObjects: [],
-    why: {}
+    why: makeWhyNormal()
   };
 }
 
+function resumedPacket() {
+  return { from: "FakeThread", type: "resumed" };
+}
+
 describe("pause", () => {
   describe("stepping", () => {
     it("should set and clear the command", async () => {
       const { dispatch, getState } = createStore(mockThreadClient);
       const mockPauseInfo = createPauseInfo();
 
       await dispatch(actions.newSource(makeSource("foo1")));
       await dispatch(actions.paused(mockPauseInfo));
       const stepped = dispatch(actions.stepIn());
       expect(isStepping(getState())).toBeTruthy();
+      if (!stepInResolve) {
+        throw new Error("no stepInResolve");
+      }
       await stepInResolve();
       await stepped;
       expect(isStepping(getState())).toBeFalsy();
     });
 
     it("should only step when paused", async () => {
       const client = { stepIn: jest.fn() };
       const { dispatch } = createStore(client);
@@ -141,18 +154,19 @@ describe("pause", () => {
       const store = createStore(mockThreadClient);
       const { dispatch } = store;
       const mockPauseInfo = createPauseInfo({
         sourceId: "await",
         line: 2,
         column: 0
       });
 
-      await dispatch(actions.newSource(makeSource("await")));
-      await dispatch(actions.loadSourceText({ id: "await" }));
+      const csr = makeSource("await");
+      await dispatch(actions.newSource(csr));
+      await dispatch(actions.loadSourceText(csr.source));
 
       await dispatch(actions.paused(mockPauseInfo));
       const getNextStepSpy = jest.spyOn(parser, "getNextStep");
       dispatch(actions.stepOver());
       expect(getNextStepSpy).toBeCalled();
       getNextStepSpy.mockRestore();
     });
 
@@ -160,18 +174,19 @@ describe("pause", () => {
       const store = createStore(mockThreadClient);
       const { dispatch } = store;
       const mockPauseInfo = createPauseInfo({
         sourceId: "await",
         line: 2,
         column: 6
       });
 
-      await dispatch(actions.newSource(makeSource("await")));
-      await dispatch(actions.loadSourceText({ id: "await" }));
+      const csr = makeSource("await");
+      await dispatch(actions.newSource(csr));
+      await dispatch(actions.loadSourceText(csr.source));
 
       await dispatch(actions.paused(mockPauseInfo));
       const getNextStepSpy = jest.spyOn(parser, "getNextStep");
       dispatch(actions.stepOver());
       expect(getNextStepSpy).toBeCalled();
       getNextStepSpy.mockRestore();
     });
 
@@ -185,19 +200,20 @@ describe("pause", () => {
       const store = createStore(mockThreadClient, {});
       const { dispatch, getState } = store;
       const mockPauseInfo = createPauseInfo(generatedLocation, {
         scope: {
           bindings: { variables: { b: {} }, arguments: [{ a: {} }] }
         }
       });
 
-      await dispatch(actions.newSource(makeSource("foo")));
+      const csr = makeSource("foo");
+      await dispatch(actions.newSource(csr));
       await dispatch(actions.newSource(makeOriginalSource("foo")));
-      await dispatch(actions.loadSourceText({ id: "foo" }));
+      await dispatch(actions.loadSourceText(csr.source));
 
       await dispatch(actions.paused(mockPauseInfo));
       expect(selectors.getFrames(getState())).toEqual([
         {
           generatedLocation: { column: 0, line: 1, sourceId: "foo" },
           id: mockFrameId,
           location: { column: 0, line: 1, sourceId: "foo" },
           scope: {
@@ -246,20 +262,22 @@ describe("pause", () => {
         }),
         getGeneratedLocation: async location => location
       };
 
       const store = createStore(mockThreadClient, {}, sourceMapsMock);
       const { dispatch, getState } = store;
       const mockPauseInfo = createPauseInfo(generatedLocation);
 
-      await dispatch(actions.newSource(makeSource("foo")));
-      await dispatch(actions.newSource(makeOriginalSource("foo")));
-      await dispatch(actions.loadSourceText({ id: "foo" }));
-      await dispatch(actions.loadSourceText({ id: "foo-original" }));
+      const fooCSR = makeSource("foo");
+      const fooOriginalCSR = makeSource("foo-original");
+      await dispatch(actions.newSource(fooCSR));
+      await dispatch(actions.newSource(fooOriginalCSR));
+      await dispatch(actions.loadSourceText(fooCSR.source));
+      await dispatch(actions.loadSourceText(fooOriginalCSR.source));
       await dispatch(actions.setSymbols("foo-original"));
 
       await dispatch(actions.paused(mockPauseInfo));
       expect(selectors.getFrames(getState())).toEqual([
         {
           generatedLocation: { column: 0, line: 1, sourceId: "foo" },
           id: mockFrameId,
           location: { column: 0, line: 3, sourceId: "foo-original" },
@@ -305,22 +323,23 @@ describe("pause", () => {
           contentType: "text/rust"
         })
       };
 
       const store = createStore(mockThreadClient, {}, sourceMapsMock);
       const { dispatch, getState } = store;
       const mockPauseInfo = createPauseInfo(generatedLocation);
 
-      await dispatch(
-        actions.newSource(makeSource("foo-wasm", { isWasm: true }))
-      );
-      await dispatch(actions.newSource(makeOriginalSource("foo-wasm")));
-      await dispatch(actions.loadSourceText({ id: "foo-wasm" }));
-      await dispatch(actions.loadSourceText({ id: "foo-wasm/originalSource" }));
+      const csr = makeSource("foo-wasm", { isWasm: true });
+      const originalCSR = makeOriginalSource("foo-wasm");
+
+      await dispatch(actions.newSource(csr));
+      await dispatch(actions.newSource(originalCSR));
+      await dispatch(actions.loadSourceText(csr.source));
+      await dispatch(actions.loadSourceText(originalCSR.source));
 
       await dispatch(actions.paused(mockPauseInfo));
       expect(selectors.getFrames(getState())).toEqual([
         {
           displayName: "fooBar",
           generatedLocation: { column: 0, line: 1, sourceId: "foo-wasm" },
           id: mockFrameId,
           isOriginal: true,
@@ -344,31 +363,31 @@ describe("pause", () => {
   });
 
   describe("resumed", () => {
     it("should not evaluate expression while stepping", async () => {
       const client = { evaluateExpressions: jest.fn() };
       const { dispatch } = createStore(client);
 
       dispatch(actions.stepIn());
-      await dispatch(actions.resumed({ from: "FakeThread" }));
+      await dispatch(actions.resumed(resumedPacket()));
       expect(client.evaluateExpressions.mock.calls).toHaveLength(1);
     });
 
     it("resuming - will re-evaluate watch expressions", async () => {
       const store = createStore(mockThreadClient);
       const { dispatch, getState } = store;
       const mockPauseInfo = createPauseInfo();
 
       await dispatch(actions.newSource(makeSource("foo1")));
       await dispatch(actions.newSource(makeSource("foo")));
       dispatch(actions.addExpression("foo"));
       await waitForState(store, state => selectors.getExpression(state, "foo"));
 
       mockThreadClient.evaluateExpressions = () => new Promise(r => r(["YAY"]));
       await dispatch(actions.paused(mockPauseInfo));
 
-      await dispatch(actions.resumed({ from: "FakeThread" }));
+      await dispatch(actions.resumed(resumedPacket()));
       const expression = selectors.getExpression(getState(), "foo");
       expect(expression.value).toEqual("YAY");
     });
   });
 });
--- a/devtools/client/debugger/new/src/actions/pause/tests/pauseOnExceptions.spec.js
+++ b/devtools/client/debugger/new/src/actions/pause/tests/pauseOnExceptions.spec.js
@@ -1,12 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
+// @flow
+
 import {
   actions,
   createStore,
   getTelemetryEvents
 } from "../../../utils/test-head";
 
 import {
   getShouldPauseOnExceptions,
--- a/devtools/client/debugger/new/src/actions/pause/tests/skipPausing.spec.js
+++ b/devtools/client/debugger/new/src/actions/pause/tests/skipPausing.spec.js
@@ -1,12 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
+// @flow
+
 import { actions, selectors, createStore } from "../../../utils/test-head";
 
 describe("sources - pretty print", () => {
   it("returns a pretty source for a minified file", async () => {
     const client = { setSkipPausing: jest.fn() };
     const { dispatch, getState } = createStore(client);
 
     await dispatch(actions.toggleSkipPausing());
--- a/devtools/client/debugger/new/src/actions/preview.js
+++ b/devtools/client/debugger/new/src/actions/preview.js
@@ -96,17 +96,17 @@ export function setPreview(
         }
 
         if (!selectedFrame) {
           return;
         }
 
         const { result } = await client.evaluateInFrame(expression, {
           frameId: selectedFrame.id,
-          thread: source.thread
+          thread: selectedFrame.thread
         });
 
         if (!result) {
           return;
         }
 
         return {
           expression,
--- a/devtools/client/debugger/new/src/actions/sources/blackbox.js
+++ b/devtools/client/debugger/new/src/actions/sources/blackbox.js
@@ -7,39 +7,52 @@
 /**
  * Redux actions for the sources state
  * @module actions/sources
  */
 
 import { isOriginalId, originalToGeneratedId } from "devtools-source-map";
 import { recordEvent } from "../../utils/telemetry";
 import { features } from "../../utils/prefs";
+import { getSourceActors } from "../../selectors";
 
 import { PROMISE } from "../utils/middleware/promise";
 
 import type { Source } from "../../types";
 import type { ThunkArgs } from "../types";
 
+async function blackboxActors(state, client, sourceId, isBlackBoxed, range?) {
+  const sourceActors = getSourceActors(state, sourceId);
+  for (const sourceActor of sourceActors) {
+    await client.blackBox(sourceActor, isBlackBoxed, range);
+  }
+  return { isBlackBoxed: !isBlackBoxed };
+}
+
 export function toggleBlackBox(source: Source) {
   return async ({ dispatch, getState, client, sourceMaps }: ThunkArgs) => {
     const { isBlackBoxed } = source;
 
     if (!isBlackBoxed) {
       recordEvent("blackbox");
     }
 
-    let promise;
-
+    let sourceId, range;
     if (features.originalBlackbox && isOriginalId(source.id)) {
-      const range = await sourceMaps.getFileGeneratedRange(source);
-      const generatedId = originalToGeneratedId(source.id);
-      promise = client.blackBox(generatedId, isBlackBoxed, range);
+      range = await sourceMaps.getFileGeneratedRange(source);
+      sourceId = originalToGeneratedId(source.id);
     } else {
-      promise = client.blackBox(source.id, isBlackBoxed);
+      sourceId = source.id;
     }
 
     return dispatch({
       type: "BLACKBOX",
       source,
-      [PROMISE]: promise
+      [PROMISE]: blackboxActors(
+        getState(),
+        client,
+        sourceId,
+        isBlackBoxed,
+        range
+      )
     });
   };
 }
--- a/devtools/client/debugger/new/src/actions/sources/loadSourceText.js
+++ b/devtools/client/debugger/new/src/actions/sources/loadSourceText.js
@@ -1,38 +1,47 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
 import { PROMISE } from "../utils/middleware/promise";
-import { getGeneratedSource, getSource } from "../../selectors";
+import {
+  getGeneratedSource,
+  getSource,
+  getSourceActors
+} from "../../selectors";
 import * as parser from "../../workers/parser";
 import { isLoaded, isOriginal } from "../../utils/source";
 import { Telemetry } from "devtools-modules";
 
 import defer from "../../utils/defer";
 import type { ThunkArgs } from "../types";
 
 import type { Source } from "../../types";
 
 const requests = new Map();
 
 // Measures the time it takes for a source to load
 const loadSourceHistogram = "DEVTOOLS_DEBUGGER_LOAD_SOURCE_MS";
 const telemetry = new Telemetry();
 
-async function loadSource(source: Source, { sourceMaps, client }) {
+async function loadSource(state, source: Source, { sourceMaps, client }) {
   const { id } = source;
   if (isOriginal(source)) {
     return sourceMaps.getOriginalSourceText(source);
   }
 
-  const response = await client.sourceContents(id);
+  const sourceActors = getSourceActors(state, id);
+  if (!sourceActors.length) {
+    throw new Error("No source actor for loadSource");
+  }
+
+  const response = await client.sourceContents(sourceActors[0]);
   telemetry.finish(loadSourceHistogram, source);
 
   return {
     id,
     text: response.source,
     contentType: response.contentType || "text/javascript"
   };
 }
@@ -60,17 +69,17 @@ export function loadSourceText(source: ?
     const deferred = defer();
     requests.set(id, deferred.promise);
 
     telemetry.start(loadSourceHistogram, source);
     try {
       await dispatch({
         type: "LOAD_SOURCE_TEXT",
         sourceId: source.id,
-        [PROMISE]: loadSource(source, { sourceMaps, client })
+        [PROMISE]: loadSource(getState(), source, { sourceMaps, client })
       });
     } catch (e) {
       deferred.resolve();
       requests.delete(id);
       return;
     }
 
     const newSource = getSource(getState(), source.id);
--- a/devtools/client/debugger/new/src/actions/sources/newSources.js
+++ b/devtools/client/debugger/new/src/actions/sources/newSources.js
@@ -16,36 +16,37 @@ import { toggleBlackBox } from "./blackb
 import { syncBreakpoint } from "../breakpoints";
 import { loadSourceText } from "./loadSourceText";
 import { togglePrettyPrint } from "./prettyPrint";
 import { selectLocation } from "../sources";
 import { getRawSourceURL, isPrettyURL, isOriginal } from "../../utils/source";
 import {
   getBlackBoxList,
   getSource,
+  hasSourceActor,
   getPendingSelectedLocation,
   getPendingBreakpointsForSource
 } from "../../selectors";
 
 import { prefs } from "../../utils/prefs";
 import sourceQueue from "../../utils/source-queue";
 
 import type { Source, SourceId } from "../../types";
 import type { Action, ThunkArgs } from "../types";
+import type { CreateSourceResult } from "../../client/firefox/types";
 
 function createOriginalSource(
   originalUrl,
   generatedSource,
   sourceMaps
 ): Source {
   return {
     url: originalUrl,
     relativeUrl: originalUrl,
     id: generatedToOriginalId(generatedSource.id, originalUrl),
-    thread: generatedSource.thread,
     isPrettyPrinted: false,
     isWasm: false,
     isBlackBoxed: false,
     loadedState: "unloaded",
     introductionUrl: null
   };
 }
 
@@ -56,17 +57,17 @@ function loadSourceMaps(sources: Source[
   }: ThunkArgs): Promise<Promise<Source>[]> {
     if (!prefs.clientSourceMapsEnabled) {
       return [];
     }
 
     const sourceList = await Promise.all(
       sources.map(async ({ id }) => {
         const originalSources = await dispatch(loadSourceMap(id));
-        sourceQueue.queueSources(originalSources);
+        sourceQueue.queueSources(originalSources.map(source => ({ source })));
         return originalSources;
       })
     );
 
     await sourceQueue.flush();
     return flatten(sourceList);
   };
 }
@@ -197,32 +198,46 @@ function restoreBlackBoxedSources(source
   };
 }
 
 /**
  * Handler for the debugger client's unsolicited newSource notification.
  * @memberof actions/sources
  * @static
  */
-export function newSource(source: Source) {
+export function newSource(source: CreateSourceResult) {
   return async ({ dispatch }: ThunkArgs) => {
     await dispatch(newSources([source]));
   };
 }
 
-export function newSources(sources: Source[]) {
+export function newSources(createdSources: CreateSourceResult[]) {
   return async ({ dispatch, getState }: ThunkArgs) => {
-    sources = sources.filter(source => !getSource(getState(), source.id));
+    // Find any sources we haven't seen before.
+    const sources = createdSources
+      .map(csr => csr.source)
+      .filter(source => !getSource(getState(), source.id));
+
+    // Find any source actors we haven't seen before.
+    const sourceActors = createdSources
+      .map(csr => csr.sourceActor)
+      .filter(
+        sourceActor => sourceActor && !hasSourceActor(getState(), sourceActor)
+      );
+
+    if (sources.length == 0 && sourceActors.length == 0) {
+      return;
+    }
+
+    dispatch({ type: "ADD_SOURCES", sources, sourceActors });
 
     if (sources.length == 0) {
       return;
     }
 
-    dispatch(({ type: "ADD_SOURCES", sources: sources }: Action));
-
     for (const source of sources) {
       dispatch(checkSelectedSource(source.id));
     }
 
     // We would like to restore the blackboxed state
     // after loading all states to make sure the correctness.
     dispatch(restoreBlackBoxedSources(sources));
 
--- a/devtools/client/debugger/new/src/actions/sources/prettyPrint.js
+++ b/devtools/client/debugger/new/src/actions/sources/prettyPrint.js
@@ -15,48 +15,55 @@ import { getPrettySourceURL, isLoaded } 
 import { loadSourceText } from "./loadSourceText";
 import { mapFrames } from "../pause";
 import { selectSpecificLocation } from "../sources";
 
 import {
   getSource,
   getSourceFromId,
   getSourceByURL,
-  getSelectedLocation
+  getSelectedLocation,
+  getSourceActors
 } from "../../selectors";
 
 import type { Action, ThunkArgs } from "../types";
 import { selectSource } from "./select";
 import type { JsSource } from "../../types";
 
 export function createPrettySource(sourceId: string) {
   return async ({ dispatch, getState, sourceMaps }: ThunkArgs) => {
     const source = getSourceFromId(getState(), sourceId);
     const url = getPrettySourceURL(source.url);
     const id = await sourceMaps.generatedToOriginalId(sourceId, url);
 
     const prettySource: JsSource = {
       url,
       relativeUrl: url,
       id,
-      thread: "",
       isBlackBoxed: false,
       isPrettyPrinted: true,
       isWasm: false,
       contentType: "text/javascript",
       loadedState: "loading",
       introductionUrl: null
     };
 
     dispatch(({ type: "ADD_SOURCE", source: prettySource }: Action));
     dispatch(selectSource(prettySource.id));
 
     const { code, mappings } = await prettyPrint({ source, url });
     await sourceMaps.applySourceMap(source.id, url, code, mappings);
 
+    // The source map URL service used by other devtools listens to changes to
+    // sources based on their actor IDs, so apply the mapping there too.
+    const sourceActors = getSourceActors(getState(), sourceId);
+    for (const sourceActor of sourceActors) {
+      await sourceMaps.applySourceMap(sourceActor.actor, url, code, mappings);
+    }
+
     const loadedPrettySource: JsSource = {
       ...prettySource,
       text: code,
       loadedState: "loaded"
     };
 
     setSource(loadedPrettySource);
 
--- a/devtools/client/debugger/new/src/actions/sources/select.js
+++ b/devtools/client/debugger/new/src/actions/sources/select.js
@@ -37,17 +37,16 @@ import {
 import type { SourceLocation, PartialPosition, Source } from "../../types";
 import type { ThunkArgs } from "../types";
 
 export const setSelectedLocation = (
   source: Source,
   location: SourceLocation
 ) => ({
   type: "SET_SELECTED_LOCATION",
-  thread: source.thread,
   source,
   location
 });
 
 export const setPendingSelectedLocation = (url: string, options: Object) => ({
   type: "SET_PENDING_SELECTED_LOCATION",
   url: url,
   line: options.location ? options.location.line : null
--- a/devtools/client/debugger/new/src/actions/sources/tests/__snapshots__/prettyPrint.spec.js.snap
+++ b/devtools/client/debugger/new/src/actions/sources/tests/__snapshots__/prettyPrint.spec.js.snap
@@ -10,12 +10,11 @@ Object {
   "isExtension": false,
   "isPrettyPrinted": true,
   "isWasm": false,
   "loadedState": "loaded",
   "relativeUrl": "http://localhost:8000/examples/base.js:formatted",
   "sourceMapURL": undefined,
   "text": "undefined
 ",
-  "thread": "",
   "url": "http://localhost:8000/examples/base.js:formatted",
 }
 `;
--- a/devtools/client/debugger/new/src/actions/sources/tests/loadSource.spec.js
+++ b/devtools/client/debugger/new/src/actions/sources/tests/loadSource.spec.js
@@ -1,109 +1,140 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
+// @flow
+
 import {
   actions,
   selectors,
   createStore,
   makeSource
 } from "../../../utils/test-head";
 import { sourceThreadClient } from "../../tests/helpers/threadClient.js";
 
-describe("loadSourceText", async () => {
+describe("loadSourceText", () => {
   it("should load source text", async () => {
     const store = createStore(sourceThreadClient);
     const { dispatch, getState } = store;
 
-    await dispatch(actions.loadSourceText({ id: "foo1" }));
+    const foo1CSR = makeSource("foo1");
+    await dispatch(actions.newSource(foo1CSR));
+    await dispatch(actions.loadSourceText(foo1CSR.source));
     const fooSource = selectors.getSource(getState(), "foo1");
 
+    if (!fooSource || typeof fooSource.text != "string") {
+      throw new Error("bad fooSource");
+    }
     expect(fooSource.text.indexOf("return foo1")).not.toBe(-1);
 
-    await dispatch(actions.loadSourceText({ id: "foo2" }));
+    const foo2CSR = makeSource("foo2");
+    await dispatch(actions.newSource(foo2CSR));
+    await dispatch(actions.loadSourceText(foo2CSR.source));
     const foo2Source = selectors.getSource(getState(), "foo2");
 
+    if (!foo2Source || typeof foo2Source.text != "string") {
+      throw new Error("bad fooSource");
+    }
     expect(foo2Source.text.indexOf("return foo2")).not.toBe(-1);
   });
 
   it("loads two sources w/ one request", async () => {
     let resolve;
     let count = 0;
     const { dispatch, getState } = createStore({
       sourceContents: () =>
         new Promise(r => {
           count++;
           resolve = r;
         })
     });
     const id = "foo";
-    let source = makeSource(id, { loadedState: "unloaded" });
+    const csr = makeSource(id, { loadedState: "unloaded" });
 
-    await dispatch(actions.newSource(source));
+    await dispatch(actions.newSource(csr));
 
-    source = selectors.getSource(getState(), id);
+    let source = selectors.getSource(getState(), id);
     dispatch(actions.loadSourceText(source));
 
     source = selectors.getSource(getState(), id);
     const loading = dispatch(actions.loadSourceText(source));
 
+    if (!resolve) {
+      throw new Error("no resolve");
+    }
     resolve({ source: "yay", contentType: "text/javascript" });
     await loading;
     expect(count).toEqual(1);
-    expect(selectors.getSource(getState(), id).text).toEqual("yay");
+
+    source = selectors.getSource(getState(), id);
+    expect(source && source.text).toEqual("yay");
   });
 
   it("doesn't re-load loaded sources", async () => {
     let resolve;
     let count = 0;
     const { dispatch, getState } = createStore({
       sourceContents: () =>
         new Promise(r => {
           count++;
           resolve = r;
         })
     });
     const id = "foo";
-    let source = makeSource(id, { loadedState: "unloaded" });
+    const csr = makeSource(id, { loadedState: "unloaded" });
 
-    await dispatch(actions.newSource(source));
-    source = selectors.getSource(getState(), id);
+    await dispatch(actions.newSource(csr));
+    let source = selectors.getSource(getState(), id);
     const loading = dispatch(actions.loadSourceText(source));
+
+    if (!resolve) {
+      throw new Error("no resolve");
+    }
     resolve({ source: "yay", contentType: "text/javascript" });
     await loading;
 
     source = selectors.getSource(getState(), id);
     await dispatch(actions.loadSourceText(source));
     expect(count).toEqual(1);
-    expect(selectors.getSource(getState(), id).text).toEqual("yay");
+
+    source = selectors.getSource(getState(), id);
+    expect(source && source.text).toEqual("yay");
   });
 
   it("should cache subsequent source text loads", async () => {
     const { dispatch, getState } = createStore(sourceThreadClient);
 
-    await dispatch(actions.loadSourceText({ id: "foo1" }));
+    const csr = makeSource("foo1");
+    await dispatch(actions.loadSourceText(csr.source));
     const prevSource = selectors.getSource(getState(), "foo1");
 
     await dispatch(actions.loadSourceText(prevSource));
     const curSource = selectors.getSource(getState(), "foo1");
 
     expect(prevSource === curSource).toBeTruthy();
   });
 
   it("should indicate a loading source", async () => {
     const { dispatch, getState } = createStore(sourceThreadClient);
 
     // Don't block on this so we can check the loading state.
-    dispatch(actions.loadSourceText({ id: "foo1" }));
+    const csr = makeSource("foo1");
+    dispatch(actions.loadSourceText(csr.source));
     const fooSource = selectors.getSource(getState(), "foo1");
-    expect(fooSource.loadedState).toEqual("loading");
+    expect(fooSource && fooSource.loadedState).toEqual("loading");
   });
 
   it("should indicate an errored source text", async () => {
     const { dispatch, getState } = createStore(sourceThreadClient);
 
-    await dispatch(actions.loadSourceText({ id: "bad-id" }));
+    const csr = makeSource("bad-id");
+    await dispatch(actions.newSource(csr));
+    await dispatch(actions.loadSourceText(csr.source));
     const badSource = selectors.getSource(getState(), "bad-id");
+
+    if (!badSource || !badSource.error) {
+      throw new Error("bad badSource");
+    }
     expect(badSource.error.indexOf("unknown source")).not.toBe(-1);
   });
 });
--- a/devtools/client/debugger/new/src/actions/sources/tests/newSources.spec.js
+++ b/devtools/client/debugger/new/src/actions/sources/tests/newSources.spec.js
@@ -1,12 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
+// @flow
+
 import {
   actions,
   selectors,
   createStore,
   makeSource,
   waitForState
 } from "../../../utils/test-head";
 const {
@@ -24,52 +26,54 @@ describe("sources - new sources", () => 
   it("should add sources to state", async () => {
     const { dispatch, getState } = createStore(threadClient);
     await dispatch(actions.newSource(makeSource("base.js")));
     await dispatch(actions.newSource(makeSource("jquery.js")));
 
     expect(getSourceCount(getState())).toEqual(2);
     const base = getSource(getState(), "base.js");
     const jquery = getSource(getState(), "jquery.js");
-    expect(base.id).toEqual("base.js");
-    expect(jquery.id).toEqual("jquery.js");
+    expect(base && base.id).toEqual("base.js");
+    expect(jquery && jquery.id).toEqual("jquery.js");
   });
 
   it("should not add multiple identical sources", async () => {
     const { dispatch, getState } = createStore(threadClient);
 
     await dispatch(actions.newSource(makeSource("base.js")));
     await dispatch(actions.newSource(makeSource("base.js")));
 
     expect(getSourceCount(getState())).toEqual(1);
   });
 
   it("should automatically select a pending source", async () => {
     const { dispatch, getState } = createStore(threadClient);
-    const baseSource = makeSource("base.js");
-    await dispatch(actions.selectSourceURL(baseSource.url));
+    const baseCSR = makeSource("base.js");
+    await dispatch(actions.selectSourceURL(baseCSR.source.url));
 
     expect(getSelectedSource(getState())).toBe(undefined);
-    await dispatch(actions.newSource(baseSource));
-    expect(getSelectedSource(getState()).url).toBe(baseSource.url);
+    await dispatch(actions.newSource(baseCSR));
+
+    const selected = getSelectedSource(getState());
+    expect(selected && selected.url).toBe(baseCSR.source.url);
   });
 
   it("should add original sources", async () => {
     const { dispatch, getState } = createStore(
       threadClient,
       {},
       {
         getOriginalURLs: async () => ["magic.js"]
       }
     );
 
-    const baseSource = makeSource("base.js", { sourceMapURL: "base.js.map" });
-    await dispatch(actions.newSource(baseSource));
-    const magic = getSourceByURL(getState(), "magic.js", true);
-    expect(magic.url).toEqual("magic.js");
+    const baseCSR = makeSource("base.js", { sourceMapURL: "base.js.map" });
+    await dispatch(actions.newSource(baseCSR));
+    const magic = getSourceByURL(getState(), "magic.js");
+    expect(magic && magic.url).toEqual("magic.js");
   });
 
   // eslint-disable-next-line
   it("should not attempt to fetch original sources if it's missing a source map url", async () => {
     const getOriginalURLs = jest.fn();
     const { dispatch } = createStore(threadClient, {}, { getOriginalURLs });
 
     await dispatch(actions.newSource(makeSource("base.js")));
@@ -86,21 +90,21 @@ describe("sources - new sources", () => 
   it("should process new sources immediately, without waiting for source maps to be fetched first", async () => {
     const { dispatch, getState } = createStore(
       threadClient,
       {},
       {
         getOriginalURLs: async () => new Promise(_ => {})
       }
     );
-    const baseSource = makeSource("base.js", { sourceMapURL: "base.js.map" });
-    await dispatch(actions.newSource(baseSource));
+    const baseCSR = makeSource("base.js", { sourceMapURL: "base.js.map" });
+    await dispatch(actions.newSource(baseCSR));
     expect(getSourceCount(getState())).toEqual(1);
     const base = getSource(getState(), "base.js");
-    expect(base.id).toEqual("base.js");
+    expect(base && base.id).toEqual("base.js");
   });
 
   // eslint-disable-next-line
   it("shouldn't let one slow loading source map delay all the other source maps", async () => {
     const dbg = createStore(
       threadClient,
       {},
       {
@@ -118,14 +122,14 @@ describe("sources - new sources", () => 
     const { dispatch, getState } = dbg;
     const fooSource = makeSource("foo.js", { sourceMapURL: "foo.js.map" });
     const barSource = makeSource("bar.js", { sourceMapURL: "bar.js.map" });
     const bazzSource = makeSource("bazz.js", { sourceMapURL: "bazz.js.map" });
     await dispatch(actions.newSources([fooSource, barSource, bazzSource]));
     await sourceQueue.flush();
     await waitForState(dbg, state => getSourceCount(state) == 5);
     expect(getSourceCount(getState())).toEqual(5);
-    const barCljs = getSourceByURL(getState(), "bar.cljs", true);
-    expect(barCljs.url).toEqual("bar.cljs");
-    const bazzCljs = getSourceByURL(getState(), "bazz.cljs", true);
-    expect(bazzCljs.url).toEqual("bazz.cljs");
+    const barCljs = getSourceByURL(getState(), "bar.cljs");
+    expect(barCljs && barCljs.url).toEqual("bar.cljs");
+    const bazzCljs = getSourceByURL(getState(), "bazz.cljs");
+    expect(bazzCljs && bazzCljs.url).toEqual("bazz.cljs");
   });
 });
--- a/devtools/client/debugger/new/src/actions/sources/tests/prettyPrint.spec.js
+++ b/devtools/client/debugger/new/src/actions/sources/tests/prettyPrint.spec.js
@@ -1,43 +1,45 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
+// @flow
+
 import {
   actions,
   selectors,
   createStore,
   makeSource
 } from "../../../utils/test-head";
 import { createPrettySource } from "../prettyPrint";
 import { sourceThreadClient } from "../../tests/helpers/threadClient.js";
 
 describe("sources - pretty print", () => {
   const { dispatch, getState } = createStore(sourceThreadClient);
 
   it("returns a pretty source for a minified file", async () => {
     const url = "base.js";
-    const source = makeSource(url);
-    await dispatch(actions.newSource(source));
-    await dispatch(createPrettySource(url));
+    const csr = makeSource(url);
+    await dispatch(actions.newSource(csr));
+    await dispatch(createPrettySource(csr.source.id));
 
-    const prettyURL = `${source.url}:formatted`;
+    const prettyURL = `${csr.source.url}:formatted`;
     const pretty = selectors.getSourceByURL(getState(), prettyURL);
-    expect(pretty.contentType).toEqual("text/javascript");
-    expect(pretty.url.includes(prettyURL)).toEqual(true);
+    expect(pretty && pretty.contentType).toEqual("text/javascript");
+    expect(pretty && pretty.url.includes(prettyURL)).toEqual(true);
     expect(pretty).toMatchSnapshot();
   });
 
   it("should create a source when first toggling pretty print", async () => {
-    const source = makeSource("foobar.js", { loadedState: "loaded" });
-    await dispatch(actions.togglePrettyPrint(source));
+    const csr = makeSource("foobar.js", { loadedState: "loaded" });
+    await dispatch(actions.togglePrettyPrint(csr.source.id));
     expect(selectors.getSourceCount(getState())).toEqual(2);
   });
 
   it("should not make a second source when toggling pretty print", async () => {
-    const source = makeSource("foobar.js", { loadedState: "loaded" });
-    await dispatch(actions.togglePrettyPrint(source));
+    const csr = makeSource("foobar.js", { loadedState: "loaded" });
+    await dispatch(actions.togglePrettyPrint(csr.source.id));
     expect(selectors.getSourceCount(getState())).toEqual(2);
-    await dispatch(actions.togglePrettyPrint(source.id));
+    await dispatch(actions.togglePrettyPrint(csr.source.id));
     expect(selectors.getSourceCount(getState())).toEqual(2);
   });
 });
--- a/devtools/client/debugger/new/src/actions/sources/tests/querystrings.spec.js
+++ b/devtools/client/debugger/new/src/actions/sources/tests/querystrings.spec.js
@@ -1,12 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
+// @flow
+
 import {
   actions,
   selectors,
   createStore,
   makeSource
 } from "../../../utils/test-head";
 const { getSourcesUrlsInSources } = selectors;
 
--- a/devtools/client/debugger/new/src/actions/sources/tests/select.spec.js
+++ b/devtools/client/debugger/new/src/actions/sources/tests/select.spec.js
@@ -1,12 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
+// @flow
+
 import { getSymbols } from "../../../reducers/ast";
 import {
   actions,
   selectors,
   createStore,
   makeFrame,
   makeSource,
   waitForState,
@@ -21,143 +23,164 @@ const {
   getOutOfScopeLocations,
   getSelectedLocation
 } = selectors;
 
 import { sourceThreadClient } from "../../tests/helpers/threadClient.js";
 
 process.on("unhandledRejection", (reason, p) => {});
 
+function initialLocation(sourceId) {
+  return { sourceId, line: 1 };
+}
+
 describe("sources", () => {
   it("should select a source", async () => {
     // Note that we pass an empty client in because the action checks
     // if it exists.
     const store = createStore(sourceThreadClient);
     const { dispatch, getState } = store;
 
+    const frame = makeFrame({ id: "1", sourceId: "foo1" });
+
     await dispatch(actions.newSource(makeSource("foo1")));
     await dispatch(
       actions.paused({
         thread: "FakeThread",
         why: { type: "debuggerStatement" },
-        frames: [makeFrame({ id: "1", sourceId: "foo1" })]
+        frame,
+        frames: [frame]
       })
     );
 
     await dispatch(
       actions.selectLocation({ sourceId: "foo1", line: 1, column: 5 })
     );
 
     const selectedSource = getSelectedSource(getState());
+    if (!selectedSource) {
+      throw new Error("bad selectedSource");
+    }
     expect(selectedSource.id).toEqual("foo1");
 
     const source = getSource(getState(), selectedSource.id);
+    if (!source) {
+      throw new Error("bad source");
+    }
     expect(source.id).toEqual("foo1");
 
     await waitForState(
       store,
       state =>
         getOutOfScopeLocations(state) && getSourceMetaData(state, source.id)
     );
     const locations = getOutOfScopeLocations(getState());
     expect(locations).toHaveLength(1);
   });
 
   it("should select next tab on tab closed if no previous tab", async () => {
     const { dispatch, getState } = createStore(sourceThreadClient);
 
-    const fooSource = makeSource("foo.js");
+    const fooCSR = makeSource("foo.js");
 
-    await dispatch(actions.newSource(fooSource));
+    await dispatch(actions.newSource(fooCSR));
     await dispatch(actions.newSource(makeSource("bar.js")));
     await dispatch(actions.newSource(makeSource("baz.js")));
 
     // 3rd tab
-    await dispatch(actions.selectLocation({ sourceId: "foo.js" }));
+    await dispatch(actions.selectLocation(initialLocation("foo.js")));
 
     // 2nd tab
-    await dispatch(actions.selectLocation({ sourceId: "bar.js" }));
+    await dispatch(actions.selectLocation(initialLocation("bar.js")));
 
     // 1st tab
-    await dispatch(actions.selectLocation({ sourceId: "baz.js" }));
+    await dispatch(actions.selectLocation(initialLocation("baz.js")));
 
     // 3rd tab is reselected
-    await dispatch(actions.selectLocation({ sourceId: "foo.js" }));
+    await dispatch(actions.selectLocation(initialLocation("foo.js")));
 
     // closes the 1st tab, which should have no previous tab
-    await dispatch(actions.closeTab(fooSource));
+    await dispatch(actions.closeTab(fooCSR.source));
 
-    expect(getSelectedSource(getState()).id).toBe("bar.js");
+    const selected = getSelectedSource(getState());
+    expect(selected && selected.id).toBe("bar.js");
     expect(getSourceTabs(getState())).toHaveLength(2);
   });
 
   it("should open a tab for the source", async () => {
     const { dispatch, getState } = createStore(sourceThreadClient);
     await dispatch(actions.newSource(makeSource("foo.js")));
-    dispatch(actions.selectLocation({ sourceId: "foo.js" }));
+    dispatch(actions.selectLocation(initialLocation("foo.js")));
 
     const tabs = getSourceTabs(getState());
     expect(tabs).toHaveLength(1);
     expect(tabs[0].url).toEqual("http://localhost:8000/examples/foo.js");
   });
 
   it("should select previous tab on tab closed", async () => {
     const { dispatch, getState } = createStore(sourceThreadClient);
     await dispatch(actions.newSource(makeSource("foo.js")));
     await dispatch(actions.newSource(makeSource("bar.js")));
 
     const bazSource = makeSource("baz.js");
     await dispatch(actions.newSource(bazSource));
 
-    await dispatch(actions.selectLocation({ sourceId: "foo.js" }));
-    await dispatch(actions.selectLocation({ sourceId: "bar.js" }));
-    await dispatch(actions.selectLocation({ sourceId: "baz.js" }));
-    await dispatch(actions.closeTab(bazSource));
-    expect(getSelectedSource(getState()).id).toBe("bar.js");
+    await dispatch(actions.selectLocation(initialLocation("foo.js")));
+    await dispatch(actions.selectLocation(initialLocation("bar.js")));
+    await dispatch(actions.selectLocation(initialLocation("baz.js")));
+    await dispatch(actions.closeTab(bazSource.source));
+
+    const selected = getSelectedSource(getState());
+    expect(selected && selected.id).toBe("bar.js");
     expect(getSourceTabs(getState())).toHaveLength(2);
   });
 
   it("should keep the selected source when other tab closed", async () => {
     const { dispatch, getState } = createStore(sourceThreadClient);
 
     const bazSource = makeSource("baz.js");
 
     await dispatch(actions.newSource(makeSource("foo.js")));
     await dispatch(actions.newSource(makeSource("bar.js")));
     await dispatch(actions.newSource(bazSource));
 
     // 3rd tab
-    await dispatch(actions.selectLocation({ sourceId: "foo.js" }));
+    await dispatch(actions.selectLocation(initialLocation("foo.js")));
 
     // 2nd tab
-    await dispatch(actions.selectLocation({ sourceId: "bar.js" }));
+    await dispatch(actions.selectLocation(initialLocation("bar.js")));
 
     // 1st tab
-    await dispatch(actions.selectLocation({ sourceId: "baz.js" }));
+    await dispatch(actions.selectLocation(initialLocation("baz.js")));
 
     // 3rd tab is reselected
-    await dispatch(actions.selectLocation({ sourceId: "foo.js" }));
-    await dispatch(actions.closeTab(bazSource));
-    expect(getSelectedSource(getState()).id).toBe("foo.js");
+    await dispatch(actions.selectLocation(initialLocation("foo.js")));
+    await dispatch(actions.closeTab(bazSource.source));
+
+    const selected = getSelectedSource(getState());
+    expect(selected && selected.id).toBe("foo.js");
     expect(getSourceTabs(getState())).toHaveLength(2);
   });
 
   it("should not select new sources that lack a URL", async () => {
     const { dispatch, getState } = createStore(sourceThreadClient);
-    await dispatch(actions.newSource({ id: "foo" }));
+
+    const csr = makeSource("foo");
+    csr.source.url = "";
+    await dispatch(actions.newSource(csr));
 
     expect(getSourceCount(getState())).toEqual(1);
     const selectedLocation = getSelectedLocation(getState());
     expect(selectedLocation).toEqual(undefined);
   });
 
   it("sets and clears selected location correctly", () => {
     const { dispatch, getState } = createStore(sourceThreadClient);
-    const source = { id: "testSource" };
-    const location = { test: "testLocation" };
+    const source = makeSource("testSource").source;
+    const location = ({ test: "testLocation" }: any);
 
     // set value
     dispatch(actions.setSelectedLocation(source, location));
     expect(getSelectedLocation(getState())).toEqual({
       sourceId: source.id,
       ...location
     });
 
@@ -183,74 +206,85 @@ describe("sources", () => {
     dispatch(actions.clearSelectedLocation());
     const clearResult = getState().sources.pendingSelectedLocation;
     expect(clearResult).toEqual({ url: "" });
   });
 
   it("should keep the generated the viewing context", async () => {
     const store = createStore(sourceThreadClient);
     const { dispatch, getState } = store;
-    const baseSource = makeSource("base.js");
-    await dispatch(actions.newSource(baseSource));
+    const baseCSR = makeSource("base.js");
+    await dispatch(actions.newSource(baseCSR));
 
     await dispatch(
-      actions.selectLocation({ sourceId: baseSource.id, line: 1 })
+      actions.selectLocation({ sourceId: baseCSR.source.id, line: 1 })
     );
 
-    expect(getSelectedSource(getState()).id).toBe(baseSource.id);
-    await waitForState(store, state => getSymbols(state, baseSource));
+    const selected = getSelectedSource(getState());
+    expect(selected && selected.id).toBe(baseCSR.source.id);
+    await waitForState(store, state => getSymbols(state, baseCSR.source));
   });
 
   it("should keep the original the viewing context", async () => {
     const { dispatch, getState } = createStore(
       sourceThreadClient,
       {},
       {
         getOriginalLocation: async location => ({ ...location, line: 12 }),
         getGeneratedLocation: async location => ({ ...location, line: 12 }),
         getOriginalSourceText: async () => ({ source: "" })
       }
     );
 
-    const baseSource = makeSource("base.js");
-    await dispatch(actions.newSource(baseSource));
+    const baseCSR = makeSource("base.js");
+    await dispatch(actions.newSource(baseCSR));
 
     const originalBaseSource = makeOriginalSource("base.js");
     await dispatch(actions.newSource(originalBaseSource));
 
-    await dispatch(actions.selectSource(originalBaseSource.id));
+    await dispatch(actions.selectSource(originalBaseSource.source.id));
 
-    const fooSource = makeSource("foo.js");
-    await dispatch(actions.newSource(fooSource));
-    await dispatch(actions.selectLocation({ sourceId: fooSource.id, line: 1 }));
+    const fooCSR = makeSource("foo.js");
+    await dispatch(actions.newSource(fooCSR));
+    await dispatch(
+      actions.selectLocation({ sourceId: fooCSR.source.id, line: 1 })
+    );
 
-    expect(getSelectedLocation(getState()).line).toBe(12);
+    const selected = getSelectedLocation(getState());
+    expect(selected && selected.line).toBe(12);
   });
 
   it("should change the original the viewing context", async () => {
     const { dispatch, getState } = createStore(
       sourceThreadClient,
       {},
       { getOriginalLocation: async location => ({ ...location, line: 12 }) }
     );
 
-    const baseSource = makeOriginalSource("base.js");
-    await dispatch(actions.newSource(baseSource));
-    await dispatch(actions.selectSource(baseSource.id));
+    const baseCSR = makeOriginalSource("base.js");
+    await dispatch(actions.newSource(baseCSR));
+    await dispatch(actions.selectSource(baseCSR.source.id));
 
     await dispatch(
-      actions.selectSpecificLocation({ sourceId: baseSource.id, line: 1 })
+      actions.selectSpecificLocation({
+        sourceId: baseCSR.source.id,
+        line: 1
+      })
     );
-    expect(getSelectedLocation(getState()).line).toBe(1);
+
+    const selected = getSelectedLocation(getState());
+    expect(selected && selected.line).toBe(1);
   });
 
   describe("selectSourceURL", () => {
     it("should automatically select a pending source", async () => {
       const { dispatch, getState } = createStore(sourceThreadClient);
-      const baseSource = makeSource("base.js");
-      await dispatch(actions.selectSourceURL(baseSource.url));
+      const baseCSR = makeSource("base.js");
+      await dispatch(actions.selectSourceURL(baseCSR.source.url));
 
       expect(getSelectedSource(getState())).toBe(undefined);
-      await dispatch(actions.newSource(baseSource));
-      expect(getSelectedSource(getState()).url).toBe(baseSource.url);
+      await dispatch(actions.newSource(baseCSR));
+
+      const selected = getSelectedSource(getState());
+      expect(selected && selected.url).toBe(baseCSR.source.url);
     });
   });
 });
--- a/devtools/client/debugger/new/src/actions/tabs.js
+++ b/devtools/client/debugger/new/src/actions/tabs.js
@@ -12,51 +12,46 @@
 import { isOriginalId } from "devtools-source-map";
 
 import { removeDocument } from "../utils/editor";
 import { selectSource } from "./sources";
 
 import {
   getSourcesByURLs,
   getSourceTabs,
-  getSource,
   getNewSelectedSourceId,
   removeSourceFromTabList,
   removeSourcesFromTabList
 } from "../selectors";
 
-import { getMainThread } from "../reducers/debuggee";
-
 import type { Action, ThunkArgs } from "./types";
-import type { Source, Worker } from "../types";
+import type { Source } from "../types";
 
 export function updateTab(source: Source, framework: string): Action {
-  const { url, id: sourceId, thread } = source;
+  const { url, id: sourceId } = source;
   const isOriginal = isOriginalId(source.id);
 
   return {
     type: "UPDATE_TAB",
     url,
     framework,
     isOriginal,
-    sourceId,
-    thread
+    sourceId
   };
 }
 
 export function addTab(source: Source): Action {
-  const { url, id: sourceId, thread } = source;
+  const { url, id: sourceId } = source;
   const isOriginal = isOriginalId(source.id);
 
   return {
     type: "ADD_TAB",
     url,
     isOriginal,
-    sourceId,
-    thread
+    sourceId
   };
 }
 
 export function moveTab(url: string, tabIndex: number): Action {
   return {
     type: "MOVE_TAB",
     url,
     tabIndex
@@ -91,33 +86,8 @@ export function closeTabs(urls: string[]
 
     const tabs = removeSourcesFromTabList(getSourceTabs(getState()), sources);
     dispatch(({ type: "CLOSE_TABS", sources, tabs }: Action));
 
     const sourceId = getNewSelectedSourceId(getState(), tabs);
     dispatch(selectSource(sourceId));
   };
 }
-
-export function closeTabsForMissingThreads(workers: Worker[]) {
-  return ({ dispatch, getState }: ThunkArgs) => {
-    const oldTabs = getSourceTabs(getState());
-    const mainThread = getMainThread(getState());
-    const removed = [];
-    for (const { sourceId } of oldTabs) {
-      if (sourceId) {
-        const source = getSource(getState(), sourceId);
-        if (
-          source &&
-          source.thread != mainThread.actor &&
-          !workers.some(({ actor }) => actor == source.thread)
-        ) {
-          removed.push(source);
-        }
-      }
-    }
-    const tabs = removeSourcesFromTabList(oldTabs, removed);
-    dispatch({ type: "CLOSE_TABS", removed, tabs });
-
-    const sourceId = getNewSelectedSourceId(getState(), tabs);
-    dispatch(selectSource(sourceId));
-  };
-}
--- a/devtools/client/debugger/new/src/actions/tests/__snapshots__/pending-breakpoints.spec.js.snap
+++ b/devtools/client/debugger/new/src/actions/tests/__snapshots__/pending-breakpoints.spec.js.snap
@@ -29,17 +29,17 @@ Object {
 
 exports[`invalid breakpoint location a corrected corresponding pending breakpoint is added 1`] = `
 Object {
   "astLocation": Object {
     "index": 0,
     "name": undefined,
     "offset": Object {
       "line": 7,
-      "sourceId": "foo.js/originalSource",
+      "sourceId": "foo.js",
       "sourceUrl": "http://localhost:8000/examples/foo.js",
     },
   },
   "disabled": false,
   "generatedLocation": Object {
     "column": undefined,
     "line": 7,
     "sourceUrl": "http://localhost:8000/examples/foo.js",
--- a/devtools/client/debugger/new/src/actions/tests/ast.spec.js
+++ b/devtools/client/debugger/new/src/actions/tests/ast.spec.js
@@ -1,13 +1,15 @@
 /* eslint max-nested-callbacks: ["error", 6] */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
+// @flow
+
 import {
   createStore,
   selectors,
   actions,
   makeSource,
   makeOriginalSource,
   makeFrame,
   waitForState
@@ -22,18 +24,18 @@ const {
   getSourceMetaData,
   getInScopeLines,
   isSymbolsLoading
 } = selectors;
 
 import { prefs } from "../../utils/prefs";
 
 const threadClient = {
-  sourceContents: async sourceId => ({
-    source: sourceTexts[sourceId],
+  sourceContents: async ({ source }) => ({
+    source: sourceTexts[source],
     contentType: "text/javascript"
   }),
   setPausePoints: async () => {},
   getFrameScopes: async () => {},
   evaluate: async expression => ({ result: evaluationResult[expression] }),
   evaluateExpressions: async expressions =>
     expressions.map(expression => ({ result: evaluationResult[expression] }))
 };
@@ -59,88 +61,93 @@ const evaluationResult = {
   this: { actor: "this", preview: {} }
 };
 
 describe("ast", () => {
   describe("setPausePoints", () => {
     it("scopes", async () => {
       const store = createStore(threadClient);
       const { dispatch, getState } = store;
-      const source = makeSource("scopes.js");
-      await dispatch(actions.newSource(source));
-      await dispatch(actions.loadSourceText({ id: "scopes.js" }));
+      const csr = makeSource("scopes.js");
+      await dispatch(actions.newSource(csr));
+      await dispatch(actions.loadSourceText(csr.source));
       await dispatch(actions.setPausePoints("scopes.js"));
       await waitForState(store, state => {
-        const lines = getEmptyLines(state, source.id);
+        const lines = getEmptyLines(state, csr.source.id);
         return lines && lines.length > 0;
       });
 
-      const emptyLines = getEmptyLines(getState(), source.id);
+      const emptyLines = getEmptyLines(getState(), csr.source.id);
       expect(emptyLines).toMatchSnapshot();
     });
   });
 
   describe("setSourceMetaData", () => {
     it("should detect react components", async () => {
       const store = createStore(threadClient, {}, sourceMaps);
       const { dispatch, getState } = store;
-      const source = makeOriginalSource("reactComponent.js");
+      const csr = makeOriginalSource("reactComponent.js");
 
       await dispatch(actions.newSource(makeSource("reactComponent.js")));
 
-      await dispatch(actions.newSource(source));
+      await dispatch(actions.newSource(csr));
 
-      await dispatch(actions.loadSourceText(getSource(getState(), source.id)));
-      await dispatch(actions.setSourceMetaData(source.id));
+      await dispatch(
+        actions.loadSourceText(getSource(getState(), csr.source.id))
+      );
+      await dispatch(actions.setSourceMetaData(csr.source.id));
 
       await waitForState(store, state => {
-        const metaData = getSourceMetaData(state, source.id);
+        const metaData = getSourceMetaData(state, csr.source.id);
         return metaData && metaData.framework;
       });
 
-      const sourceMetaData = getSourceMetaData(getState(), source.id);
+      const sourceMetaData = getSourceMetaData(getState(), csr.source.id);
       expect(sourceMetaData.framework).toBe("React");
     });
 
     it("should not give false positive on non react components", async () => {
       const store = createStore(threadClient);
       const { dispatch, getState } = store;
       const base = makeSource("base.js");
       await dispatch(actions.newSource(base));
-      await dispatch(actions.loadSourceText({ id: "base.js" }));
+      await dispatch(actions.loadSourceText(base.source));
       await dispatch(actions.setSourceMetaData("base.js"));
 
-      const sourceMetaData = getSourceMetaData(getState(), base.id);
+      const sourceMetaData = getSourceMetaData(getState(), base.source.id);
       expect(sourceMetaData.framework).toBe(undefined);
     });
   });
 
   describe("setSymbols", () => {
     describe("when the source is loaded", () => {
       it("should be able to set symbols", async () => {
         const store = createStore(threadClient);
         const { dispatch, getState } = store;
         const base = makeSource("base.js");
         await dispatch(actions.newSource(base));
-        await dispatch(actions.loadSourceText({ id: "base.js" }));
+        await dispatch(actions.loadSourceText(base.source));
         await dispatch(actions.setSymbols("base.js"));
-        await waitForState(store, state => !isSymbolsLoading(state, base));
+        await waitForState(
+          store,
+          state => !isSymbolsLoading(state, base.source)
+        );
 
-        const baseSymbols = getSymbols(getState(), base);
+        const baseSymbols = getSymbols(getState(), base.source);
         expect(baseSymbols).toMatchSnapshot();
       });
     });
 
     describe("when the source is not loaded", () => {
       it("should return null", async () => {
         const { getState, dispatch } = createStore(threadClient);
         const base = makeSource("base.js");
         await dispatch(actions.newSource(base));
 
-        const baseSymbols = getSymbols(getState(), base);
+        const baseSymbols = getSymbols(getState(), base.source);
         expect(baseSymbols).toEqual(null);
       });
     });
 
     describe("when there is no source", () => {
       it("should return null", async () => {
         const { getState } = createStore(threadClient);
         const baseSymbols = getSymbols(getState());
@@ -152,28 +159,30 @@ describe("ast", () => {
   describe("getOutOfScopeLocations", () => {
     beforeEach(async () => {
       prefs.autoPrettyPrint = false;
     });
 
     it("with selected line", async () => {
       const store = createStore(threadClient);
       const { dispatch, getState } = store;
-      const source = makeSource("scopes.js");
-      await dispatch(actions.newSource(source));
+      const csr = makeSource("scopes.js");
+      await dispatch(actions.newSource(csr));
 
       await dispatch(
         actions.selectLocation({ sourceId: "scopes.js", line: 5 })
       );
 
+      const frame = makeFrame({ id: "1", sourceId: "scopes.js" });
       await dispatch(
         actions.paused({
           thread: "FakeThread",
           why: { type: "debuggerStatement" },
-          frames: [makeFrame({ id: "1", sourceId: "scopes.js" })]
+          frame,
+          frames: [frame]
         })
       );
 
       await dispatch(actions.setOutOfScopeLocations());
 
       await waitForState(store, state => getOutOfScopeLocations(state));
 
       const locations = getOutOfScopeLocations(getState());
--- a/devtools/client/debugger/new/src/actions/tests/expressions.spec.js
+++ b/devtools/client/debugger/new/src/actions/tests/expressions.spec.js
@@ -1,13 +1,22 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
-import { actions, selectors, createStore } from "../../utils/test-head";
+// @flow
+
+import {
+  actions,
+  selectors,
+  createStore,
+  makeSource
+} from "../../utils/test-head";
+
+import { makeMockFrame } from "../../utils/test-mockup";
 
 const mockThreadClient = {
   evaluateInFrame: (script, { frameId }) =>
     new Promise((resolve, reject) => {
       if (!frameId) {
         resolve("bla");
       } else {
         resolve("boo");
@@ -22,17 +31,17 @@ const mockThreadClient = {
               resolve("bla");
             } else {
               resolve("boo");
             }
           })
       )
     ),
   getFrameScopes: async () => {},
-  sourceContents: () => ({}),
+  sourceContents: () => ({ source: "", contentType: "text/javascript" }),
   autocomplete: () => {
     return new Promise(resolve => {
       resolve({
         from: "foo",
         matches: ["toLocaleString", "toSource", "toString", "toolbar", "top"],
         matchProp: "to"
       });
     });
@@ -45,17 +54,17 @@ describe("expressions", () => {
 
     await dispatch(actions.addExpression("foo"));
     expect(selectors.getExpressions(getState()).size).toBe(1);
   });
 
   it("should not add empty expressions", () => {
     const { dispatch, getState } = createStore(mockThreadClient);
 
-    dispatch(actions.addExpression());
+    dispatch(actions.addExpression((undefined: any)));
     dispatch(actions.addExpression(""));
 
     expect(selectors.getExpressions(getState()).size).toBe(0);
   });
 
   it("should not add invalid expressions", async () => {
     const { dispatch, getState } = createStore(mockThreadClient);
     await dispatch(actions.addExpression("foo#"));
@@ -129,33 +138,28 @@ describe("expressions", () => {
     expect(selectors.getExpression(getState(), "bar").value).toBe("boo");
   });
 
   it("should get the autocomplete matches for the input", async () => {
     const { dispatch, getState } = createStore(mockThreadClient);
 
     await dispatch(actions.autocomplete("to", 2));
 
-    expect(
-      selectors.getAutocompleteMatchset(getState(), "to")
-    ).toMatchSnapshot();
+    expect(selectors.getAutocompleteMatchset(getState())).toMatchSnapshot();
   });
 });
 
 async function createFrames(dispatch) {
-  const sourceId = "example.js";
-  const frame = {
-    id: "2",
-    location: { sourceId, line: 3 }
-  };
+  const frame = makeMockFrame();
 
-  await dispatch(actions.newSource({ id: sourceId }));
+  await dispatch(actions.newSource(makeSource("example.js")));
 
   await dispatch(
     actions.paused({
       thread: "UnknownThread",
+      frame,
       frames: [frame],
       why: { type: "just because" }
     })
   );
 
   await dispatch(actions.selectFrame(frame));
 }
--- a/devtools/client/debugger/new/src/actions/tests/file-search.spec.js
+++ b/devtools/client/debugger/new/src/actions/tests/file-search.spec.js
@@ -1,12 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
+// @flow
+
 import { createStore, selectors, actions } from "../../utils/test-head";
 
 const {
   getFileSearchQuery,
   getFileSearchModifiers,
   getFileSearchResults
 } = selectors;
 
--- a/devtools/client/debugger/new/src/actions/tests/helpers/threadClient.js
+++ b/devtools/client/debugger/new/src/actions/tests/helpers/threadClient.js
@@ -1,13 +1,13 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
-import { makeLocationId } from "../../../utils/breakpoint";
+import { makeBreakpointId } from "../../../utils/breakpoint";
 
 function createSource(name) {
   name = name.replace(/\..*$/, "");
   return {
     source: `function ${name}() {\n  return ${name} \n}`,
     contentType: "text/javascript"
   };
 }
@@ -35,40 +35,40 @@ export const simpleMockThreadClient = {
   setBreakpoint: (location, _condition) =>
     Promise.resolve({ id: "hi", actualLocation: location }),
 
   removeBreakpoint: _id => Promise.resolve(),
 
   setBreakpointOptions: (_id, _location, _options, _noSliding) =>
     Promise.resolve({ sourceId: "a", line: 5 }),
   setPausePoints: () => Promise.resolve({}),
-  sourceContents: sourceId =>
+  sourceContents: ({ source }) =>
     new Promise((resolve, reject) => {
-      if (sources.includes(sourceId)) {
-        resolve(createSource(sourceId));
+      if (sources.includes(source)) {
+        resolve(createSource(source));
       }
 
-      reject(`unknown source: ${sourceId}`);
+      reject(`unknown source: ${source}`);
     })
 };
 
 // Breakpoint Sliding
 function generateCorrectingThreadClient(offset = 0) {
   return {
     getBreakpointByLocation: jest.fn(),
     setBreakpoint: (location, condition) => {
       const actualLocation = { ...location, line: location.line + offset };
 
       return Promise.resolve({
-        id: makeLocationId(location),
+        id: makeBreakpointId(location),
         actualLocation,
         condition
       });
     },
-    sourceContents: sourceId => Promise.resolve(createSource(sourceId))
+    sourceContents: ({ source }) => Promise.resolve(createSource(source))
   };
 }
 
 /* in some cases, a breakpoint may be added, but the source will respond
  * with a different breakpoint location. This is due to the breakpoint being
  * added between functions, or somewhere that doesnt make sense. This function
  * simulates that behavior.
  * */
@@ -76,22 +76,22 @@ export function simulateCorrectThreadCli
   const correctedThreadClient = generateCorrectingThreadClient(offset);
   const offsetLine = { line: location.line + offset };
   const correctedLocation = { ...location, ...offsetLine };
   return { correctedThreadClient, correctedLocation };
 }
 
 // sources and tabs
 export const sourceThreadClient = {
-  sourceContents: function(sourceId) {
+  sourceContents: function({ source }) {
     return new Promise((resolve, reject) => {
-      if (sources.includes(sourceId)) {
-        resolve(createSource(sourceId));
+      if (sources.includes(source)) {
+        resolve(createSource(source));
       }
 
-      reject(`unknown source: ${sourceId}`);
+      reject(`unknown source: ${source}`);
     });
   },
   threadClient: async () => {},
   getFrameScopes: async () => {},
   setPausePoints: async () => {},
   evaluateExpressions: async () => {}
 };
--- a/devtools/client/debugger/new/src/actions/tests/navigation.spec.js
+++ b/devtools/client/debugger/new/src/actions/tests/navigation.spec.js
@@ -1,12 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
+// @flow
+
 import {
   createStore,
   selectors,
   actions,
   makeSource
 } from "../../utils/test-head";
 
 jest.mock("../../utils/editor");
@@ -26,19 +28,26 @@ const threadClient = {
       source: "function foo1() {\n  const foo = 5; return foo;\n}",
       contentType: "text/javascript"
     })
 };
 
 describe("navigation", () => {
   it("connect sets the debuggeeUrl", async () => {
     const { dispatch, getState } = createStore({
-      fetchWorkers: () => Promise.resolve([])
+      fetchWorkers: () => Promise.resolve([]),
+      getMainThread: () => "FakeThread"
     });
-    await dispatch(actions.connect("http://test.com/foo"));
+    await dispatch(
+      actions.connect(
+        "http://test.com/foo",
+        "actor",
+        false
+      )
+    );
     expect(selectors.getDebuggeeUrl(getState())).toEqual("http://test.com/foo");
   });
 
   it("navigation closes project-search", async () => {
     const { dispatch, getState } = createStore(threadClient);
     const mockQuery = "foo";
 
     await dispatch(actions.newSource(makeSource("foo1")));
--- a/devtools/client/debugger/new/src/actions/tests/pending-breakpoints.spec.js
+++ b/devtools/client/debugger/new/src/actions/tests/pending-breakpoints.spec.js
@@ -1,12 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
+// @flow
+
 // TODO: we would like to mock this in the local tests
 import {
   generateBreakpoint,
   mockPendingBreakpoint
 } from "./helpers/breakpoints.js";
 
 import {
   simulateCorrectThreadClient,
@@ -38,30 +40,32 @@ jest.mock("../../utils/prefs", () => ({
 
 import "../sources/loadSourceText";
 
 import {
   createStore,
   selectors,
   actions,
   makeOriginalSource,
-  waitForState
+  waitForState,
+  makeSource
 } from "../../utils/test-head";
 
 import { makePendingLocationId } from "../../utils/breakpoint";
 
 describe("when adding breakpoints", () => {
   it("a corresponding pending breakpoint should be added", async () => {
     const { dispatch, getState } = createStore(
       simpleMockThreadClient,
       loadInitialState()
     );
 
-    await dispatch(actions.newSource(makeOriginalSource("foo.js")));
-    await dispatch(actions.loadSourceText(makeOriginalSource("foo.js")));
+    const csr = makeOriginalSource("foo.js");
+    await dispatch(actions.newSource(csr));
+    await dispatch(actions.loadSourceText(csr.source));
 
     const bp = generateBreakpoint("foo.js");
     const id = makePendingLocationId(bp.location);
 
     await dispatch(actions.addBreakpoint(bp.location));
     const pendingBps = selectors.getPendingBreakpoints(getState());
 
     expect(selectors.getPendingBreakpointList(getState())).toHaveLength(2);
@@ -82,56 +86,65 @@ describe("when adding breakpoints", () =
     });
 
     it("add a corresponding pendingBreakpoint for each addition", async () => {
       const { dispatch, getState } = createStore(
         simpleMockThreadClient,
         loadInitialState()
       );
 
-      await dispatch(actions.newSource(makeOriginalSource("foo")));
-      await dispatch(actions.newSource(makeOriginalSource("foo2")));
+      const csr1 = makeOriginalSource("foo");
+      const csr2 = makeOriginalSource("foo2");
 
-      await dispatch(actions.loadSourceText(makeOriginalSource("foo")));
-      await dispatch(actions.loadSourceText(makeOriginalSource("foo2")));
+      await dispatch(actions.newSource(csr1));
+      await dispatch(actions.newSource(csr2));
+
+      await dispatch(actions.loadSourceText(csr1.source));
+      await dispatch(actions.loadSourceText(csr2.source));
 
       await dispatch(actions.addBreakpoint(breakpoint1.location));
       await dispatch(actions.addBreakpoint(breakpoint2.location));
 
       const pendingBps = selectors.getPendingBreakpoints(getState());
       expect(pendingBps[breakpointLocationId1]).toMatchSnapshot();
       expect(pendingBps[breakpointLocationId2]).toMatchSnapshot();
     });
 
     it("hidden breakponts do not create pending bps", async () => {
       const { dispatch, getState } = createStore(
         simpleMockThreadClient,
         loadInitialState()
       );
 
-      await dispatch(actions.newSource(makeOriginalSource("foo")));
-      await dispatch(actions.loadSourceText(makeOriginalSource("foo")));
+      const csr = makeOriginalSource("foo");
+      await dispatch(actions.newSource(csr));
+      await dispatch(actions.loadSourceText(csr.source));
 
       await dispatch(
         actions.addBreakpoint(breakpoint1.location, { hidden: true })
       );
       const pendingBps = selectors.getPendingBreakpoints(getState());
 
       expect(pendingBps[breakpointLocationId1]).toBeUndefined();
     });
 
     it("remove a corresponding pending breakpoint when deleting", async () => {
       const { dispatch, getState } = createStore(
         simpleMockThreadClient,
         loadInitialState()
       );
-      await dispatch(actions.newSource(makeOriginalSource("foo")));
-      await dispatch(actions.newSource(makeOriginalSource("foo2")));
-      await dispatch(actions.loadSourceText(makeOriginalSource("foo")));
-      await dispatch(actions.loadSourceText(makeOriginalSource("foo2")));
+
+      const csr1 = makeOriginalSource("foo");
+      const csr2 = makeOriginalSource("foo2");
+
+      await dispatch(actions.newSource(csr1));
+      await dispatch(actions.newSource(csr2));
+
+      await dispatch(actions.loadSourceText(csr1.source));
+      await dispatch(actions.loadSourceText(csr2.source));
 
       await dispatch(actions.addBreakpoint(breakpoint1.location));
       await dispatch(actions.addBreakpoint(breakpoint2.location));
       await dispatch(actions.removeBreakpoint(breakpoint1));
 
       const pendingBps = selectors.getPendingBreakpoints(getState());
       expect(pendingBps.hasOwnProperty(breakpointLocationId1)).toBe(false);
       expect(pendingBps.hasOwnProperty(breakpointLocationId2)).toBe(true);
@@ -142,18 +155,20 @@ describe("when adding breakpoints", () =
 describe("when changing an existing breakpoint", () => {
   it("updates corresponding pendingBreakpoint", async () => {
     const { dispatch, getState } = createStore(
       simpleMockThreadClient,
       loadInitialState()
     );
     const bp = generateBreakpoint("foo");
     const id = makePendingLocationId(bp.location);
-    await dispatch(actions.newSource(makeOriginalSource("foo")));
-    await dispatch(actions.loadSourceText(makeOriginalSource("foo")));
+
+    const csr = makeOriginalSource("foo");
+    await dispatch(actions.newSource(csr));
+    await dispatch(actions.loadSourceText(csr.source));
 
     await dispatch(actions.addBreakpoint(bp.location));
     await dispatch(
       actions.setBreakpointOptions(bp.location, { condition: "2" })
     );
     const bps = selectors.getPendingBreakpoints(getState());
     const breakpoint = bps[id];
     expect(breakpoint.options.condition).toBe("2");
@@ -162,35 +177,37 @@ describe("when changing an existing brea
   it("if disabled, updates corresponding pendingBreakpoint", async () => {
     const { dispatch, getState } = createStore(
       simpleMockThreadClient,
       loadInitialState()
     );
     const bp = generateBreakpoint("foo");
     const id = makePendingLocationId(bp.location);
 
-    await dispatch(actions.newSource(makeOriginalSource("foo")));
-    await dispatch(actions.loadSourceText(makeOriginalSource("foo")));
+    const csr = makeOriginalSource("foo");
+    await dispatch(actions.newSource(csr));
+    await dispatch(actions.loadSourceText(csr.source));
 
     await dispatch(actions.addBreakpoint(bp.location));
     await dispatch(actions.disableBreakpoint(bp));
     const bps = selectors.getPendingBreakpoints(getState());
     const breakpoint = bps[id];
     expect(breakpoint.disabled).toBe(true);
   });
 
   it("does not delete the pre-existing pendingBreakpoint", async () => {
     const { dispatch, getState } = createStore(
       simpleMockThreadClient,
       loadInitialState()
     );
     const bp = generateBreakpoint("foo.js");
-    const source = makeOriginalSource("foo.js");
-    await dispatch(actions.newSource(source));
-    await dispatch(actions.loadSourceText(makeOriginalSource("foo.js")));
+
+    const csr = makeOriginalSource("foo.js");
+    await dispatch(actions.newSource(csr));
+    await dispatch(actions.loadSourceText(csr.source));
 
     const id = makePendingLocationId(bp.location);
 
     await dispatch(actions.addBreakpoint(bp.location));
     await dispatch(
       actions.setBreakpointOptions(bp.location, { condition: "2" })
     );
     const bps = selectors.getPendingBreakpoints(getState());
@@ -210,122 +227,128 @@ describe("initializing when pending brea
   });
 
   it("re-adding breakpoints update existing pending breakpoints", async () => {
     const { dispatch, getState } = createStore(
       simpleMockThreadClient,
       loadInitialState()
     );
     const bar = generateBreakpoint("bar.js");
-    await dispatch(actions.newSource(makeOriginalSource("bar.js")));
-    await dispatch(actions.loadSourceText(makeOriginalSource("bar.js")));
+
+    const csr = makeOriginalSource("bar.js");
+    await dispatch(actions.newSource(csr));
+    await dispatch(actions.loadSourceText(csr.source));
 
     await dispatch(actions.addBreakpoint(bar.location));
 
     const bps = selectors.getPendingBreakpointList(getState());
     expect(bps).toHaveLength(1);
   });
 
   it("adding bps doesn't remove existing pending breakpoints", async () => {
     const { dispatch, getState } = createStore(
       simpleMockThreadClient,
       loadInitialState()
     );
     const bp = generateBreakpoint("foo.js");
 
-    await dispatch(actions.newSource(makeOriginalSource("foo.js")));
-    await dispatch(actions.loadSourceText(makeOriginalSource("foo.js")));
+    const csr = makeOriginalSource("foo.js");
+    await dispatch(actions.newSource(csr));
+    await dispatch(actions.loadSourceText(csr.source));
 
     await dispatch(actions.addBreakpoint(bp.location));
 
     const bps = selectors.getPendingBreakpointList(getState());
     expect(bps).toHaveLength(2);
   });
 });
 
 describe("initializing with disabled pending breakpoints in prefs", () => {
   it("syncs breakpoints with pending breakpoints", async () => {
     const store = createStore(
       simpleMockThreadClient,
       loadInitialState({ disabled: true })
     );
 
     const { getState, dispatch } = store;
-    const source = makeOriginalSource("bar.js");
+    const csr = makeOriginalSource("bar.js");
 
-    await dispatch(actions.newSource(source));
-    await dispatch(actions.loadSourceText(source));
+    await dispatch(actions.newSource(csr));
+    await dispatch(actions.loadSourceText(csr.source));
 
     await waitForState(store, state => {
-      const bps = selectors.getBreakpointsForSource(state, source.id);
+      const bps = selectors.getBreakpointsForSource(state, csr.source.id);
       return bps && Object.values(bps).length > 0;
     });
 
     const bp = selectors.getBreakpointForLocation(getState(), {
       line: 5,
       column: undefined,
-      sourceUrl: source.url,
-      sourceId: source.id
+      sourceUrl: csr.source.url,
+      sourceId: csr.source.id
     });
-    expect(bp.location.sourceId).toEqual(source.id);
+    if (!bp) {
+      throw new Error("no bp");
+    }
+    expect(bp.location.sourceId).toEqual(csr.source.id);
     expect(bp.disabled).toEqual(true);
   });
 });
 
 describe("adding sources", () => {
   it("corresponding breakpoints are added for a single source", async () => {
     const store = createStore(simpleMockThreadClient, loadInitialState());
     const { getState, dispatch } = store;
 
     expect(selectors.getBreakpointCount(getState())).toEqual(0);
 
-    const source = makeOriginalSource("bar.js");
-    await dispatch(actions.newSource(source));
-    await dispatch(actions.loadSourceText(makeOriginalSource("bar.js")));
+    const csr = makeOriginalSource("bar.js");
+    await dispatch(actions.newSource(csr));
+    await dispatch(actions.loadSourceText(csr.source));
 
     await waitForState(store, state => selectors.getBreakpointCount(state) > 0);
 
     expect(selectors.getBreakpointCount(getState())).toEqual(1);
   });
 
   it("corresponding breakpoints are added to the original source", async () => {
-    const source = makeOriginalSource("bar.js", { sourceMapURL: "foo" });
+    const csr = makeOriginalSource("bar.js", { sourceMapURL: "foo" });
     const store = createStore(simpleMockThreadClient, loadInitialState(), {
-      getOriginalURLs: async () => [source.url],
+      getOriginalURLs: async () => [csr.source.url],
       getOriginalSourceText: async () => ({ source: "" }),
       getGeneratedLocation: async (location, _source) => ({
         line: location.line,
         column: location.column,
         sourceId: _source.id
       }),
       getOriginalLocation: async location => location
     });
 
     const { getState, dispatch } = store;
 
     expect(selectors.getBreakpointCount(getState())).toEqual(0);
 
-    await dispatch(actions.newSource(source));
+    await dispatch(actions.newSource(csr));
 
     await waitForState(store, state => selectors.getBreakpointCount(state) > 0);
 
     expect(selectors.getBreakpointCount(getState())).toEqual(1);
   });
 
   it("add corresponding breakpoints for multiple sources", async () => {
     const store = createStore(simpleMockThreadClient, loadInitialState());
     const { getState, dispatch } = store;
 
     expect(selectors.getBreakpointCount(getState())).toEqual(0);
 
-    const source1 = makeOriginalSource("bar.js");
-    const source2 = makeOriginalSource("foo.js");
-    await dispatch(actions.newSources([source1, source2]));
-    await dispatch(actions.loadSourceText(makeOriginalSource("foo.js")));
-    await dispatch(actions.loadSourceText(makeOriginalSource("bar.js")));
+    const csr1 = makeOriginalSource("bar.js");
+    const csr2 = makeOriginalSource("foo.js");
+    await dispatch(actions.newSources([csr1, csr2]));
+    await dispatch(actions.loadSourceText(csr1.source));
+    await dispatch(actions.loadSourceText(csr2.source));
 
     await waitForState(store, state => selectors.getBreakpointCount(state) > 0);
 
     expect(selectors.getBreakpointCount(getState())).toEqual(1);
   });
 });
 
 describe("invalid breakpoint location", () => {
@@ -335,17 +358,23 @@ describe("invalid breakpoint location", 
     const {
       correctedThreadClient,
       correctedLocation
     } = simulateCorrectThreadClient(2, bp.location);
     const { dispatch, getState } = createStore(correctedThreadClient);
     const correctedPendingId = makePendingLocationId(correctedLocation);
 
     // test
-    await dispatch(actions.newSource(makeOriginalSource("foo.js")));
-    await dispatch(actions.loadSourceText(makeOriginalSource("foo.js")));
+    const csr = makeSource("foo.js");
+    await dispatch(actions.newSource(csr));
+    await dispatch(actions.loadSourceText(csr.source));
+
+    // Fixup the breakpoint so that its location can be loaded.
+    bp.location.sourceId = "foo.js";
+    bp.generatedLocation = { ...bp.location };
 
     await dispatch(actions.addBreakpoint(bp.location));
     const pendingBps = selectors.getPendingBreakpoints(getState());
+
     const pendingBp = pendingBps[correctedPendingId];
     expect(pendingBp).toMatchSnapshot();
   });
 });
--- a/devtools/client/debugger/new/src/actions/tests/project-text-search.spec.js
+++ b/devtools/client/debugger/new/src/actions/tests/project-text-search.spec.js
@@ -1,30 +1,32 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
+// @flow
+
 import {
   actions,
   createStore,
   selectors,
   makeSource
 } from "../../utils/test-head";
 
 const {
   getSource,
   getTextSearchQuery,
   getTextSearchResults,
   getTextSearchStatus
 } = selectors;
 
 const threadClient = {
-  sourceContents: function(sourceId) {
+  sourceContents: function({ source }) {
     return new Promise((resolve, reject) => {
-      switch (sourceId) {
+      switch (source) {
         case "foo1":
           resolve({
             source: "function foo1() {\n  const foo = 5; return foo;\n}",
             contentType: "text/javascript"
           });
           break;
         case "foo2":
           resolve({
@@ -41,17 +43,17 @@ const threadClient = {
         case "bar:formatted":
           resolve({
             source: "function bla(x, y) {\n const bar = 4; return 2;\n}",
             contentType: "text/javascript"
           });
           break;
       }
 
-      reject(`unknown source: ${sourceId}`);
+      reject(`unknown source: ${source}`);
     });
   }
 };
 
 describe("project text search", () => {
   it("should add a project text search query", () => {
     const { dispatch, getState } = createStore();
     const mockQuery = "foo";
@@ -59,61 +61,66 @@ describe("project text search", () => {
     dispatch(actions.addSearchQuery(mockQuery));
 
     expect(getTextSearchQuery(getState())).toEqual(mockQuery);
   });
 
   it("should search all the loaded sources based on the query", async () => {
     const { dispatch, getState } = createStore(threadClient);
     const mockQuery = "foo";
-    const source1 = makeSource("foo1");
-    const source2 = makeSource("foo2");
+    const csr1 = makeSource("foo1");
+    const csr2 = makeSource("foo2");
 
-    await dispatch(actions.newSource(source1));
-    await dispatch(actions.newSource(source2));
+    await dispatch(actions.newSource(csr1));
+    await dispatch(actions.newSource(csr2));
 
     await dispatch(actions.searchSources(mockQuery));
 
     const results = getTextSearchResults(getState());
     expect(results).toMatchSnapshot();
   });
 
   it("should ignore sources with minified versions", async () => {
-    const source1 = makeSource("bar", { sourceMapURL: "bar:formatted" });
-    const source2 = makeSource("bar:formatted");
+    const csr1 = makeSource("bar", { sourceMapURL: "bar:formatted" });
+    const csr2 = makeSource("bar:formatted");
 
     const mockMaps = {
       getOriginalSourceText: async () => ({
         source: "function bla(x, y) {\n const bar = 4; return 2;\n}",
         contentType: "text/javascript"
       }),
-      getOriginalURLs: async () => [source2.url]
+      getOriginalURLs: async () => [csr2.source.url]
     };
 
     const { dispatch, getState } = createStore(threadClient, {}, mockMaps);
     const mockQuery = "bla";
 
-    await dispatch(actions.newSource(source1));
-    await dispatch(actions.newSource(source2));
+    await dispatch(actions.newSource(csr1));
+    await dispatch(actions.newSource(csr2));
 
     await dispatch(actions.searchSources(mockQuery));
 
     const results = getTextSearchResults(getState());
     expect(results).toMatchSnapshot();
   });
 
   it("should search a specific source", async () => {
     const { dispatch, getState } = createStore(threadClient);
 
-    await dispatch(actions.newSource(makeSource("bar")));
-    await dispatch(actions.loadSourceText({ id: "bar" }));
+    const csr = makeSource("bar");
+    await dispatch(actions.newSource(csr));
+    await dispatch(actions.loadSourceText(csr.source));
 
     dispatch(actions.addSearchQuery("bla"));
 
-    const sourceId = getSource(getState(), "bar").id;
+    const barSource = getSource(getState(), "bar");
+    if (!barSource) {
+      throw new Error("no barSource");
+    }
+    const sourceId = barSource.id;
 
     await dispatch(actions.searchSource(sourceId, "bla"), "bla");
 
     const results = getTextSearchResults(getState());
 
     expect(results).toMatchSnapshot();
     expect(results).toHaveLength(1);
   });
--- a/devtools/client/debugger/new/src/actions/tests/setProjectDirectoryRoot.spec.js
+++ b/devtools/client/debugger/new/src/actions/tests/setProjectDirectoryRoot.spec.js
@@ -1,19 +1,23 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
+// @flow
+
 import {
   createStore,
   selectors,
   actions,
   makeSource
 } from "../../utils/test-head";
 
+import type { Source } from "../../types";
+
 const { getProjectDirectoryRoot, getRelativeSources } = selectors;
 
 describe("setProjectDirectoryRoot", () => {
   it("should set domain directory as root", async () => {
     const { dispatch, getState } = createStore();
     dispatch(actions.setProjectDirectoryRoot("example.com"));
     expect(getProjectDirectoryRoot(getState())).toBe("example.com");
   });
@@ -44,17 +48,17 @@ describe("setProjectDirectoryRoot", () =
     const { dispatch, getState } = store;
     await dispatch(actions.newSource(makeSource("js/scopes.js")));
     await dispatch(actions.newSource(makeSource("lib/vendor.js")));
 
     dispatch(actions.setProjectDirectoryRoot("localhost:8000/examples/js"));
 
     const filteredSourcesByThread = getRelativeSources(getState());
     const filteredSources = Object.values(filteredSourcesByThread)[0];
-    const firstSource = Object.values(filteredSources)[0];
+    const firstSource: Source = (Object.values(filteredSources)[0]: any);
 
     expect(firstSource.url).toEqual(
       "http://localhost:8000/examples/js/scopes.js"
     );
 
     expect(firstSource.relativeUrl).toEqual("scopes.js");
   });
 
--- a/devtools/client/debugger/new/src/actions/tests/source-tree.spec.js
+++ b/devtools/client/debugger/new/src/actions/tests/source-tree.spec.js
@@ -1,17 +1,19 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
+// @flow
+
 import { actions, selectors, createStore } from "../../utils/test-head";
 const { getExpandedState } = selectors;
 
 describe("source tree", () => {
   it("should set the expanded state", () => {
     const { dispatch, getState } = createStore();
     const expandedState = new Set(["foo", "bar"]);
 
-    expect(getExpandedState(getState())).toEqual(undefined);
+    expect(getExpandedState(getState(), "FakeThread")).toEqual(undefined);
     dispatch(actions.setExpandedState("FakeThread", expandedState));
     expect(getExpandedState(getState(), "FakeThread")).toEqual(expandedState);
   });
 });
--- a/devtools/client/debugger/new/src/actions/tests/tabs.spec.js
+++ b/devtools/client/debugger/new/src/actions/tests/tabs.spec.js
@@ -1,123 +1,129 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
+// @flow
+
 import {
   actions,
   selectors,
   createStore,
   makeSource
 } from "../../utils/test-head";
 const { getSelectedSource, getSourceTabs } = selectors;
 
 import { sourceThreadClient as threadClient } from "./helpers/threadClient.js";
 
 describe("closing tabs", () => {
   it("closing a tab", async () => {
     const { dispatch, getState } = createStore(threadClient);
 
-    const fooSource = makeSource("foo.js");
-    await dispatch(actions.newSource(fooSource));
-    await dispatch(actions.selectLocation({ sourceId: "foo.js" }));
-    dispatch(actions.closeTab(fooSource));
+    const fooCSR = makeSource("foo.js");
+    await dispatch(actions.newSource(fooCSR));
+    await dispatch(actions.selectLocation({ sourceId: "foo.js", line: 1 }));
+    dispatch(actions.closeTab(fooCSR.source));
 
     expect(getSelectedSource(getState())).toBe(undefined);
     expect(getSourceTabs(getState())).toHaveLength(0);
   });
 
   it("closing the inactive tab", async () => {
     const { dispatch, getState } = createStore(threadClient);
 
-    const fooSource = makeSource("foo.js");
-    await dispatch(actions.newSource(fooSource));
+    const fooCSR = makeSource("foo.js");
+    await dispatch(actions.newSource(fooCSR));
     await dispatch(actions.newSource(makeSource("bar.js")));
-    await dispatch(actions.selectLocation({ sourceId: "foo.js" }));
-    await dispatch(actions.selectLocation({ sourceId: "bar.js" }));
-    dispatch(actions.closeTab(fooSource));
+    await dispatch(actions.selectLocation({ sourceId: "foo.js", line: 1 }));
+    await dispatch(actions.selectLocation({ sourceId: "bar.js", line: 1 }));
+    dispatch(actions.closeTab(fooCSR.source));
 
-    expect(getSelectedSource(getState()).id).toBe("bar.js");
+    const selected = getSelectedSource(getState());
+    expect(selected && selected.id).toBe("bar.js");
     expect(getSourceTabs(getState())).toHaveLength(1);
   });
 
   it("closing the only tab", async () => {
     const { dispatch, getState } = createStore(threadClient);
 
-    const fooSource = makeSource("foo.js");
-    await dispatch(actions.newSource(fooSource));
-    await dispatch(actions.selectLocation({ sourceId: "foo.js" }));
-    dispatch(actions.closeTab(fooSource));
+    const fooCSR = makeSource("foo.js");
+    await dispatch(actions.newSource(fooCSR));
+    await dispatch(actions.selectLocation({ sourceId: "foo.js", line: 1 }));
+    dispatch(actions.closeTab(fooCSR.source));
 
     expect(getSelectedSource(getState())).toBe(undefined);
     expect(getSourceTabs(getState())).toHaveLength(0);
   });
 
   it("closing the active tab", async () => {
     const { dispatch, getState } = createStore(threadClient);
 
-    const barSource = makeSource("bar.js");
+    const barCSR = makeSource("bar.js");
     await dispatch(actions.newSource(makeSource("foo.js")));
-    await dispatch(actions.newSource(barSource));
-    await dispatch(actions.selectLocation({ sourceId: "foo.js" }));
-    await dispatch(actions.selectLocation({ sourceId: "bar.js" }));
-    await dispatch(actions.closeTab(barSource));
+    await dispatch(actions.newSource(barCSR));
+    await dispatch(actions.selectLocation({ sourceId: "foo.js", line: 1 }));
+    await dispatch(actions.selectLocation({ sourceId: "bar.js", line: 1 }));
+    await dispatch(actions.closeTab(barCSR.source));
 
-    expect(getSelectedSource(getState()).id).toBe("foo.js");
+    const selected = getSelectedSource(getState());
+    expect(selected && selected.id).toBe("foo.js");
     expect(getSourceTabs(getState())).toHaveLength(1);
   });
 
   it("closing many inactive tabs", async () => {
     const { dispatch, getState } = createStore(threadClient);
 
-    const fooSource = makeSource("foo.js");
-    const barSource = makeSource("bar.js");
-    await dispatch(actions.newSource(fooSource));
-    await dispatch(actions.newSource(barSource));
+    const fooCSR = makeSource("foo.js");
+    const barCSR = makeSource("bar.js");
+    await dispatch(actions.newSource(fooCSR));
+    await dispatch(actions.newSource(barCSR));
     await dispatch(actions.newSource(makeSource("bazz.js")));
-    await dispatch(actions.selectLocation({ sourceId: "foo.js" }));
-    await dispatch(actions.selectLocation({ sourceId: "bar.js" }));
-    await dispatch(actions.selectLocation({ sourceId: "bazz.js" }));
+    await dispatch(actions.selectLocation({ sourceId: "foo.js", line: 1 }));
+    await dispatch(actions.selectLocation({ sourceId: "bar.js", line: 1 }));
+    await dispatch(actions.selectLocation({ sourceId: "bazz.js", line: 1 }));
 
     const tabs = [
       "http://localhost:8000/examples/foo.js",
       "http://localhost:8000/examples/bar.js"
     ];
     dispatch(actions.closeTabs(tabs));
 
-    expect(getSelectedSource(getState()).id).toBe("bazz.js");
+    const selected = getSelectedSource(getState());
+    expect(selected && selected.id).toBe("bazz.js");
     expect(getSourceTabs(getState())).toHaveLength(1);
   });
 
   it("closing many tabs including the active tab", async () => {
     const { dispatch, getState } = createStore(threadClient);
 
     await dispatch(actions.newSource(makeSource("foo.js")));
     await dispatch(actions.newSource(makeSource("bar.js")));
     await dispatch(actions.newSource(makeSource("bazz.js")));
-    await dispatch(actions.selectLocation({ sourceId: "foo.js" }));
-    await dispatch(actions.selectLocation({ sourceId: "bar.js" }));
-    await dispatch(actions.selectLocation({ sourceId: "bazz.js" }));
+    await dispatch(actions.selectLocation({ sourceId: "foo.js", line: 1 }));
+    await dispatch(actions.selectLocation({ sourceId: "bar.js", line: 1 }));
+    await dispatch(actions.selectLocation({ sourceId: "bazz.js", line: 1 }));
     const tabs = [
       "http://localhost:8000/examples/bar.js",
       "http://localhost:8000/examples/bazz.js"
     ];
     await dispatch(actions.closeTabs(tabs));
 
-    expect(getSelectedSource(getState()).id).toBe("foo.js");
+    const selected = getSelectedSource(getState());
+    expect(selected && selected.id).toBe("foo.js");
     expect(getSourceTabs(getState())).toHaveLength(1);
   });
 
   it("closing all the tabs", async () => {
     const { dispatch, getState } = createStore(threadClient);
 
     await dispatch(actions.newSource(makeSource("foo.js")));
     await dispatch(actions.newSource(makeSource("bar.js")));
-    await dispatch(actions.selectLocation({ sourceId: "foo.js" }));
-    await dispatch(actions.selectLocation({ sourceId: "bar.js" }));
+    await dispatch(actions.selectLocation({ sourceId: "foo.js", line: 1 }));
+    await dispatch(actions.selectLocation({ sourceId: "bar.js", line: 1 }));
     await dispatch(
       actions.closeTabs([
         "http://localhost:8000/examples/foo.js",
         "http://localhost:8000/examples/bar.js"
       ])
     );
 
     expect(getSelectedSource(getState())).toBe(undefined);
--- a/devtools/client/debugger/new/src/actions/tests/ui.spec.js
+++ b/devtools/client/debugger/new/src/actions/tests/ui.spec.js
@@ -1,28 +1,32 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
+// @flow
+
 import {
   createStore,
   selectors,
   actions,
   makeSource
 } from "../../utils/test-head";
 
 const {
   getActiveSearch,
   getFrameworkGroupingState,
   getPaneCollapse,
   getHighlightedLineRange,
   getProjectDirectoryRoot,
   getRelativeSources
 } = selectors;
 
+import type { Source } from "../../types";
+
 describe("ui", () => {
   it("should toggle the visible state of project search", () => {
     const { dispatch, getState } = createStore();
     expect(getActiveSearch(getState())).toBe(null);
     dispatch(actions.setActiveSearch("project"));
     expect(getActiveSearch(getState())).toBe("project");
   });
 
@@ -60,24 +64,24 @@ describe("ui", () => {
     const { dispatch, getState } = createStore();
     const currentState = getFrameworkGroupingState(getState());
     dispatch(actions.toggleFrameworkGrouping(!currentState));
     expect(getFrameworkGroupingState(getState())).toBe(!currentState);
   });
 
   it("should highlight lines", () => {
     const { dispatch, getState } = createStore();
-    const range = { start: 3, end: 5, sourceId: 2 };
+    const range = { start: 3, end: 5, sourceId: "2" };
     dispatch(actions.highlightLineRange(range));
     expect(getHighlightedLineRange(getState())).toEqual(range);
   });
 
   it("should clear highlight lines", () => {
     const { dispatch, getState } = createStore();
-    const range = { start: 3, end: 5, sourceId: 2 };
+    const range = { start: 3, end: 5, sourceId: "2" };
     dispatch(actions.highlightLineRange(range));
     dispatch(actions.clearHighlightLineRange());
     expect(getHighlightedLineRange(getState())).toEqual({});
   });
 });
 
 describe("setProjectDirectoryRoot", () => {
   it("should set domain directory as root", () => {
@@ -112,17 +116,17 @@ describe("setProjectDirectoryRoot", () =
     const { dispatch, getState } = store;
     await dispatch(actions.newSource(makeSource("js/scopes.js")));
     await dispatch(actions.newSource(makeSource("lib/vendor.js")));
 
     dispatch(actions.setProjectDirectoryRoot("localhost:8000/examples/js"));
 
     const filteredSourcesByThread = getRelativeSources(getState());
     const filteredSources = Object.values(filteredSourcesByThread)[0];
-    const firstSource = Object.values(filteredSources)[0];
+    const firstSource: Source = (Object.values(filteredSources)[0]: any);
 
     expect(firstSource.url).toEqual(
       "http://localhost:8000/examples/js/scopes.js"
     );
 
     expect(firstSource.relativeUrl).toEqual("scopes.js");
   });
 
--- a/devtools/client/debugger/new/src/actions/toolbox.js
+++ b/devtools/client/debugger/new/src/actions/toolbox.js
@@ -1,58 +1,36 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
-const { isDevelopment } = require("devtools-environment");
-
 import type { ThunkArgs } from "./types";
 import type { Worker, Grip } from "../types";
 
 /**
  * @memberof actions/toolbox
  * @static
  */
 export function openLink(url: string) {
-  return async function({ openLink: openLinkCommand }: ThunkArgs) {
-    if (isDevelopment()) {
-      const win = window.open(url, "_blank");
-      win.focus();
-    } else {
-      openLinkCommand(url);
-    }
+  return async function({ panel }: ThunkArgs) {
+    return panel.openLink(url);
   };
 }
 
 export function openWorkerToolbox(worker: Worker) {
-  return async function({
-    getState,
-    openWorkerToolbox: openWorkerToolboxCommand
-  }: ThunkArgs) {
-    if (isDevelopment()) {
-      alert(worker.url);
-    } else {
-      openWorkerToolboxCommand(worker);
-    }
+  return async function({ getState, panel }: ThunkArgs) {
+    return panel.openWorkerToolbox(worker);
   };
 }
 
 export function evaluateInConsole(inputString: string) {
-  return async ({ openConsoleAndEvaluate }: ThunkArgs) => {
-    if (isDevelopment()) {
-      alert(`console.log: ${inputString}`);
-    } else {
-      return openConsoleAndEvaluate(inputString);
-    }
+  return async ({ panel }: ThunkArgs) => {
+    return panel.openConsoleAndEvaluate(inputString);
   };
 }
 
 export function openElementInInspectorCommand(grip: Grip) {
-  return async ({ openElementInInspector }: ThunkArgs) => {
-    if (isDevelopment()) {
-      alert(`Opening node in Inspector: ${grip.class}`);
-    } else {
-      return openElementInInspector(grip);
-    }
+  return async ({ panel }: ThunkArgs) => {
+    return panel.openElementInInspector(grip);
   };
 }
--- a/devtools/client/debugger/new/src/actions/types/SourceAction.js
+++ b/devtools/client/debugger/new/src/actions/types/SourceAction.js
@@ -1,15 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
-import type { Source, SourceLocation } from "../../types";
+import type { Source, SourceActor, SourceLocation } from "../../types";
 import type { PromiseAction } from "../utils/middleware/promise";
 
 export type LoadSourceAction = PromiseAction<
   {|
     +type: "LOAD_SOURCE_TEXT",
     +sourceId: string
   |},
   Source
@@ -17,17 +17,18 @@ export type LoadSourceAction = PromiseAc
 export type SourceAction =
   | LoadSourceAction
   | {|
       +type: "ADD_SOURCE",
       +source: Source
     |}
   | {|
       +type: "ADD_SOURCES",
-      +sources: Array<Source>
+      +sources: Array<Source>,
+      +sourceActors: SourceActor[]
     |}
   | {|
       +type: "UPDATE_SOURCE",
       +source: Source
     |}
   | {|
       +type: "SET_SELECTED_LOCATION",
       +source: Source,
--- a/devtools/client/debugger/new/src/actions/types/index.js
+++ b/devtools/client/debugger/new/src/actions/types/index.js
@@ -1,33 +1,27 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
-import type {
-  Frame,
-  Scope,
-  Why,
-  Worker,
-  WorkerList,
-  MainThread
-} from "../../types";
+import type { Frame, Scope, Why, WorkerList, MainThread } from "../../types";
 import type { State } from "../../reducers/types";
 import type { MatchedLocations } from "../../reducers/file-search";
 import type { TreeNode } from "../../utils/sources-tree/types";
 import type { SearchOperation } from "../../reducers/project-text-search";
 
 import type { BreakpointAction } from "./BreakpointAction";
 import type { SourceAction } from "./SourceAction";
 import type { UIAction } from "./UIAction";
 import type { PauseAction } from "./PauseAction";
 import type { ASTAction } from "./ASTAction";
 import { clientCommands } from "../../client/firefox";
+import type { Panel } from "../../client/firefox/types";
 
 /**
  * Flow types
  * @module actions/types
  */
 
 /**
  * Argument parameters via Thunk middleware for {@link https://github.com/gaearon/redux-thunk|Redux Thunk}
@@ -36,20 +30,17 @@ import { clientCommands } from "../../cl
  * @static
  * @typedef {Object} ThunkArgs
  */
 export type ThunkArgs = {
   dispatch: (action: any) => Promise<any>,
   getState: () => State,
   client: typeof clientCommands,
   sourceMaps: any,
-  openLink: (url: string) => void,
-  openWorkerToolbox: (worker: Worker) => void,
-  openElementInInspector: (grip: Object) => void,
-  onReload: () => void
+  panel: Panel
 };
 
 export type Thunk = ThunkArgs => any;
 
 export type ActionType = Object | Function;
 
 type ProjectTextSearchResult = {
   sourceId: string,
@@ -57,27 +48,25 @@ type ProjectTextSearchResult = {
   matches: MatchedLocations[]
 };
 
 type AddTabAction = {|
   +type: "ADD_TAB",
   +url: string,
   +framework?: string,
   +isOriginal?: boolean,
-  +sourceId?: string,
-  +thread: string
+  +sourceId?: string
 |};
 
 type UpdateTabAction = {|
   +type: "UPDATE_TAB",
   +url: string,
   +framework?: string,
   +isOriginal?: boolean,
-  +sourceId?: string,
-  +thread: string
+  +sourceId?: string
 |};
 
 type ReplayAction =
   | {|
       +type: "TRAVEL_TO",
       +data: {
         paused: {
           why: Why,
@@ -145,17 +134,18 @@ export type FileTextSearchAction =
 export type QuickOpenAction =
   | {| +type: "SET_QUICK_OPEN_QUERY", +query: string |}
   | {| +type: "OPEN_QUICK_OPEN", +query?: string |}
   | {| +type: "CLOSE_QUICK_OPEN" |};
 
 export type DebugeeAction =
   | {|
       +type: "SET_WORKERS",
-      +workers: WorkerList
+      +workers: WorkerList,
+      +mainThread: string
     |}
   | {|
       +type: "SELECT_THREAD",
       +thread: string
     |};
 
 export type {
   StartPromiseAction,
--- a/devtools/client/debugger/new/src/client/firefox/commands.js
+++ b/devtools/client/debugger/new/src/client/firefox/commands.js
@@ -1,70 +1,70 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
 import type {
-  BreakpointId,
+  ActorId,
   BreakpointOptions,
   BreakpointResult,
   EventListenerBreakpoints,
   Frame,
   FrameId,
-  SourceLocation,
   Script,
-  Source,
   SourceId,
+  SourceActor,
+  SourceActorLocation,
   Worker
 } from "../../types";
 
 import type {
   TabTarget,
   DebuggerClient,
   Grip,
   ThreadClient,
   ObjectClient,
-  BPClients
+  BPClients,
+  SourcesPacket
 } from "./types";
 
 import type { PausePointsMap } from "../../workers/parser";
 
-import { makePendingLocationId } from "../../utils/breakpoint";
+import { makeBreakpointActorId } from "../../utils/breakpoint";
 
 import { createSource, createBreakpointLocation, createWorker } from "./create";
-import { originalToGeneratedId, isOriginalId } from "devtools-source-map";
 import { supportsWorkers, updateWorkerClients } from "./workers";
 
 import { features } from "../../utils/prefs";
 
 let bpClients: BPClients;
 let workerClients: Object;
-let sourceThreads: Object;
 let threadClient: ThreadClient;
 let tabTarget: TabTarget;
 let debuggerClient: DebuggerClient;
+let sourceActors: { [ActorId]: SourceId };
 let supportsWasm: boolean;
 
 type Dependencies = {
   threadClient: ThreadClient,
   tabTarget: TabTarget,
   debuggerClient: DebuggerClient,
   supportsWasm: boolean
 };
 
 function setupCommands(dependencies: Dependencies): { bpClients: BPClients } {
   threadClient = dependencies.threadClient;
   tabTarget = dependencies.tabTarget;
   debuggerClient = dependencies.debuggerClient;
   supportsWasm = dependencies.supportsWasm;
   bpClients = {};
   workerClients = {};
-  sourceThreads = {};
+  sourceActors = {};
 
   return { bpClients };
 }
 
 function createObjectClient(grip: Grip) {
   return debuggerClient.createObjectClient(grip);
 }
 
@@ -144,26 +144,28 @@ function reverseStepOut(thread: string):
     lookupThreadClient(thread).reverseStepOut(resolve);
   });
 }
 
 function breakOnNext(thread: string): Promise<*> {
   return lookupThreadClient(thread).breakOnNext();
 }
 
-function sourceContents(
-  sourceId: SourceId
-): {| source: Source, contentType: string |} {
-  const sourceThreadClient = sourceThreads[sourceId];
-  const sourceClient = sourceThreadClient.source({ actor: sourceId });
-  return sourceClient.source();
+async function sourceContents({
+  actor,
+  thread
+}: SourceActor): Promise<{| source: any, contentType: ?string |}> {
+  const sourceThreadClient = lookupThreadClient(thread);
+  const sourceClient = sourceThreadClient.source({ actor });
+  const { source, contentType } = await sourceClient.source();
+  return { source, contentType };
 }
 
-function getBreakpointByLocation(location: SourceLocation) {
-  const id = makePendingLocationId(location);
+function getBreakpointByLocation(location: SourceActorLocation) {
+  const id = makeBreakpointActorId(location);
   const bpClient = bpClients[id];
 
   if (bpClient) {
     const { actor, url, line, column } = bpClient.location;
     return {
       id: bpClient.actor,
       options: bpClient.options,
       actualLocation: {
@@ -190,73 +192,74 @@ function removeXHRBreakpoint(path: strin
 function transformOptionsToCondition(options) {
   if (options.logValue) {
     return `console.log(${options.logValue})`;
   }
   return options.condition;
 }
 
 function setBreakpoint(
-  location: SourceLocation,
+  location: SourceActorLocation,
   options: BreakpointOptions,
   noSliding: boolean
 ): Promise<BreakpointResult> {
-  const sourceThreadClient = sourceThreads[location.sourceId];
-  const sourceClient = sourceThreadClient.source({ actor: location.sourceId });
+  const sourceThreadClient = lookupThreadClient(location.sourceActor.thread);
+  const sourceClient = sourceThreadClient.source({
+    actor: location.sourceActor.actor
+  });
 
   return sourceClient
     .setBreakpoint({
       line: location.line,
       column: location.column,
       condition: transformOptionsToCondition(options),
       noSliding
     })
     .then(([{ actualLocation }, bpClient]) => {
       actualLocation = createBreakpointLocation(location, actualLocation);
-      const id = makePendingLocationId(actualLocation);
+
+      const id = makeBreakpointActorId(actualLocation);
       bpClients[id] = bpClient;
       bpClient.location.line = actualLocation.line;
       bpClient.location.column = actualLocation.column;
-      bpClient.location.url = actualLocation.sourceUrl || "";
 
       return { id, actualLocation };
     });
 }
 
 function removeBreakpoint(
-  generatedLocation: SourceLocation
+  location: SourceActorLocation
 ): Promise<void> | ?BreakpointResult {
   try {
-    const id = makePendingLocationId(generatedLocation);
+    const id = makeBreakpointActorId(location);
     const bpClient = bpClients[id];
     if (!bpClient) {
       console.warn("No breakpoint to delete on server");
       return Promise.resolve();
     }
     delete bpClients[id];
     return bpClient.remove();
   } catch (_error) {
     console.warn("No breakpoint to delete on server");
   }
 }
 
 function setBreakpointOptions(
-  breakpointId: BreakpointId,
-  location: SourceLocation,
+  location: SourceActorLocation,
   options: BreakpointOptions
 ) {
-  const bpClient = bpClients[breakpointId];
-  delete bpClients[breakpointId];
+  const id = makeBreakpointActorId(location);
+  const bpClient = bpClients[id];
+  delete bpClients[id];
 
-  const sourceThreadClient = sourceThreads[bpClient.source.actor];
+  const sourceThreadClient = bpClient.source._activeThread;
   return bpClient
     .setCondition(sourceThreadClient, transformOptionsToCondition(options))
     .then(_bpClient => {
-      bpClients[breakpointId] = _bpClient;
-      return { id: breakpointId };
+      bpClients[id] = _bpClient;
     });
 }
 
 async function evaluateInFrame(script: Script, options: EvaluateParam) {
   return evaluate(script, options);
 }
 
 async function evaluateExpressions(scripts: Script[], options: EvaluateParam) {
@@ -323,65 +326,55 @@ function getProperties(thread: string, g
   });
 }
 
 async function getFrameScopes(frame: Frame): Promise<*> {
   if (frame.scope) {
     return frame.scope;
   }
 
-  let sourceId = frame.location.sourceId;
-  if (isOriginalId(sourceId)) {
-    sourceId = originalToGeneratedId(sourceId);
-  }
-
-  const sourceThreadClient = sourceThreads[sourceId];
+  const sourceThreadClient = lookupThreadClient(frame.thread);
   return sourceThreadClient.getEnvironment(frame.id);
 }
 
 function pauseOnExceptions(
   thread: string,
   shouldPauseOnExceptions: boolean,
   shouldPauseOnCaughtExceptions: boolean
 ): Promise<*> {
   return lookupThreadClient(thread).pauseOnExceptions(
     shouldPauseOnExceptions,
     // Providing opposite value because server
     // uses "shouldIgnoreCaughtExceptions"
     !shouldPauseOnCaughtExceptions
   );
 }
 
-function prettyPrint(sourceId: SourceId, indentSize: number): Promise<*> {
-  const sourceClient = threadClient.source({ actor: sourceId });
-  return sourceClient.prettyPrint(indentSize);
-}
-
 async function blackBox(
-  sourceId: SourceId,
+  sourceActor: SourceActor,
   isBlackBoxed: boolean,
   range?: Range
 ): Promise<*> {
-  const sourceClient = threadClient.source({ actor: sourceId });
+  const sourceClient = threadClient.source({ actor: sourceActor.actor });
   if (isBlackBoxed) {
     await sourceClient.unblackBox(range);
   } else {
     await sourceClient.blackBox(range);
   }
-
-  return { isBlackBoxed: !isBlackBoxed };
 }
 
-function disablePrettyPrint(sourceId: SourceId): Promise<*> {
-  const sourceClient = threadClient.source({ actor: sourceId });
-  return sourceClient.disablePrettyPrint();
-}
-
-async function setPausePoints(sourceId: SourceId, pausePoints: PausePointsMap) {
-  return sendPacket({ to: sourceId, type: "setPausePoints", pausePoints });
+async function setPausePoints(
+  sourceActor: SourceActor,
+  pausePoints: PausePointsMap
+) {
+  return sendPacket({
+    to: sourceActor.actor,
+    type: "setPausePoints",
+    pausePoints
+  });
 }
 
 async function setSkipPausing(thread: string, shouldSkip: boolean) {
   const client = lookupThreadClient(thread);
   return client.request({
     skip: shouldSkip,
     to: client.actor,
     type: "skipBreakpoints"
@@ -399,42 +392,48 @@ function eventListeners(): Promise<*> {
 function setEventListenerBreakpoints(eventTypes: EventListenerBreakpoints) {
   // TODO: Figure out what sendpoint we want to hit
 }
 
 function pauseGrip(thread: string, func: Function): ObjectClient {
   return lookupThreadClient(thread).pauseGrip(func);
 }
 
-function registerSource(source: Source) {
-  if (isOriginalId(source.id)) {
-    throw new Error("registerSource called with original ID");
-  }
-  sourceThreads[source.id] = lookupThreadClient(source.thread);
+function registerSourceActor(sourceActor: SourceActor) {
+  sourceActors[sourceActor.actor] = sourceActor.source;
 }
 
 async function createSources(client: ThreadClient) {
-  const { sources } = await client.getSources();
-  return (
-    sources &&
-    sources.map(packet => createSource(client.actor, packet, { supportsWasm }))
+  const { sources }: SourcesPacket = await client.getSources();
+  if (!sources) {
+    return null;
+  }
+  return sources.map(packet =>
+    createSource(client.actor, packet, { supportsWasm })
   );
 }
 
 async function fetchSources(): Promise<any[]> {
   const sources = await createSources(threadClient);
 
   // NOTE: this happens when we fetch sources and then immediately navigate
   if (!sources) {
     return [];
   }
 
   return sources;
 }
 
+function getSourceForActor(actor: ActorId) {
+  if (!sourceActors[actor]) {
+    throw new Error(`Unknown source actor: ${actor}`);
+  }
+  return sourceActors[actor];
+}
+
 async function fetchWorkers(): Promise<Worker[]> {
   if (features.windowlessWorkers) {
     workerClients = await updateWorkerClients({
       tabTarget,
       debuggerClient,
       threadClient,
       workerClients
     });
@@ -453,16 +452,20 @@ async function fetchWorkers(): Promise<W
   if (!supportsWorkers(tabTarget)) {
     return Promise.resolve([]);
   }
 
   const { workers } = await tabTarget.activeTab.listWorkers();
   return workers;
 }
 
+function getMainThread() {
+  return threadClient.actor;
+}
+
 const clientCommands = {
   autocomplete,
   blackBox,
   createObjectClient,
   releaseActor,
   interrupt,
   eventListeners,
   pauseGrip,
@@ -471,34 +474,34 @@ const clientCommands = {
   stepOut,
   stepOver,
   rewind,
   reverseStepIn,
   reverseStepOut,
   reverseStepOver,
   breakOnNext,
   sourceContents,
+  getSourceForActor,
   getBreakpointByLocation,
   setBreakpoint,
   setXHRBreakpoint,
   removeXHRBreakpoint,
   removeBreakpoint,
   setBreakpointOptions,
   evaluate,
   evaluateInFrame,
   evaluateExpressions,
   navigate,
   reload,
   getProperties,
   getFrameScopes,
   pauseOnExceptions,
-  prettyPrint,
-  disablePrettyPrint,
   fetchSources,
+  registerSourceActor,
   fetchWorkers,
+  getMainThread,
   sendPacket,
   setPausePoints,
   setSkipPausing,
-  setEventListenerBreakpoints,
-  registerSource
+  setEventListenerBreakpoints
 };
 
 export { setupCommands, clientCommands };
--- a/devtools/client/debugger/new/src/client/firefox/create.js
+++ b/devtools/client/debugger/new/src/client/firefox/create.js
@@ -1,108 +1,119 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 // This module converts Firefox specific types to the generic types
 
-import type { Frame, Source, SourceLocation } from "../../types";
+import type { Frame, Source, SourceActorLocation, ThreadId } from "../../types";
 import type {
   PausedPacket,
   FramesResponse,
   FramePacket,
-  SourcePayload
+  SourcePayload,
+  CreateSourceResult
 } from "./types";
 
 import { clientCommands } from "./commands";
 
-export function createFrame(frame: FramePacket): ?Frame {
+export function createFrame(thread: ThreadId, frame: FramePacket): ?Frame {
   if (!frame) {
     return null;
   }
   let title;
   if (frame.type == "call") {
     const c = frame.callee;
     title = c.name || c.userDisplayName || c.displayName;
   } else {
     title = `(${frame.type})`;
   }
 
   // NOTE: Firefox 66 switched from where.source to where.actor
+  const actor = frame.where.source
+    ? frame.where.source.actor
+    : frame.where.actor;
+
   const location = {
-    sourceId: frame.where.source ? frame.where.source.actor : frame.where.actor,
+    sourceId: clientCommands.getSourceForActor(actor),
     line: frame.where.line,
     column: frame.where.column
   };
 
   return {
     id: frame.actor,
+    thread,
     displayName: title,
     location,
     generatedLocation: location,
     this: frame.this,
     scope: frame.environment
   };
 }
 
+function makeSourceId(source) {
+  return source.url ? `sourceURL-${source.url}` : `source-${source.actor}`;
+}
+
 export function createSource(
   thread: string,
   source: SourcePayload,
   { supportsWasm }: { supportsWasm: boolean }
-): Source {
-  const createdSource = {
-    id: source.actor,
-    thread,
+): CreateSourceResult {
+  const createdSource: any = {
+    id: makeSourceId(source),
     url: source.url,
     relativeUrl: source.url,
     isPrettyPrinted: false,
-    isWasm: false,
     sourceMapURL: source.sourceMapURL,
     introductionUrl: source.introductionUrl,
     isBlackBoxed: false,
-    loadedState: "unloaded"
+    loadedState: "unloaded",
+    isWasm: supportsWasm && source.introductionType === "wasm"
   };
-  clientCommands.registerSource(createdSource);
-  return Object.assign(createdSource, {
-    isWasm: supportsWasm && source.introductionType === "wasm"
-  });
+  const sourceActor = {
+    actor: source.actor,
+    source: createdSource.id,
+    thread
+  };
+  clientCommands.registerSourceActor(sourceActor);
+  return { sourceActor, source: (createdSource: Source) };
 }
 
 export function createPause(
   thread: string,
   packet: PausedPacket,
   response: FramesResponse
 ): any {
   // NOTE: useful when the debugger is already paused
   const frame = packet.frame || response.frames[0];
 
   return {
     ...packet,
     thread,
-    frame: createFrame(frame),
-    frames: response.frames.map(createFrame)
+    frame: createFrame(thread, frame),
+    frames: response.frames.map(createFrame.bind(null, thread))
   };
 }
 
 // Firefox only returns `actualLocation` if it actually changed,
 // but we want it always to exist. Format `actualLocation` if it
 // exists, otherwise use `location`.
 
 export function createBreakpointLocation(
-  location: SourceLocation,
+  location: SourceActorLocation,
   actualLocation?: Object
-): SourceLocation {
+): SourceActorLocation {
   if (!actualLocation) {
     return location;
   }
 
   return {
-    sourceId: actualLocation.source.actor,
-    sourceUrl: actualLocation.source.url,
+    ...location,
     line: actualLocation.line,
     column: actualLocation.column
   };
 }
 
 export function createWorker(actor: string, url: string) {
   return {
     actor,
--- a/devtools/client/debugger/new/src/client/firefox/tests/commands.spec.js
+++ b/devtools/client/debugger/new/src/client/firefox/tests/commands.spec.js
@@ -1,77 +1,89 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
+// @flow
+
 import { setupCommands, clientCommands } from "../commands";
 
 function makeThreadCLient(resp) {
-  return {
+  // Coerce this to any to avoid supplying the additional members needed in a
+  // thread client.
+  return ({
     pauseGrip: () => ({
       getPrototypeAndProperties: async () => resp
     })
+  }: any);
+}
+
+function makeDependencies() {
+  return {
+    debuggerClient: (null: any),
+    supportsWasm: true,
+    tabTarget: (null: any)
   };
 }
 
 describe("firefox commands", () => {
   describe("getProperties", () => {
     it("empty response", async () => {
       const { getProperties } = clientCommands;
       const threadClient = makeThreadCLient({
         ownProperties: {},
         safeGetterValues: {}
       });
 
-      setupCommands({ threadClient });
-      const props = await getProperties({});
+      setupCommands({ ...makeDependencies(), threadClient });
+      const props = await getProperties("", { actor: "" });
       expect(props).toMatchSnapshot();
     });
 
     it("simple properties", async () => {
       const { getProperties } = clientCommands;
       const threadClient = makeThreadCLient({
         ownProperties: {
           obj: { value: "obj" },
           foo: { value: "foo" }
         },
         safeGetterValues: {}
       });
 
-      setupCommands({ threadClient });
-      const props = await getProperties({});
+      setupCommands({ ...makeDependencies(), threadClient });
+      const props = await getProperties("", { actor: "" });
       expect(props).toMatchSnapshot();
     });
 
     it("getter values", async () => {
       const { getProperties } = clientCommands;
       const threadClient = makeThreadCLient({
         ownProperties: {
           obj: { value: "obj" },
           foo: { value: "foo" }
         },
         safeGetterValues: {
           obj: { getterValue: "getter", enumerable: true, writable: false }
         }
       });
 
-      setupCommands({ threadClient });
-      const props = await getProperties({});
+      setupCommands({ ...makeDependencies(), threadClient });
+      const props = await getProperties("", { actor: "" });
       expect(props).toMatchSnapshot();
     });
 
     it("new getter values", async () => {
       const { getProperties } = clientCommands;
       const threadClient = makeThreadCLient({
         ownProperties: {
           foo: { value: "foo" }
         },
         safeGetterValues: {
           obj: { getterValue: "getter", enumerable: true, writable: false }
         }
       });
 
-      setupCommands({ threadClient });
-      const props = await getProperties({});
+      setupCommands({ ...makeDependencies(), threadClient });
+      const props = await getProperties("", { actor: "" });
       expect(props).toMatchSnapshot();
     });
   });
 });
--- a/devtools/client/debugger/new/src/client/firefox/tests/onconnect.spec.js
+++ b/devtools/client/debugger/new/src/client/firefox/tests/onconnect.spec.js
@@ -1,12 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
+// @flow
+
 import { onConnect } from "../../firefox";
 
 const tabTarget = {
   on: () => {},
   _form: {
     url: "url"
   }
 };
--- a/devtools/client/debugger/new/src/client/firefox/types.js
+++ b/devtools/client/debugger/new/src/client/firefox/types.js
@@ -13,17 +13,19 @@
 import type {
   BreakpointOptions,
   FrameId,
   ActorId,
   Script,
   Source,
   Pause,
   Frame,
-  SourceId
+  SourceId,
+  Worker,
+  SourceActor
 } from "../../types";
 
 type URL = string;
 
 /**
  * The protocol is carried by a reliable, bi-directional byte stream; data sent
  * in both directions consists of JSON objects, called packets. A packet is a
  * top-level JSON object, not contained inside any other value.
@@ -112,16 +114,21 @@ export type SourcePacket = {
  * @memberof firefox/packets
  * @static
  */
 export type SourcesPacket = {
   from: ActorId,
   sources: SourcePayload[]
 };
 
+export type CreateSourceResult = {|
+  sourceActor?: SourceActor,
+  +source: Source
+|};
+
 /**
  * Pause Packet sent when the server is in a "paused" state
  *
  * @memberof firefox
  * @static
  */
 export type PausedPacket = {
   actor: ActorId,
@@ -221,18 +228,18 @@ export type TabTarget = {
     evaluateJS: (
       script: Script,
       func: Function,
       params?: { frameActor: ?FrameId }
     ) => void,
     evaluateJSAsync: (
       script: Script,
       func: Function,
-      params?: { frameActor?: FrameId }
-    ) => void,
+      params?: { frameActor: ?FrameId }
+    ) => Promise<{ result: ?Object }>,
     autocomplete: (
       input: string,
       cursor: number,
       func: Function,
       frameId: ?string
     ) => void
   },
   form: { consoleActor: any },
@@ -312,17 +319,18 @@ export type FunctionGrip = {|
 |};
 
 /**
  * SourceClient
  * @memberof firefox
  * @static
  */
 export type SourceClient = {
-  source: () => Source,
+  source: () => { source: any, contentType?: string },
+  _activeThread: ThreadClient,
   actor: string,
   setBreakpoint: ({
     line: number,
     column: ?number,
     condition: ?string,
     noSliding: boolean
   }) => Promise<BreakpointResponse>,
   prettyPrint: number => Promise<*>,
@@ -409,8 +417,16 @@ export type BreakpointResponse = [
 ];
 
 export type FirefoxClientConnection = {
   getTabTarget: () => TabTarget,
   getThreadClient: () => ThreadClient,
   setTabTarget: (target: TabTarget) => void,
   setThreadClient: (client: ThreadClient) => void
 };
+
+export type Panel = {|
+  emit: (eventName: string) => void,
+  openLink: (url: string) => void,
+  openWorkerToolbox: (worker: Worker) => void,
+  openElementInInspector: (grip: Object) => void,
+  openConsoleAndEvaluate: (input: string) => void
+|};
--- a/devtools/client/debugger/new/src/client/index.js
+++ b/devtools/client/debugger/new/src/client/index.js
@@ -12,16 +12,18 @@ import { setupHelper } from "../utils/db
 
 import {
   bootstrapApp,
   bootstrapStore,
   bootstrapWorkers
 } from "../utils/bootstrap";
 import { initialBreakpointsState } from "../reducers/breakpoints";
 
+import type { Panel } from "./firefox/types";
+
 function loadFromPrefs(actions: Object) {
   const { pauseOnExceptions, pauseOnCaughtExceptions } = prefs;
   if (pauseOnExceptions || pauseOnCaughtExceptions) {
     return actions.pauseOnExceptions(
       pauseOnExceptions,
       pauseOnCaughtExceptions
     );
   }
@@ -52,46 +54,45 @@ function getClient(connection: any) {
   const {
     tab: { clientType }
   } = connection;
   return clientType == "firefox" ? firefox : chrome;
 }
 
 export async function onConnect(
   connection: Object,
-  { services, toolboxActions }: Object
+  sourceMaps: Object,
+  panel: Panel
 ) {
   // NOTE: the landing page does not connect to a JS process
   if (!connection) {
     return;
   }
 
   const client = getClient(connection);
   const commands = client.clientCommands;
 
   const initialState = await loadInitialState();
 
   const { store, actions, selectors } = bootstrapStore(
     commands,
-    {
-      services,
-      toolboxActions
-    },
+    sourceMaps,
+    panel,
     initialState
   );
 
   const workers = bootstrapWorkers();
   await client.onConnect(connection, actions);
 
   await loadFromPrefs(actions);
   syncXHRBreakpoints();
   setupHelper({
     store,
     actions,
     selectors,
-    workers: { ...workers, ...services },
+    workers: { ...workers, sourceMaps },
     connection,
     client: client.clientCommands
   });
 
   bootstrapApp(store);
   return { store, actions, selectors, client: commands };
 }
--- a/devtools/client/debugger/new/src/components/Editor/Breakpoints.js
+++ b/devtools/client/debugger/new/src/components/Editor/Breakpoints.js
@@ -2,17 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 import React, { Component } from "react";
 import Breakpoint from "./Breakpoint";
 
 import { getSelectedSource, getFirstVisibleBreakpoints } from "../../selectors";
-import { makeLocationId } from "../../utils/breakpoint";
+import { makeBreakpointId } from "../../utils/breakpoint";
 import { connect } from "../../utils/connect";
 import { breakpointItemActions } from "./menus/breakpoints";
 import { editorItemActions } from "./menus/editor";
 
 import type { BreakpointItemActions } from "./menus/breakpoints";
 import type { EditorItemActions } from "./menus/editor";
 import type { Breakpoint as BreakpointType, Source } from "../../types";
 
@@ -38,17 +38,17 @@ class Breakpoints extends Component<Prop
       return null;
     }
 
     return (
       <div>
         {breakpoints.map(bp => {
           return (
             <Breakpoint
-              key={makeLocationId(bp.location)}
+              key={makeBreakpointId(bp.location)}
               breakpoint={bp}
               selectedSource={selectedSource}
               editor={editor}
               breakpointActions={breakpointActions}
               editorActions={editorActions}
             />
           );
         })}
--- a/devtools/client/debugger/new/src/components/Editor/ColumnBreakpoints.js
+++ b/devtools/client/debugger/new/src/components/Editor/ColumnBreakpoints.js
@@ -4,17 +4,17 @@
 
 import React, { Component } from "react";
 
 import ColumnBreakpoint from "./ColumnBreakpoint";
 import "./ColumnBreakpoints.css";
 
 import { getSelectedSource, visibleColumnBreakpoints } from "../../selectors";
 import { connect } from "../../utils/connect";
-import { makeLocationId } from "../../utils/breakpoint";
+import { makeBreakpointId } from "../../utils/breakpoint";
 import { breakpointItemActions } from "./menus/breakpoints";
 import type { BreakpointItemActions } from "./menus/breakpoints";
 
 import type { Source } from "../../types";
 // eslint-disable-next-line max-len
 import type { ColumnBreakpoint as ColumnBreakpointType } from "../../selectors/visibleColumnBreakpoints";
 
 class ColumnBreakpoints extends Component {
@@ -36,17 +36,17 @@ class ColumnBreakpoints extends Componen
     if (!selectedSource || selectedSource.isBlackBoxed) {
       return null;
     }
 
     let breakpoints;
     editor.codeMirror.operation(() => {
       breakpoints = columnBreakpoints.map(breakpoint => (
         <ColumnBreakpoint
-          key={makeLocationId(breakpoint.location)}
+          key={makeBreakpointId(breakpoint.location)}
           columnBreakpoint={breakpoint}
           editor={editor}
           source={selectedSource}
           breakpointActions={breakpointActions}
         />
       ));
     });
     return <div>{breakpoints}</div>;
--- a/devtools/client/debugger/new/src/components/Editor/tests/DebugLine.spec.js
+++ b/devtools/client/debugger/new/src/components/Editor/tests/DebugLine.spec.js
@@ -26,17 +26,17 @@ function createMockDocument(clear) {
 
 function generateDefaults(editor, overrides) {
   return {
     editor,
     pauseInfo: {
       why: { type: "breakpoint" }
     },
     frame: null,
-    source: makeSource("foo"),
+    source: makeSource("foo").source,
     ...overrides
   };
 }
 
 function createFrame(line) {
   return {
     location: {
       sourceId: "foo",
@@ -60,33 +60,33 @@ function render(overrides = {}) {
   });
   return { component, props, clear, editor, doc };
 }
 
 describe("DebugLine Component", () => {
   describe("pausing at the first location", () => {
     it("should show a new debug line", async () => {
       const { component, props, doc } = render({
-        source: makeSource("foo", { loadedState: "loaded" })
+        source: makeSource("foo", { loadedState: "loaded" }).source
       });
       const line = 2;
       const frame = createFrame(line);
 
       component.setProps({ ...props, frame });
 
       expect(doc.removeLineClass.mock.calls).toEqual([]);
       expect(doc.addLineClass.mock.calls).toEqual([
         [toEditorLine("foo", line), "line", "new-debug-line"]
       ]);
     });
 
     describe("pausing at a new location", () => {
       it("should replace the first debug line", async () => {
         const { props, component, clear, doc } = render({
-          source: makeSource("foo", { loadedState: "loaded" })
+          source: makeSource("foo", { loadedState: "loaded" }).source
         });
 
         component.instance().debugExpression = { clear: jest.fn() };
         const firstLine = 2;
         const secondLine = 2;
 
         component.setProps({ ...props, frame: createFrame(firstLine) });
         component.setProps({
--- a/devtools/client/debugger/new/src/components/Editor/tests/Footer.spec.js
+++ b/devtools/client/debugger/new/src/components/Editor/tests/Footer.spec.js
@@ -23,17 +23,17 @@ function generateDefaults(overrides) {
   return {
     editor: {
       codeMirror: {
         doc: {},
         cursorActivity: jest.fn(),
         on: jest.fn()
       }
     },
-    selectedSource: makeSource("foo"),
+    selectedSource: makeSource("foo").source,
     ...overrides
   };
 }
 
 function render(overrides = {}, position = { line: 0, column: 0 }) {
   const clear = jest.fn();
   const props = generateDefaults(overrides);
 
--- a/devtools/client/debugger/new/src/components/PrimaryPanes/Sources.css
+++ b/devtools/client/debugger/new/src/components/PrimaryPanes/Sources.css
@@ -94,16 +94,20 @@
   font-size: 0.9em;
   color: var(--theme-comment);
 }
 
 .sources-list .tree .focused .label .suffix {
   color: inherit;
 }
 
+.sources-list .tree .img.arrow.expanded {
+  transform: rotate(0deg);
+}
+
 .theme-dark .source-list .tree .node.focused {
   background-color: var(--theme-tab-toolbar-background);
 }
 
 .sources-list .tree .focused .label {
   background-color: var(--theme-selection-background);
 }
 
--- a/devtools/client/debugger/new/src/components/PrimaryPanes/SourcesTree.js
+++ b/devtools/client/debugger/new/src/components/PrimaryPanes/SourcesTree.js
@@ -12,17 +12,16 @@ import { connect } from "../../utils/con
 // Selectors
 import {
   getShownSource,
   getSelectedSource,
   getDebuggeeUrl,
   getExpandedState,
   getProjectDirectoryRoot,
   getRelativeSourcesForThread,
-  getSourceCount,
   getFocusedSourceItem,
   getWorkerByThread,
   getWorkerCount
 } from "../../selectors";
 
 import { getGeneratedSourceByURL } from "../../reducers/sources";
 
 // Actions
@@ -341,45 +340,53 @@ class SourcesTree extends Component<Prop
         {this.renderTree()}
       </div>
     );
   }
 }
 
 function getSourceForTree(
   state: AppState,
+  relativeSources: SourcesMap,
   source: ?Source,
-  thread: ?string
+  thread
 ): ?Source {
+  if (!source) {
+    return null;
+  }
+
+  source = relativeSources[source.id];
   if (!source || !source.isPrettyPrinted) {
     return source;
   }
 
-  const candidate = getGeneratedSourceByURL(state, getRawSourceURL(source.url));
-
-  if (!thread || !candidate || candidate.thread == thread) {
-    return candidate;
-  }
+  return getGeneratedSourceByURL(state, getRawSourceURL(source.url));
 }
 
 const mapStateToProps = (state, props) => {
   const selectedSource = getSelectedSource(state);
   const shownSource = getShownSource(state);
   const focused = getFocusedSourceItem(state);
   const thread = props.thread;
+  const relativeSources = getRelativeSourcesForThread(state, thread);
 
   return {
-    shownSource: getSourceForTree(state, shownSource, thread),
-    selectedSource: getSourceForTree(state, selectedSource, thread),
+    shownSource: getSourceForTree(state, relativeSources, shownSource, thread),
+    selectedSource: getSourceForTree(
+      state,
+      relativeSources,
+      selectedSource,
+      thread
+    ),
     debuggeeUrl: getDebuggeeUrl(state),
     expanded: getExpandedState(state, props.thread),
     focused: focused && focused.thread == props.thread ? focused.item : null,
     projectRoot: getProjectDirectoryRoot(state),
-    sources: getRelativeSourcesForThread(state, thread),
-    sourceCount: getSourceCount(state, props.thread),
+    sources: relativeSources,
+    sourceCount: Object.values(relativeSources).length,
     worker: getWorkerByThread(state, thread),
     workerCount: getWorkerCount(state)
   };
 };
 
 export default connect(
   mapStateToProps,
   {
--- a/devtools/client/debugger/new/src/components/PrimaryPanes/SourcesTreeItem.js
+++ b/devtools/client/debugger/new/src/components/PrimaryPanes/SourcesTreeItem.js
@@ -9,17 +9,18 @@ import { connect } from "../../utils/con
 import classnames from "classnames";
 import { showMenu } from "devtools-contextmenu";
 
 import SourceIcon from "../shared/SourceIcon";
 import AccessibleImage from "../shared/AccessibleImage";
 
 import {
   getGeneratedSourceByURL,
-  getHasSiblingOfSameName
+  getHasSiblingOfSameName,
+  hasPrettySource as checkHasPrettySource
 } from "../../selectors";
 import actions from "../../actions";
 
 import {
   isOriginal as isOriginalSource,
   getSourceQueryString,
   isUrlExtension
 } from "../../utils/source";
@@ -35,16 +36,17 @@ type Props = {
   projectRoot: string,
   source: ?Source,
   item: TreeNode,
   depth: number,
   focused: boolean,
   expanded: boolean,
   hasMatchingGeneratedSource: boolean,
   hasSiblingOfSameName: boolean,
+  hasPrettySource: boolean,
   focusItem: TreeNode => void,
   selectItem: TreeNode => void,
   setExpanded: (TreeNode, boolean, boolean) => void,
   clearProjectDirectoryRoot: typeof actions.clearProjectDirectoryRoot,
   setProjectDirectoryRoot: typeof actions.setProjectDirectoryRoot
 };
 
 type State = {};
@@ -55,17 +57,17 @@ type MenuOption = {
   disabled: boolean,
   click: () => any
 };
 
 type ContextMenu = Array<MenuOption>;
 
 class SourceTreeItem extends Component<Props, State> {
   getIcon(item: TreeNode, depth: number) {
-    const { debuggeeUrl, projectRoot, source } = this.props;
+    const { debuggeeUrl, projectRoot, source, hasPrettySource } = this.props;
 
     if (item.path === "webpack://") {
       return <AccessibleImage className="webpack" />;
     } else if (item.path === "ng://") {
       return <AccessibleImage className="angular" />;
     } else if (isUrlExtension(item.path) && depth === 0) {
       return <AccessibleImage className="extension" />;
     }
@@ -79,16 +81,20 @@ class SourceTreeItem extends Component<P
         />
       );
     }
 
     if (isDirectory(item)) {
       return <AccessibleImage className="folder" />;
     }
 
+    if (hasPrettySource) {
+      return <AccessibleImage className="prettyPrint" />;
+    }
+
     if (source) {
       return <SourceIcon source={source} />;
     }
 
     return null;
   }
 
   onClick = (e: MouseEvent) => {
@@ -249,17 +255,18 @@ function getHasMatchingGeneratedSource(s
 
   return !!getGeneratedSourceByURL(state, source.url);
 }
 
 const mapStateToProps = (state, props) => {
   const { source } = props;
   return {
     hasMatchingGeneratedSource: getHasMatchingGeneratedSource(state, source),
-    hasSiblingOfSameName: getHasSiblingOfSameName(state, source)
+    hasSiblingOfSameName: getHasSiblingOfSameName(state, source),
+    hasPrettySource: source ? checkHasPrettySource(state, source.id) : false
   };
 };
 
 export default connect(
   mapStateToProps,
   {
     setProjectDirectoryRoot: actions.setProjectDirectoryRoot,
     clearProjectDirectoryRoot: actions.clearProjectDirectoryRoot
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/index.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/index.js
@@ -13,17 +13,17 @@ import ExceptionOption from "./Exception
 import Breakpoint from "./Breakpoint";
 import BreakpointHeading from "./BreakpointHeading";
 
 import actions from "../../../actions";
 import { getDisplayPath } from "../../../utils/source";
 import { getSelectedLocation } from "../../../utils/source-maps";
 
 import {
-  makeLocationId,
+  makeBreakpointId,
   sortSelectedBreakpoints
 } from "../../../utils/breakpoint";
 
 import { getSelectedSource, getBreakpointSources } from "../../../selectors";
 
 import type { Source } from "../../../types";
 import type { BreakpointSources } from "../../../selectors/breakpointSources";
 
@@ -96,17 +96,17 @@ class Breakpoints extends Component<Prop
             path={path}
             key={source.url}
           />,
           ...sortedBreakpoints.map(breakpoint => (
             <Breakpoint
               breakpoint={breakpoint}
               source={source}
               selectedSource={selectedSource}
-              key={makeLocationId(
+              key={makeBreakpointId(
                 getSelectedLocation(breakpoint, selectedSource)
               )}
             />
           ))
         ];
       })
     ];
   }
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/tests/Breakpoint.spec.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/tests/Breakpoint.spec.js
@@ -26,17 +26,17 @@ describe("Breakpoint", () => {
       frame: { selectedLocation: generatedLocation }
     });
     expect(component).toMatchSnapshot();
   });
 
   it("paused at an original location", () => {
     const { component } = render(
       {
-        selectedSource: makeOriginalSource("foo"),
+        selectedSource: makeOriginalSource("foo").source,
         frame: { selectedLocation: location }
       },
       { location, options: {} }
     );
 
     expect(component).toMatchSnapshot();
   });
 
@@ -67,19 +67,19 @@ function makeBreakpoint(overrides = {}) 
     generatedLocation,
     disabled: false,
     options: {},
     ...overrides
   };
 }
 
 function generateDefaults(overrides = {}, breakpointOverrides = {}) {
-  const source = makeSource("foo");
+  const source = makeSource("foo").source;
   const breakpoint = makeBreakpoint(breakpointOverrides);
-  const selectedSource = makeSource("foo");
+  const selectedSource = makeSource("foo").source;
   return {
     source,
     breakpoint,
     selectedSource,
     frame: (null: any),
     ...overrides
   };
 }
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/tests/BreakpointsContextMenu.spec.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/tests/BreakpointsContextMenu.spec.js
@@ -22,38 +22,38 @@ function render(disabled = false) {
 }
 
 function generateDefaults(disabled) {
   const breakpoints = [
     createBreakpoint(
       {
         line: 1,
         column: undefined,
-        sourceId: "server1.conn26.child3/source26",
+        sourceId: "source-https://example.com/main.js",
         sourceUrl: "https://example.com/main.js"
       },
-      { id: "https://example.com/main.js:1:", disabled: disabled }
+      { id: "source-https://example.com/main.js:1:", disabled: disabled }
     ),
     createBreakpoint(
       {
         line: 2,
         column: undefined,
-        sourceId: "server1.conn26.child3/source26",
+        sourceId: "source-https://example.com/main.js",
         sourceUrl: "https://example.com/main.js"
       },
-      { id: "https://example.com/main.js:2:", disabled: disabled }
+      { id: "source-https://example.com/main.js:2:", disabled: disabled }
     ),
     createBreakpoint(
       {
         line: 3,
         column: undefined,
-        sourceId: "server1.conn26.child3/source26",
+        sourceId: "source-https://example.com/main.js",
         sourceUrl: "https://example.com/main.js"
       },
-      { id: "https://example.com/main.js:3:", disabled: disabled }
+      { id: "source-https://example.com/main.js:3:", disabled: disabled }
     )
   ];
 
   const props = {
     breakpoints,
     breakpoint: breakpoints[0],
     removeBreakpoint: jest.fn(),
     removeBreakpoints: jest.fn(),
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Frames/Frames.css
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Frames/Frames.css
@@ -1,15 +1,15 @@
 /* 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/>. */
 
-.frames [role="list"]l {
+.frames [role="list"] {
   list-style: none;
-  margin-top: 4px;
+  margin: 0;
   padding: 0;
 }
 
 .frames [role="list"] [role="listitem"] {
   padding: 2px 10px 2px 20px;
   overflow: hidden;
   display: flex;
   justify-content: space-between;
@@ -101,16 +101,17 @@
 .show-more:hover {
   background-color: var(--theme-toolbar-background-hover);
 }
 
 .annotation-logo {
   mask-size: 100%;
   display: inline-block;
   width: 12px;
+  margin-inline-start: 4px;
 }
 
 :root.theme-dark .annotation-logo:not(.angular) svg path {
   fill: var(--theme-highlight-blue);
 }
 
 /* Some elements are added to the DOM only to be printed into the clipboard
    when the user copy some elements. We don't want those elements to mess with
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Frames/Group.css
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Frames/Group.css
@@ -1,41 +1,46 @@
 /* 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/>. */
 
-.frames ul .frames-group .group,
-.frames ul .frames-group .group .location {
+.frames [role="list"] .frames-group .group,
+.frames [role="list"] .frames-group .group .location {
   font-weight: 500;
   cursor: default;
   /*
    * direction:rtl is set in Frames.css to overflow the location text from the
    * start. Here we need to reset it in order to display the framework icon
    * after the framework name.
    */
   direction: ltr;
 }
 
-.frames ul .frames-group.expanded .group,
-.frames ul .frames-group.expanded .group .location {
+.frames [role="list"] .frames-group.expanded .group,
+.frames [role="list"] .frames-group.expanded .group .location {
   color: var(--theme-highlight-blue);
 }
 
-.frames ul .frames-group.expanded .react path {
+.frames [role="list"] .frames-group.expanded .react path {
   fill: var(--theme-highlight-blue);
 }
 
-.frames ul .frames-group .frames-list li {
+.frames [role="list"] .frames-group .frames-list [role="listitem"] {
   padding-left: 30px;
 }
 
-.frames ul .frames-group .frames-list {
+.frames [role="list"] .frames-group .frames-list {
   border-top: 1px solid var(--theme-splitter-color);
   border-bottom: 1px solid var(--theme-splitter-color);
 }
 
-.frames ul .frames-group.expanded .badge {
+/* We don't want to display those as flex since only the name is displayed */
+.frames [role="list"] .frames-group .frames-list [role="listitem"] {
+  display: block;
+}
+
+.frames [role="list"] .frames-group.expanded .badge {
   color: var(--theme-highlight-blue);
 }
 
 .group-description-name {
   padding-left: 5px;
 }
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Frames/Group.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Frames/Group.js
@@ -154,17 +154,17 @@ export default class Group extends Compo
         onClick={this.toggleFrames}
         tabIndex={0}
         title={title}
       >
         {selectable && <FrameIndent />}
         <FrameLocation frame={frame} expanded={expanded} />
         {selectable && <span className="clipboard-only"> </span>}
         <Badge>{this.props.group.length}</Badge>
-        {selectable && <br className="clipboard-only"/>}
+        {selectable && <br className="clipboard-only" />}
       </div>
     );
   }
 
   render() {
     const { expanded } = this.state;
     const { disableContextMenu } = this.props;
     return (
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Frames/tests/Frame.spec.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Frames/tests/Frame.spec.js
@@ -71,31 +71,31 @@ describe("Frame", () => {
         line: 12
       }
     };
 
     const { component } = render({ id: 3 }, backboneFrame);
     expect(component).toMatchSnapshot();
   });
 
-  it("filename only", () => {
+  fit("filename only", () => {
     const frame = {
       id: 1,
       source: {
         url: "https://firefox.com/assets/src/js/foo-view.js"
       },
       displayName: "renderFoo",
       location: {
         line: 10
       }
     };
 
     const props = frameProperties(frame, null);
     const component = mount(<Frame {...props} />);
-    expect(component.text()).toBe("renderFoo foo-view.js:10");
+    expect(component.text()).toBe("\trenderFoo foo-view.js:10\n");
   });
 
   it("full URL", () => {
     const url = `https://${"a".repeat(100)}.com/assets/src/js/foo-view.js`;
     const frame = {
       id: 1,
       source: {
         url
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Frames/tests/__snapshots__/Frame.spec.js.snap
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Frames/tests/__snapshots__/Frame.spec.js.snap
@@ -1,21 +1,22 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
 exports[`Frame getFrameTitle 1`] = `
-<li
+<div
   className="frame"
   key="1"
   onContextMenu={[Function]}
   onKeyUp={[Function]}
   onMouseDown={[Function]}
+  role="listitem"
   tabIndex={0}
   title="Jump to https://aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.com/assets/src/js/foo-view.js:10"
 >
-  	
+  <FrameIndent />
   <FrameTitle
     frame={
       Object {
         "displayName": "renderFoo",
         "id": 1,
         "location": Object {
           "line": 10,
         },
@@ -25,47 +26,53 @@ exports[`Frame getFrameTitle 1`] = `
       }
     }
     options={
       Object {
         "shouldMapDisplayName": true,
       }
     }
   />
-   
+  <span
+    className="clipboard-only"
+  >
+     
+  </span>
   <FrameLocation
     displayFullUrl={false}
     frame={
       Object {
         "displayName": "renderFoo",
         "id": 1,
         "location": Object {
           "line": 10,
         },
         "source": Object {
           "url": "https://aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.com/assets/src/js/foo-view.js",
         },
       }
     }
   />
-  
-
-</li>
+  <br
+    className="clipboard-only"
+  />
+</div>
 `;
 
 exports[`Frame library frame 1`] = `
-<li
+<div
   className="frame selected"
   key="3"
   onContextMenu={[Function]}
   onKeyUp={[Function]}
   onMouseDown={[Function]}
+  role="listitem"
   tabIndex={0}
 >
-  	
+  <FrameIndent />
   <FrameTitle
     frame={
       Object {
         "displayName": "updateEvents",
         "frameworkGroupingOn": false,
         "id": 3,
         "library": "backbone",
         "location": Object {
@@ -78,17 +85,21 @@ exports[`Frame library frame 1`] = `
       }
     }
     options={
       Object {
         "shouldMapDisplayName": true,
       }
     }
   />
-   
+  <span
+    className="clipboard-only"
+  >
+     
+  </span>
   <FrameLocation
     displayFullUrl={false}
     frame={
       Object {
         "displayName": "updateEvents",
         "frameworkGroupingOn": false,
         "id": 3,
         "library": "backbone",
@@ -97,31 +108,33 @@ exports[`Frame library frame 1`] = `
         },
         "source": Object {
           "url": "backbone.js",
         },
         "toggleFrameworkGrouping": [MockFunction],
       }
     }
   />
-  
-
-</li>
+  <br
+    className="clipboard-only"
+  />
+</div>
 `;
 
 exports[`Frame user frame (not selected) 1`] = `
-<li
+<div
   className="frame"
   key="1"
   onContextMenu={[Function]}
   onKeyUp={[Function]}
   onMouseDown={[Function]}
+  role="listitem"
   tabIndex={0}
 >
-  	
+  <FrameIndent />
   <FrameTitle
     frame={
       Object {
         "displayName": "renderFoo",
         "frameworkGroupingOn": false,
         "id": 1,
         "library": false,
         "location": Object {
@@ -135,17 +148,21 @@ exports[`Frame user frame (not selected)
       }
     }
     options={
       Object {
         "shouldMapDisplayName": true,
       }
     }
   />
-   
+  <span
+    className="clipboard-only"
+  >
+     
+  </span>
   <FrameLocation
     displayFullUrl={false}
     frame={
       Object {
         "displayName": "renderFoo",
         "frameworkGroupingOn": false,
         "id": 1,
         "library": false,
@@ -155,31 +172,33 @@ exports[`Frame user frame (not selected)
         "source": Object {
           "isBlackBoxed": false,
           "url": "foo-view.js",
         },
         "toggleFrameworkGrouping": [MockFunction],
       }
     }
   />
-  
-
-</li>
+  <br
+    className="clipboard-only"
+  />
+</div>
 `;
 
 exports[`Frame user frame 1`] = `
-<li
+<div
   className="frame selected"
   key="1"
   onContextMenu={[Function]}
   onKeyUp={[Function]}
   onMouseDown={[Function]}
+  role="listitem"
   tabIndex={0}
 >
-  	
+  <FrameIndent />
   <FrameTitle
     frame={
       Object {
         "displayName": "renderFoo",
         "frameworkGroupingOn": false,
         "id": 1,
         "library": false,
         "location": Object {
@@ -193,17 +212,21 @@ exports[`Frame user frame 1`] = `
       }
     }
     options={
       Object {
         "shouldMapDisplayName": true,
       }
     }
   />
-   
+  <span
+    className="clipboard-only"
+  >
+     
+  </span>
   <FrameLocation
     displayFullUrl={false}
     frame={
       Object {
         "displayName": "renderFoo",
         "frameworkGroupingOn": false,
         "id": 1,
         "library": false,
@@ -213,12 +236,13 @@ exports[`Frame user frame 1`] = `
         "source": Object {
           "isBlackBoxed": false,
           "url": "foo-view.js",
         },
         "toggleFrameworkGrouping": [MockFunction],
       }
     }
   />
-  
-
-</li>
+  <br
+    className="clipboard-only"
+  />
+</div>
 `;
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Frames/tests/__snapshots__/Frames.spec.js.snap
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Frames/tests/__snapshots__/Frames.spec.js.snap
@@ -1,15 +1,17 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
 exports[`Frames Blackboxed Frames filters blackboxed frames 1`] = `
 <div
   className="pane frames"
 >
-  <ul>
+  <div
+    role="list"
+  >
     <Frame
       copyStackTrace={[Function]}
       disableContextMenu={false}
       frame={
         Object {
           "id": 1,
           "location": Object {
             "sourceId": "1",
@@ -18,16 +20,17 @@ exports[`Frames Blackboxed Frames filter
             "id": "1",
           },
         }
       }
       frameworkGroupingOn={false}
       hideLocation={false}
       key="1"
       selectFrame={[MockFunction]}
+      selectable={false}
       selectedFrame={
         Object {
           "id": 1,
           "location": Object {
             "sourceId": "1",
           },
         }
       }
@@ -48,49 +51,53 @@ exports[`Frames Blackboxed Frames filter
             "id": "1",
           },
         }
       }
       frameworkGroupingOn={false}
       hideLocation={false}
       key="3"
       selectFrame={[MockFunction]}
+      selectable={false}
       selectedFrame={
         Object {
           "id": 1,
           "location": Object {
             "sourceId": "1",
           },
         }
       }
       shouldMapDisplayName={true}
       toggleBlackBox={[MockFunction]}
       toggleFrameworkGrouping={[Function]}
     />
-  </ul>
+  </div>
 </div>
 `;
 
 exports[`Frames Library Frames groups all the Webpack-related frames 1`] = `
 <div
   className="pane frames"
 >
-  <ul>
+  <div
+    role="list"
+  >
     <Frame
       copyStackTrace={[Function]}
       disableContextMenu={false}
       frame={
         Object {
           "id": "1-appFrame",
         }
       }
       frameworkGroupingOn={true}
       hideLocation={false}
       key="1-appFrame"
       selectFrame={[MockFunction]}
+      selectable={false}
       selectedFrame={
         Object {
           "id": "1-appFrame",
         }
       }
       shouldMapDisplayName={true}
       toggleBlackBox={[MockFunction]}
       toggleFrameworkGrouping={[Function]}
@@ -123,45 +130,49 @@ exports[`Frames Library Frames groups al
             "source": Object {
               "url": "https://foo.com/bundle.js",
             },
           },
         ]
       }
       key="2-webpackBootstrapFrame"
       selectFrame={[MockFunction]}
+      selectable={false}
       selectedFrame={
         Object {
           "id": "1-appFrame",
         }
       }
       toggleBlackBox={[MockFunction]}
       toggleFrameworkGrouping={[Function]}
     />
-  </ul>
+  </div>
 </div>
 `;
 
-exports[`Frames Library Frames toggling framework frames 1`] = `
+exports[`Frames Library Frames selectable framework frames 1`] = `
 <div
   className="pane frames"
 >
-  <ul>
+  <div
+    role="list"
+  >
     <Frame
       copyStackTrace={[Function]}
       disableContextMenu={false}
       frame={
         Object {
           "id": 1,
         }
       }
       frameworkGroupingOn={false}
       hideLocation={false}
       key="1"
       selectFrame={[MockFunction]}
+      selectable={true}
       selectedFrame={
         Object {
           "id": 1,
         }
       }
       shouldMapDisplayName={true}
       toggleBlackBox={[MockFunction]}
       toggleFrameworkGrouping={[Function]}
@@ -174,16 +185,17 @@ exports[`Frames Library Frames toggling 
           "id": 2,
           "library": "back",
         }
       }
       frameworkGroupingOn={false}
       hideLocation={false}
       key="2"
       selectFrame={[MockFunction]}
+      selectable={true}
       selectedFrame={
         Object {
           "id": 1,
         }
       }
       shouldMapDisplayName={true}
       toggleBlackBox={[MockFunction]}
       toggleFrameworkGrouping={[Function]}
@@ -196,16 +208,17 @@ exports[`Frames Library Frames toggling 
           "id": 3,
           "library": "back",
         }
       }
       frameworkGroupingOn={false}
       hideLocation={false}
       key="3"
       selectFrame={[MockFunction]}
+      selectable={true}
       selectedFrame={
         Object {
           "id": 1,
         }
       }
       shouldMapDisplayName={true}
       toggleBlackBox={[MockFunction]}
       toggleFrameworkGrouping={[Function]}
@@ -217,46 +230,232 @@ exports[`Frames Library Frames toggling 
         Object {
           "id": 8,
         }
       }
       frameworkGroupingOn={false}
       hideLocation={false}
       key="8"
       selectFrame={[MockFunction]}
+      selectable={true}
       selectedFrame={
         Object {
           "id": 1,
         }
       }
       shouldMapDisplayName={true}
       toggleBlackBox={[MockFunction]}
       toggleFrameworkGrouping={[Function]}
     />
-  </ul>
+  </div>
 </div>
 `;
 
-exports[`Frames Library Frames toggling framework frames 2`] = `
+exports[`Frames Library Frames selectable framework frames 2`] = `
 <div
   className="pane frames"
 >
-  <ul>
+  <div
+    role="list"
+  >
     <Frame
       copyStackTrace={[Function]}
       disableContextMenu={false}
       frame={
         Object {
           "id": 1,
         }
       }
       frameworkGroupingOn={true}
       hideLocation={false}
       key="1"
       selectFrame={[MockFunction]}
+      selectable={true}
+      selectedFrame={
+        Object {
+          "id": 1,
+        }
+      }
+      shouldMapDisplayName={true}
+      toggleBlackBox={[MockFunction]}
+      toggleFrameworkGrouping={[Function]}
+    />
+    <Group
+      copyStackTrace={[Function]}
+      frameworkGroupingOn={true}
+      group={
+        Array [
+          Object {
+            "id": 2,
+            "library": "back",
+          },
+          Object {
+            "id": 3,
+            "library": "back",
+          },
+        ]
+      }
+      key="2"
+      selectFrame={[MockFunction]}
+      selectable={true}
+      selectedFrame={
+        Object {
+          "id": 1,
+        }
+      }
+      toggleBlackBox={[MockFunction]}
+      toggleFrameworkGrouping={[Function]}
+    />
+    <Frame
+      copyStackTrace={[Function]}
+      disableContextMenu={false}
+      frame={
+        Object {
+          "id": 8,
+        }
+      }
+      frameworkGroupingOn={true}
+      hideLocation={false}
+      key="8"
+      selectFrame={[MockFunction]}
+      selectable={true}
+      selectedFrame={
+        Object {
+          "id": 1,
+        }
+      }
+      shouldMapDisplayName={true}
+      toggleBlackBox={[MockFunction]}
+      toggleFrameworkGrouping={[Function]}
+    />
+  </div>
+</div>
+`;
+
+exports[`Frames Library Frames toggling framework frames 1`] = `
+<div
+  className="pane frames"
+>
+  <div
+    role="list"
+  >
+    <Frame
+      copyStackTrace={[Function]}
+      disableContextMenu={false}
+      frame={
+        Object {
+          "id": 1,
+        }
+      }
+      frameworkGroupingOn={false}
+      hideLocation={false}
+      key="1"
+      selectFrame={[MockFunction]}
+      selectable={false}
+      selectedFrame={
+        Object {
+          "id": 1,
+        }
+      }
+      shouldMapDisplayName={true}
+      toggleBlackBox={[MockFunction]}
+      toggleFrameworkGrouping={[Function]}
+    />
+    <Frame
+      copyStackTrace={[Function]}
+      disableContextMenu={false}
+      frame={
+        Object {
+          "id": 2,
+          "library": "back",
+        }
+      }
+      frameworkGroupingOn={false}
+      hideLocation={false}
+      key="2"
+      selectFrame={[MockFunction]}
+      selectable={false}
+      selectedFrame={
+        Object {
+          "id": 1,
+        }
+      }
+      shouldMapDisplayName={true}
+      toggleBlackBox={[MockFunction]}
+      toggleFrameworkGrouping={[Function]}
+    />
+    <Frame
+      copyStackTrace={[Function]}
+      disableContextMenu={false}
+      frame={
+        Object {
+          "id": 3,
+          "library": "back",
+        }
+      }
+      frameworkGroupingOn={false}
+      hideLocation={false}
+      key="3"
+      selectFrame={[MockFunction]}
+      selectable={false}
+      selectedFrame={
+        Object {
+          "id": 1,
+        }
+      }
+      shouldMapDisplayName={true}
+      toggleBlackBox={[MockFunction]}
+      toggleFrameworkGrouping={[Function]}
+    />
+    <Frame
+      copyStackTrace={[Function]}
+      disableContextMenu={false}
+      frame={
+        Object {
+          "id": 8,
+        }
+      }
+      frameworkGroupingOn={false}
+      hideLocation={false}
+      key="8"
+      selectFrame={[MockFunction]}
+      selectable={false}
+      selectedFrame={
+        Object {
+          "id": 1,
+        }
+      }
+      shouldMapDisplayName={true}
+      toggleBlackBox={[MockFunction]}
+      toggleFrameworkGrouping={[Function]}
+    />
+  </div>
+</div>
+`;
+
+exports[`Frames Library Frames toggling framework frames 2`] = `
+<div
+  className="pane frames"
+>
+  <div
+    role="list"
+  >
+    <Frame
+      copyStackTrace={[Function]}
+      disableContextMenu={false}
+      frame={
+        Object {
+          "id": 1,
+        }
+      }
+      frameworkGroupingOn={true}
+      hideLocation={false}
+      key="1"
+      selectFrame={[MockFunction]}
+      selectable={false}
       selectedFrame={
         Object {
           "id": 1,
         }
       }
       shouldMapDisplayName={true}
       toggleBlackBox={[MockFunction]}
       toggleFrameworkGrouping={[Function]}
@@ -273,16 +472,17 @@ exports[`Frames Library Frames toggling 
           Object {
             "id": 3,
             "library": "back",
           },
         ]
       }
       key="2"
       selectFrame={[MockFunction]}
+      selectable={false}
       selectedFrame={
         Object {
           "id": 1,
         }
       }
       toggleBlackBox={[MockFunction]}
       toggleFrameworkGrouping={[Function]}
     />
@@ -293,46 +493,50 @@ exports[`Frames Library Frames toggling 
         Object {
           "id": 8,
         }
       }
       frameworkGroupingOn={true}
       hideLocation={false}
       key="8"
       selectFrame={[MockFunction]}
+      selectable={false}
       selectedFrame={
         Object {
           "id": 1,
         }
       }
       shouldMapDisplayName={true}
       toggleBlackBox={[MockFunction]}
       toggleFrameworkGrouping={[Function]}
     />
-  </ul>
+  </div>
 </div>
 `;
 
 exports[`Frames Supports different number of frames disable frame truncation 1`] = `
 <div
   className="pane frames"
 >
-  <ul>
+  <div
+    role="list"
+  >
     <Frame
       copyStackTrace={[Function]}
       disableContextMenu={false}
       frame={
         Object {
           "id": 1,
         }
       }
       frameworkGroupingOn={false}
       hideLocation={false}
       key="1"
       selectFrame={[MockFunction]}
+      selectable={false}
       selectedFrame={null}
       shouldMapDisplayName={true}
       toggleBlackBox={[MockFunction]}
       toggleFrameworkGrouping={[Function]}
     />
     <Frame
       copyStackTrace={[Function]}
       disableContextMenu={false}
@@ -340,16 +544,17 @@ exports[`Frames Supports different numbe
         Object {
           "id": 2,
         }
       }
       frameworkGroupingOn={false}
       hideLocation={false}
       key="2"
       selectFrame={[MockFunction]}
+      selectable={false}
       selectedFrame={null}
       shouldMapDisplayName={true}
       toggleBlackBox={[MockFunction]}
       toggleFrameworkGrouping={[Function]}
     />
     <Frame
       copyStackTrace={[Function]}
       disableContextMenu={false}
@@ -357,16 +562,17 @@ exports[`Frames Supports different numbe
         Object {
           "id": 3,
         }
       }
       frameworkGroupingOn={false}
       hideLocation={false}
       key="3"
       selectFrame={[MockFunction]}
+      selectable={false}
       selectedFrame={null}
       shouldMapDisplayName={true}
       toggleBlackBox={[MockFunction]}
       toggleFrameworkGrouping={[Function]}
     />
     <Frame
       copyStackTrace={[Function]}
       disableContextMenu={false}
@@ -374,16 +580,17 @@ exports[`Frames Supports different numbe
         Object {
           "id": 4,
         }
       }
       frameworkGroupingOn={false}
       hideLocation={false}
       key="4"
       selectFrame={[MockFunction]}
+      selectable={false}
       selectedFrame={null}
       shouldMapDisplayName={true}
       toggleBlackBox={[MockFunction]}
       toggleFrameworkGrouping={[Function]}
     />
     <Frame
       copyStackTrace={[Function]}
       disableContextMenu={false}
@@ -391,16 +598,17 @@ exports[`Frames Supports different numbe
         Object {
           "id": 5,
         }
       }
       frameworkGroupingOn={false}
       hideLocation={false}
       key="5"
       selectFrame={[MockFunction]}
+      selectable={false}
       selectedFrame={null}
       shouldMapDisplayName={true}
       toggleBlackBox={[MockFunction]}
       toggleFrameworkGrouping={[Function]}
     />
     <Frame
       copyStackTrace={[Function]}
       disableContextMenu={false}
@@ -408,16 +616,17 @@ exports[`Frames Supports different numbe
         Object {
           "id": 6,
         }
       }
       frameworkGroupingOn={false}
       hideLocation={false}
       key="6"
       selectFrame={[MockFunction]}
+      selectable={false}
       selectedFrame={null}
       shouldMapDisplayName={true}
       toggleBlackBox={[MockFunction]}
       toggleFrameworkGrouping={[Function]}
     />
     <Frame
       copyStackTrace={[Function]}
       disableContextMenu={false}
@@ -425,16 +634,17 @@ exports[`Frames Supports different numbe
         Object {
           "id": 7,
         }
       }
       frameworkGroupingOn={false}
       hideLocation={false}
       key="7"
       selectFrame={[MockFunction]}
+      selectable={false}
       selectedFrame={null}
       shouldMapDisplayName={true}
       toggleBlackBox={[MockFunction]}
       toggleFrameworkGrouping={[Function]}
     />
     <Frame
       copyStackTrace={[Function]}
       disableContextMenu={false}
@@ -442,16 +652,17 @@ exports[`Frames Supports different numbe
         Object {
           "id": 8,
         }
       }
       frameworkGroupingOn={false}
       hideLocation={false}
       key="8"
       selectFrame={[MockFunction]}
+      selectable={false}
       selectedFrame={null}
       shouldMapDisplayName={true}
       toggleBlackBox={[MockFunction]}
       toggleFrameworkGrouping={[Function]}
     />
     <Frame
       copyStackTrace={[Function]}
       disableContextMenu={false}
@@ -459,16 +670,17 @@ exports[`Frames Supports different numbe
         Object {
           "id": 9,
         }
       }
       frameworkGroupingOn={false}
       hideLocation={false}
       key="9"
       selectFrame={[MockFunction]}
+      selectable={false}
       selectedFrame={null}
       shouldMapDisplayName={true}
       toggleBlackBox={[MockFunction]}
       toggleFrameworkGrouping={[Function]}
     />
     <Frame
       copyStackTrace={[Function]}
       disableContextMenu={false}
@@ -476,16 +688,17 @@ exports[`Frames Supports different numbe
         Object {
           "id": 10,
         }
       }
       frameworkGroupingOn={false}
       hideLocation={false}
       key="10"
       selectFrame={[MockFunction]}
+      selectable={false}
       selectedFrame={null}
       shouldMapDisplayName={true}
       toggleBlackBox={[MockFunction]}
       toggleFrameworkGrouping={[Function]}
     />
     <Frame
       copyStackTrace={[Function]}
       disableContextMenu={false}
@@ -493,16 +706,17 @@ exports[`Frames Supports different numbe
         Object {
           "id": 11,
         }
       }
       frameworkGroupingOn={false}
       hideLocation={false}
       key="11"
       selectFrame={[MockFunction]}
+      selectable={false}
       selectedFrame={null}
       shouldMapDisplayName={true}
       toggleBlackBox={[MockFunction]}
       toggleFrameworkGrouping={[Function]}
     />
     <Frame
       copyStackTrace={[Function]}
       disableContextMenu={false}
@@ -510,16 +724,17 @@ exports[`Frames Supports different numbe
         Object {
           "id": 12,
         }
       }
       frameworkGroupingOn={false}
       hideLocation={false}
       key="12"
       selectFrame={[MockFunction]}
+      selectable={false}
       selectedFrame={null}
       shouldMapDisplayName={true}
       toggleBlackBox={[MockFunction]}
       toggleFrameworkGrouping={[Function]}
     />
     <Frame
       copyStackTrace={[Function]}
       disableContextMenu={false}
@@ -527,16 +742,17 @@ exports[`Frames Supports different numbe
         Object {
           "id": 13,
         }
       }
       frameworkGroupingOn={false}
       hideLocation={false}
       key="13"
       selectFrame={[MockFunction]}
+      selectable={false}
       selectedFrame={null}
       shouldMapDisplayName={true}
       toggleBlackBox={[MockFunction]}
       toggleFrameworkGrouping={[Function]}
     />
     <Frame
       copyStackTrace={[Function]}
       disableContextMenu={false}
@@ -544,16 +760,17 @@ exports[`Frames Supports different numbe
         Object {
           "id": 14,
         }
       }
       frameworkGroupingOn={false}
       hideLocation={false}
       key="14"
       selectFrame={[MockFunction]}
+      selectable={false}
       selectedFrame={null}
       shouldMapDisplayName={true}
       toggleBlackBox={[MockFunction]}
       toggleFrameworkGrouping={[Function]}
     />
     <Frame
       copyStackTrace={[Function]}
       disableContextMenu={false}
@@ -561,16 +778,17 @@ exports[`Frames Supports different numbe
         Object {
           "id": 15,
         }
       }
       frameworkGroupingOn={false}
       hideLocation={false}
       key="15"
       selectFrame={[MockFunction]}
+      selectable={false}
       selectedFrame={null}
       shouldMapDisplayName={true}
       toggleBlackBox={[MockFunction]}
       toggleFrameworkGrouping={[Function]}
     />
     <Frame
       copyStackTrace={[Function]}
       disableContextMenu={false}
@@ -578,16 +796,17 @@ exports[`Frames Supports different numbe
         Object {
           "id": 16,
         }
       }
       frameworkGroupingOn={false}
       hideLocation={false}
       key="16"
       selectFrame={[MockFunction]}
+      selectable={false}
       selectedFrame={null}
       shouldMapDisplayName={true}
       toggleBlackBox={[MockFunction]}
       toggleFrameworkGrouping={[Function]}
     />
     <Frame
       copyStackTrace={[Function]}
       disableContextMenu={false}
@@ -595,16 +814,17 @@ exports[`Frames Supports different numbe
         Object {
           "id": 17,
         }
       }
       frameworkGroupingOn={false}
       hideLocation={false}
       key="17"
       selectFrame={[MockFunction]}
+      selectable={false}
       selectedFrame={null}
       shouldMapDisplayName={true}
       toggleBlackBox={[MockFunction]}
       toggleFrameworkGrouping={[Function]}
     />
     <Frame
       copyStackTrace={[Function]}
       disableContextMenu={false}
@@ -612,16 +832,17 @@ exports[`Frames Supports different numbe
         Object {
           "id": 18,
         }
       }
       frameworkGroupingOn={false}
       hideLocation={false}
       key="18"
       selectFrame={[MockFunction]}
+      selectable={false}
       selectedFrame={null}
       shouldMapDisplayName={true}
       toggleBlackBox={[MockFunction]}
       toggleFrameworkGrouping={[Function]}
     />
     <Frame
       copyStackTrace={[Function]}
       disableContextMenu={false}
@@ -629,16 +850,17 @@ exports[`Frames Supports different numbe
         Object {
           "id": 19,
         }
       }
       frameworkGroupingOn={false}
       hideLocation={false}
       key="19"
       selectFrame={[MockFunction]}
+      selectable={false}
       selectedFrame={null}
       shouldMapDisplayName={true}
       toggleBlackBox={[MockFunction]}
       toggleFrameworkGrouping={[Function]}
     />
     <Frame
       copyStackTrace={[Function]}
       disableContextMenu={false}
@@ -646,22 +868,23 @@ exports[`Frames Supports different numbe
         Object {
           "id": 20,
         }
       }
       frameworkGroupingOn={false}
       hideLocation={false}
       key="20"
       selectFrame={[MockFunction]}
+      selectable={false}
       selectedFrame={null}
       shouldMapDisplayName={true}
       toggleBlackBox={[MockFunction]}
       toggleFrameworkGrouping={[Function]}
     />
-  </ul>
+  </div>
 </div>
 `;
 
 exports[`Frames Supports different number of frames empty frames 1`] = `
 <div
   className="pane frames"
 >
   <div
@@ -671,47 +894,52 @@ exports[`Frames Supports different numbe
   </div>
 </div>
 `;
 
 exports[`Frames Supports different number of frames one frame 1`] = `
 <div
   className="pane frames"
 >
-  <ul>
+  <div
+    role="list"
+  >
     <Frame
       copyStackTrace={[Function]}
       disableContextMenu={false}
       frame={
         Object {
           "id": 1,
         }
       }
       frameworkGroupingOn={false}
       hideLocation={false}
       key="1"
       selectFrame={[MockFunction]}
+      selectable={false}
       selectedFrame={
         Object {
           "id": 1,
         }
       }
       shouldMapDisplayName={true}
       toggleBlackBox={[MockFunction]}
       toggleFrameworkGrouping={[Function]}
     />
-  </ul>
+  </div>
 </div>
 `;
 
 exports[`Frames Supports different number of frames passes the getFrameTitle prop to the Frame component 1`] = `
 <div
   className="pane frames"
 >
-  <ul>
+  <div
+    role="list"
+  >
     <Frame
       copyStackTrace={[Function]}
       disableContextMenu={false}
       frame={
         Object {
           "displayName": "renderFoo",
           "id": 1,
           "location": Object {
@@ -722,42 +950,46 @@ exports[`Frames Supports different numbe
           },
         }
       }
       frameworkGroupingOn={false}
       getFrameTitle={[Function]}
       hideLocation={false}
       key="1"
       selectFrame={[MockFunction]}
+      selectable={false}
       selectedFrame={null}
       shouldMapDisplayName={true}
       toggleBlackBox={[MockFunction]}
       toggleFrameworkGrouping={[Function]}
     />
-  </ul>
+  </div>
 </div>
 `;
 
 exports[`Frames Supports different number of frames toggling the show more button 1`] = `
 <div
   className="pane frames"
 >
-  <ul>
+  <div
+    role="list"
+  >
     <Frame
       copyStackTrace={[Function]}
       disableContextMenu={false}
       frame={
         Object {
           "id": 1,
         }
       }
       frameworkGroupingOn={false}
       hideLocation={false}
       key="1"
       selectFrame={[MockFunction]}
+      selectable={false}
       selectedFrame={
         Object {
           "id": 1,
         }
       }
       shouldMapDisplayName={true}
       toggleBlackBox={[MockFunction]}
       toggleFrameworkGrouping={[Function]}
@@ -769,16 +1001,17 @@ exports[`Frames Supports different numbe
         Object {
           "id": 2,
         }
       }
       frameworkGroupingOn={false}
       hideLocation={false}
       key="2"
       selectFrame={[MockFunction]}
+      selectable={false}
       selectedFrame={
         Object {
           "id": 1,
         }
       }
       shouldMapDisplayName={true}
       toggleBlackBox={[MockFunction]}
       toggleFrameworkGrouping={[Function]}
@@ -790,16 +1023,17 @@ exports[`Frames Supports different numbe
         Object {
           "id": 3,
         }
       }
       frameworkGroupingOn={false}
       hideLocation={false}
       key="3"
       selectFrame={[MockFunction]}
+      selectable={false}
       selectedFrame={
         Object {
           "id": 1,
         }
       }
       shouldMapDisplayName={true}
       toggleBlackBox={[MockFunction]}
       toggleFrameworkGrouping={[Function]}
@@ -811,16 +1045,17 @@ exports[`Frames Supports different numbe
         Object {
           "id": 4,
         }
       }
       frameworkGroupingOn={false}
       hideLocation={false}
       key="4"
       selectFrame={[MockFunction]}
+      selectable={false}
       selectedFrame={
         Object {
           "id": 1,
         }
       }
       shouldMapDisplayName={true}
       toggleBlackBox={[MockFunction]}
       toggleFrameworkGrouping={[Function]}
@@ -832,16 +1067,17 @@ exports[`Frames Supports different numbe
         Object {
           "id": 5,
         }
       }
       frameworkGroupingOn={false}
       hideLocation={false}
       key="5"
       selectFrame={[MockFunction]}
+      selectable={false}
       selectedFrame={
         Object {
           "id": 1,
         }
       }
       shouldMapDisplayName={true}
       toggleBlackBox={[MockFunction]}
       toggleFrameworkGrouping={[Function]}
@@ -853,16 +1089,17 @@ exports[`Frames Supports different numbe
         Object {
           "id": 6,
         }
       }
       frameworkGroupingOn={false}
       hideLocation={false}
       key="6"
       selectFrame={[MockFunction]}
+      selectable={false}
       selectedFrame={
         Object {
           "id": 1,
         }
       }
       shouldMapDisplayName={true}
       toggleBlackBox={[MockFunction]}
       toggleFrameworkGrouping={[Function]}
@@ -874,16 +1111,17 @@ exports[`Frames Supports different numbe
         Object {
           "id": 7,
         }
       }
       frameworkGroupingOn={false}
       hideLocation={false}
       key="7"
       selectFrame={[MockFunction]}
+      selectable={false}
       selectedFrame={
         Object {
           "id": 1,
         }
       }
       shouldMapDisplayName={true}
       toggleBlackBox={[MockFunction]}
       toggleFrameworkGrouping={[Function]}
@@ -895,16 +1133,17 @@ exports[`Frames Supports different numbe
         Object {
           "id": 8,
         }
       }
       frameworkGroupingOn={false}
       hideLocation={false}
       key="8"
       selectFrame={[MockFunction]}
+      selectable={false}
       selectedFrame={
         Object {
           "id": 1,
         }
       }
       shouldMapDisplayName={true}
       toggleBlackBox={[MockFunction]}
       toggleFrameworkGrouping={[Function]}
@@ -916,16 +1155,17 @@ exports[`Frames Supports different numbe
         Object {
           "id": 9,
         }
       }
       frameworkGroupingOn={false}
       hideLocation={false}
       key="9"
       selectFrame={[MockFunction]}
+      selectable={false}
       selectedFrame={
         Object {
           "id": 1,
         }
       }
       shouldMapDisplayName={true}
       toggleBlackBox={[MockFunction]}
       toggleFrameworkGrouping={[Function]}
@@ -937,26 +1177,27 @@ exports[`Frames Supports different numbe
         Object {
           "id": 10,
         }
       }
       frameworkGroupingOn={false}
       hideLocation={false}
       key="10"
       selectFrame={[MockFunction]}
+      selectable={false}
       selectedFrame={
         Object {
           "id": 1,
         }
       }
       shouldMapDisplayName={true}
       toggleBlackBox={[MockFunction]}
       toggleFrameworkGrouping={[Function]}
     />
-  </ul>
+  </div>
   <div
     className="show-more-container"
   >
     <button
       className="show-more"
       onClick={[Function]}
     >
       Collapse rows
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Frames/tests/__snapshots__/Group.spec.js.snap
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Frames/tests/__snapshots__/Group.spec.js.snap
@@ -1,80 +1,87 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
 exports[`Group displays a group 1`] = `
 <div
   className="frames-group"
   onContextMenu={[Function]}
 >
-  <li
+  <div
     className="group"
     onClick={[Function]}
+    role="listitem"
     tabIndex={0}
     title="Show Back frames"
   >
-    <span
-      className="title"
-    >
-      foo
-    </span>
-    <Badge>
-      1
-    </Badge>
+    <FrameIndent />
     <FrameLocation
+      expanded={false}
       frame={
         Object {
           "displayName": "foo",
           "library": "Back",
         }
       }
     />
-  </li>
+    <span
+      className="clipboard-only"
+    >
+       
+    </span>
+    <Badge>
+      1
+    </Badge>
+  </div>
 </div>
 `;
 
 exports[`Group passes the getFrameTitle prop to the Frame components 1`] = `
 <div
   className="frames-group expanded"
   onContextMenu={[Function]}
 >
-  <li
+  <div
     className="group"
     key="1"
     onClick={[Function]}
+    role="listitem"
     tabIndex={0}
     title="Collapse Back frames"
   >
-    <span
-      className="title"
-    >
-      renderFoo
-    </span>
-    <Badge>
-      3
-    </Badge>
+    <FrameIndent />
     <FrameLocation
+      expanded={true}
       frame={
         Object {
           "displayName": "renderFoo",
           "id": 1,
           "library": "Back",
           "location": Object {
             "line": 55,
           },
           "source": Object {
             "url": "http://myfile.com/mahscripts.js",
           },
         }
       }
     />
-  </li>
+    <span
+      className="clipboard-only"
+    >
+       
+    </span>
+    <Badge>
+      3
+    </Badge>
+  </div>
   <div
     className="frames-list"
   >
+    <FrameIndent />
     <Frame
       copyStackTrace={[MockFunction]}
       disableContextMenu={false}
       displayFullUrl={false}
       frame={
         Object {
           "displayName": "renderFoo",
           "id": 1,
@@ -87,21 +94,23 @@ exports[`Group passes the getFrameTitle 
           },
         }
       }
       frameworkGroupingOn={true}
       getFrameTitle={[Function]}
       hideLocation={true}
       key="1"
       selectFrame={[MockFunction]}
+      selectable={true}
       selectedFrame={Object {}}
       shouldMapDisplayName={false}
       toggleBlackBox={[MockFunction]}
       toggleFrameworkGrouping={[MockFunction]}
     />
+    <FrameIndent />
     <Frame
       copyStackTrace={[MockFunction]}
       disableContextMenu={false}
       displayFullUrl={false}
       frame={
         Object {
           "displayName": "a",
           "id": 2,
@@ -114,21 +123,23 @@ exports[`Group passes the getFrameTitle 
           },
         }
       }
       frameworkGroupingOn={true}
       getFrameTitle={[Function]}
       hideLocation={true}
       key="2"
       selectFrame={[MockFunction]}
+      selectable={true}
       selectedFrame={Object {}}
       shouldMapDisplayName={false}
       toggleBlackBox={[MockFunction]}
       toggleFrameworkGrouping={[MockFunction]}
     />
+    <FrameIndent />
     <Frame
       copyStackTrace={[MockFunction]}
       disableContextMenu={false}
       displayFullUrl={false}
       frame={
         Object {
           "displayName": "b",
           "id": 3,
@@ -141,103 +152,111 @@ exports[`Group passes the getFrameTitle 
           },
         }
       }
       frameworkGroupingOn={true}
       getFrameTitle={[Function]}
       hideLocation={true}
       key="3"
       selectFrame={[MockFunction]}
+      selectable={true}
       selectedFrame={Object {}}
       shouldMapDisplayName={false}
       toggleBlackBox={[MockFunction]}
       toggleFrameworkGrouping={[MockFunction]}
     />
   </div>
 </div>
 `;
 
 exports[`Group renders group with anonymous functions 1`] = `
 <div
   className="frames-group"
   onContextMenu={[Function]}
 >
-  <li
+  <div
     className="group"
     key="1"
     onClick={[Function]}
+    role="listitem"
     tabIndex={0}
     title="Show Back frames"
   >
-    <span
-      className="title"
-    >
-      &lt;anonymous&gt;
-    </span>
-    <Badge>
-      3
-    </Badge>
+    <FrameIndent />
     <FrameLocation
+      expanded={false}
       frame={
         Object {
           "displayName": "",
           "id": 1,
           "library": "Back",
           "location": Object {
             "line": 55,
           },
           "source": Object {
             "url": "http://myfile.com/mahscripts.js",
           },
         }
       }
     />
-  </li>
+    <span
+      className="clipboard-only"
+    >
+       
+    </span>
+    <Badge>
+      3
+    </Badge>
+  </div>
 </div>
 `;
 
 exports[`Group renders group with anonymous functions 2`] = `
 <div
   className="frames-group expanded"
   onContextMenu={[Function]}
 >
-  <li
+  <div
     className="group"
     key="1"
     onClick={[Function]}
+    role="listitem"
     tabIndex={0}
     title="Collapse Back frames"
   >
-    <span
-      className="title"
-    >
-      &lt;anonymous&gt;
-    </span>
-    <Badge>
-      3
-    </Badge>
+    <FrameIndent />
     <FrameLocation
+      expanded={true}
       frame={
         Object {
           "displayName": "",
           "id": 1,
           "library": "Back",
           "location": Object {
             "line": 55,
           },
           "source": Object {
             "url": "http://myfile.com/mahscripts.js",
           },
         }
       }
     />
-  </li>
+    <span
+      className="clipboard-only"
+    >
+       
+    </span>
+    <Badge>
+      3
+    </Badge>
+  </div>
   <div
     className="frames-list"
   >
+    <FrameIndent />
     <Frame
       copyStackTrace={[MockFunction]}
       disableContextMenu={false}
       displayFullUrl={false}
       frame={
         Object {
           "displayName": "",
           "id": 1,
@@ -249,21 +268,23 @@ exports[`Group renders group with anonym
             "url": "http://myfile.com/mahscripts.js",
           },
         }
       }
       frameworkGroupingOn={true}
       hideLocation={true}
       key="1"
       selectFrame={[MockFunction]}
+      selectable={true}
       selectedFrame={Object {}}
       shouldMapDisplayName={false}
       toggleBlackBox={[MockFunction]}
       toggleFrameworkGrouping={[MockFunction]}
     />
+    <FrameIndent />
     <Frame
       copyStackTrace={[MockFunction]}
       disableContextMenu={false}
       displayFullUrl={false}
       frame={
         Object {
           "displayName": "",
           "id": 2,
@@ -275,21 +296,23 @@ exports[`Group renders group with anonym
             "url": "http://myfile.com/back.js",
           },
         }
       }
       frameworkGroupingOn={true}
       hideLocation={true}
       key="2"
       selectFrame={[MockFunction]}
+      selectable={true}
       selectedFrame={Object {}}
       shouldMapDisplayName={false}
       toggleBlackBox={[MockFunction]}
       toggleFrameworkGrouping={[MockFunction]}
     />
+    <FrameIndent />
     <Frame
       copyStackTrace={[MockFunction]}
       disableContextMenu={false}
       displayFullUrl={false}
       frame={
         Object {
           "displayName": "",
           "id": 3,
@@ -301,16 +324,17 @@ exports[`Group renders group with anonym
             "url": "http://myfile.com/back.js",
           },
         }
       }
       frameworkGroupingOn={true}
       hideLocation={true}
       key="3"
       selectFrame={[MockFunction]}
+      selectable={true}
       selectedFrame={Object {}}
       shouldMapDisplayName={false}
       toggleBlackBox={[MockFunction]}
       toggleFrameworkGrouping={[MockFunction]}
     />
   </div>
 </div>
 `;
--- a/devtools/client/debugger/new/src/components/shared/AccessibleImage.css
+++ b/devtools/client/debugger/new/src/components/shared/AccessibleImage.css
@@ -2,12 +2,8 @@
   /* default height an width which will likely be overrode */
   width: 12px;
   height: 12px;
   /* makes span appear like an image */
   display: inline-block;
   background: var(--theme-body-color);
   mask-size: 100%;
 }
-
-.img.arrow.arrow.expanded {
-  transform: rotate(0deg);
-}
--- a/devtools/client/debugger/new/src/components/shared/SourceIcon.css
+++ b/devtools/client/debugger/new/src/components/shared/SourceIcon.css
@@ -6,21 +6,27 @@
   margin-inline-end: 5px;
 }
 
 .source-icon {
   width: 15px;
   height: 15px;
 }
 
-.source-icon.prettyPrint {
+.img.prettyPrint {
   mask: url(/images/prettyPrint.svg) no-repeat;
   mask-size: 100%;
   background: var(--theme-highlight-blue);
   fill: var(--theme-textbox-box-shadow);
+  position: relative;
+}
+
+.sources-list .img.prettyPrint {
+  top: 2px;
+  margin-inline-start: 3px;
 }
 
 .source-icon.vue {
   background: url(/images/vuejs.svg) 1px 1px no-repeat;
   background-size: 15px;
 }
 
 .source-icon.angular {
--- a/devtools/client/debugger/new/src/main.js
+++ b/devtools/client/debugger/new/src/main.js
@@ -20,45 +20,50 @@ function unmountRoot() {
 
 if (isFirefoxPanel()) {
   module.exports = {
     bootstrap: ({
       threadClient,
       tabTarget,
       debuggerClient,
       sourceMaps,
-      toolboxActions
+      panel
     }: any) => {
       return onConnect(
         {
           tab: { clientType: "firefox" },
           tabConnection: {
             tabTarget,
             threadClient,
             debuggerClient
           }
         },
-        {
-          services: { sourceMaps },
-          toolboxActions
-        }
+        sourceMaps,
+        panel
       );
     },
     destroy: () => {
       unmountRoot();
       sourceQueue.clear();
       teardownWorkers();
     }
   };
 } else {
   const { bootstrap, L10N } = require("devtools-launchpad");
 
   window.L10N = L10N;
   // $FlowIgnore:
   window.L10N.setBundle(require("../assets/panel/debugger.properties"));
 
   bootstrap(React, ReactDOM).then(connection => {
-    onConnect(connection, {
-      services: { sourceMaps: require("devtools-source-map") },
-      toolboxActions: {}
+    onConnect(connection, require("devtools-source-map"), {
+      emit: eventName => console.log(`emitted: ${eventName}`),
+      openLink: url => {
+        const win = window.open(url, "_blank");
+        win.focus();
+      },
+      openWorkerToolbox: worker => alert(worker.url),
+      openElementInInspector: grip =>
+        alert(`Opening node in Inspector: ${grip.class}`),
+      openConsoleAndEvaluate: input => alert(`console.log: ${input}`)
     });
   });
 }
--- a/devtools/client/debugger/new/src/reducers/breakpoints.js
+++ b/devtools/client/debugger/new/src/reducers/breakpoints.js
@@ -7,22 +7,27 @@
 /**
  * Breakpoints reducer
  * @module reducers/breakpoints
  */
 
 import { isGeneratedId } from "devtools-source-map";
 import { isEqual } from "lodash";
 
-import { makeLocationId } from "../utils/breakpoint";
+import { makeBreakpointId } from "../utils/breakpoint";
 
-import type { XHRBreakpoint, Breakpoint, SourceLocation } from "../types";
+import type {
+  XHRBreakpoint,
+  Breakpoint,
+  BreakpointId,
+  SourceLocation
+} from "../types";
 import type { Action, DonePromiseAction } from "../actions/types";
 
-export type BreakpointsMap = { [string]: Breakpoint };
+export type BreakpointsMap = { [BreakpointId]: Breakpoint };
 export type XHRBreakpointsList = $ReadOnlyArray<XHRBreakpoint>;
 
 export type BreakpointsState = {
   breakpoints: BreakpointsMap,
   xhrBreakpoints: XHRBreakpointsList
 };
 
 export function initialBreakpointsState(
@@ -169,88 +174,88 @@ function unsetBreakpoint(state, location
     ...state,
     breakpoints: { ...breakpoints }
   };
 }
 
 function addBreakpoint(state, action): BreakpointsState {
   if (action.status === "start" && action.breakpoint) {
     const { breakpoint } = action;
-    const locationId = makeLocationId(breakpoint.location);
+    const locationId = makeBreakpointId(breakpoint.location);
     return setBreakpoint(state, locationId, breakpoint);
   }
 
   // when the action completes, we can commit the breakpoint
   if (action.status === "done") {
     const { value } = ((action: any): DonePromiseAction);
     return syncBreakpoint(state, value);
   }
 
   // Remove the optimistic update
   if (action.status === "error" && action.breakpoint) {
-    const locationId = makeLocationId(action.breakpoint.location);
+    const locationId = makeBreakpointId(action.breakpoint.location);
     return unsetBreakpoint(state, locationId);
   }
 
   return state;
 }
 
 function syncBreakpoint(state, data): BreakpointsState {
   const { breakpoint, previousLocation } = data;
 
   if (previousLocation) {
     state = {
       ...state,
       breakpoints: { ...state.breakpoints }
     };
-    delete state.breakpoints[makeLocationId(previousLocation)];
+    delete state.breakpoints[makeBreakpointId(previousLocation)];
   }
 
   if (!breakpoint) {
     return state;
   }
 
-  const locationId = makeLocationId(breakpoint.location);
+  const locationId = makeBreakpointId(breakpoint.location);
   return setBreakpoint(state, locationId, breakpoint);
 }
 
 function updateBreakpoint(state, action): BreakpointsState {
   const { breakpoint } = action;
-  const locationId = makeLocationId(breakpoint.location);
+  const locationId = makeBreakpointId(breakpoint.location);
   return setBreakpoint(state, locationId, breakpoint);
 }
 
 function updateAllBreakpoints(state, action): BreakpointsState {
   const { breakpoints } = action;
   state = {
     ...state,
     breakpoints: { ...state.breakpoints }
   };
   breakpoints.forEach(breakpoint => {
-    const locationId = makeLocationId(breakpoint.location);
+    const locationId = makeBreakpointId(breakpoint.location);
     state.breakpoints[locationId] = breakpoint;
   });
   return state;
 }
 
 function remapBreakpoints(state, action): BreakpointsState {
   const breakpoints = action.breakpoints.reduce(
     (updatedBreakpoints, breakpoint) => {
-      const locationId = makeLocationId(breakpoint.location);
+      const locationId = makeBreakpointId(breakpoint.location);
       return { ...updatedBreakpoints, [locationId]: breakpoint };
     },
     {}
   );
 
   return { ...state, breakpoints };
 }
 
 function removeBreakpoint(state, action): BreakpointsState {
   const { breakpoint } = action;
-  const id = makeLocationId(breakpoint.location);
+  const id = makeBreakpointId(breakpoint.location);
   return unsetBreakpoint(state, id);
 }
 
 function isMatchingLocation(location1, location2) {
   return isEqual(location1, location2);
 }
 
 // Selectors
@@ -270,17 +275,17 @@ export function getBreakpointCount(state
   return getBreakpointsList(state).length;
 }
 
 export function getBreakpoint(
   state: OuterState,
   location: SourceLocation
 ): ?Breakpoint {
   const breakpoints = getBreakpointsMap(state);
-  return breakpoints[makeLocationId(location)];
+  return breakpoints[makeBreakpointId(location)];
 }
 
 export function getBreakpointsDisabled(state: OuterState): boolean {
   const breakpoints = getBreakpointsList(state);
   return breakpoints.every(breakpoint => breakpoint.disabled);
 }
 
 export function getBreakpointsLoading(state: OuterState): boolean {
--- a/devtools/client/debugger/new/src/reducers/pause.js
+++ b/devtools/client/debugger/new/src/reducers/pause.js
@@ -63,16 +63,17 @@ type ThreadPauseState = {
       }
     }
   },
   selectedFrameId: ?string,
   loadedObjects: Object,
   shouldPauseOnExceptions: boolean,
   shouldPauseOnCaughtExceptions: boolean,
   command: Command,
+  lastCommand: Command,
   previousLocation: ?MappedLocation,
   skipPausing: boolean
 };
 
 // Pause state describing all threads.
 export type PauseState = {
   currentThread: string,
   canRewind: boolean,
@@ -99,16 +100,17 @@ const resumedPauseState = {
 
 const createInitialPauseState = () => ({
   ...resumedPauseState,
   isWaitingOnBreak: false,
   shouldPauseOnExceptions: prefs.pauseOnExceptions,
   shouldPauseOnCaughtExceptions: prefs.pauseOnCaughtExceptions,
   canRewind: false,
   command: null,
+  lastCommand: null,
   previousLocation: null,
   skipPausing: prefs.skipPausing
 });
 
 function getThreadPauseState(state: PauseState, thread: string) {
   // Thread state is lazily initialized so that we don't have to keep track of
   // the current set of worker threads.
   return state.threads[thread] || createInitialPauseState();
@@ -259,16 +261,17 @@ function update(
       });
     }
 
     case "COMMAND":
       if (action.status === "start") {
         return updateThreadState({
           ...resumedPauseState,
           command: action.command,
+          lastCommand: action.command,
           previousLocation: getPauseLocation(threadState(), action)
         });
       }
       return updateThreadState({ command: null });
 
     case "RESUME":
       // Workaround for threads resuming before the initial connection.
       if (!action.thread && !state.currentThread) {
@@ -347,16 +350,20 @@ export const getAllPopupObjectProperties
 export function getPauseReason(state: OuterState): ?Why {
   return getCurrentPauseState(state).why;
 }
 
 export function getPauseCommand(state: OuterState): Command {
   return getCurrentPauseState(state).command;
 }
 
+export function getLastCommand(state: OuterState, thread: string) {
+  return getThreadPauseState(state.pause, thread).lastCommand;
+}
+
 export function isStepping(state: OuterState) {
   return ["stepIn", "stepOver", "stepOut"].includes(getPauseCommand(state));
 }
 
 export function getCurrentThread(state: OuterState) {
   return state.pause.currentThread;
 }
 
--- a/devtools/client/debugger/new/src/reducers/sources.js
+++ b/devtools/client/debugger/new/src/reducers/sources.js
@@ -17,43 +17,67 @@ import {
   isGenerated,
   isOriginal as isOriginalSource,
   isUrlExtension
 } from "../utils/source";
 
 import { originalToGeneratedId } from "devtools-source-map";
 import { prefs } from "../utils/prefs";
 
-import type { Source, SourceId, SourceLocation, Thread } from "../types";
+import type {
+  Source,
+  SourceActor,
+  SourceId,
+  SourceLocation,
+  ThreadId,
+  WorkerList
+} from "../types";
 import type { PendingSelectedLocation, Selector } from "./types";
 import type { Action, DonePromiseAction, FocusItem } from "../actions/types";
 import type { LoadSourceAction } from "../actions/types/SourceAction";
 import { omitBy, mapValues } from "lodash";
 
-export type SourcesMap = { [string]: Source };
-export type SourcesMapByThread = { [string]: SourcesMap };
+export type SourcesMap = { [SourceId]: Source };
+export type SourcesMapByThread = { [ThreadId]: SourcesMap };
 
+type SourceActorsMap = { [SourceId]: SourceActor[] };
 type UrlsMap = { [string]: SourceId[] };
 type GetRelativeSourcesSelector = OuterState => SourcesMapByThread;
 
 export type SourcesState = {
+  // All known sources.
   sources: SourcesMap,
+
+  // Actors associated with each source.
+  sourceActors: SourceActorsMap,
+
+  // All sources associated with a given URL. When using source maps, multiple
+  // sources can have the same URL.
   urls: UrlsMap,
+
+  // All original sources associated with a generated source.
+  originalSources: { [SourceId]: SourceId[] },
+
+  // For each thread, all sources in that thread that are under the project root
+  // and should be shown in the editor's sources pane.
   relativeSources: SourcesMapByThread,
+
   pendingSelectedLocation?: PendingSelectedLocation,
   selectedLocation: ?SourceLocation,
   projectDirectoryRoot: string,
   chromeAndExtenstionsEnabled: boolean,
   focusedItem: ?FocusItem
 };
 
 export function initialSourcesState(): SourcesState {
   return {
     sources: {},
+    sourceActors: {},
     urls: {},
+    originalSources: {},
     relativeSources: {},
     selectedLocation: undefined,
     pendingSelectedLocation: prefs.pendingSelectedLocation,
     projectDirectoryRoot: prefs.projectDirectoryRoot,
     chromeAndExtenstionsEnabled: prefs.chromeAndExtenstionsEnabled,
     focusedItem: null
   };
 }
@@ -77,29 +101,27 @@ export function createSource(source: Obj
 
 function update(
   state: SourcesState = initialSourcesState(),
   action: Action
 ): SourcesState {
   let location = null;
 
   switch (action.type) {
-    case "UPDATE_SOURCE": {
-      const source = action.source;
-      return updateSources(state, [source]);
-    }
+    case "UPDATE_SOURCE":
+      return updateSources(state, [action.source]);
+
+    case "ADD_SOURCE":
+      return updateSources(state, [action.source]);
 
-    case "ADD_SOURCE": {
-      const source = action.source;
-      return updateSources(state, [source]);
-    }
+    case "ADD_SOURCES":
+      return updateSources(state, action.sources, action.sourceActors);
 
-    case "ADD_SOURCES": {
-      return updateSources(state, action.sources);
-    }
+    case "SET_WORKERS":
+      return updateWorkers(state, action.workers, action.mainThread);
 
     case "SET_SELECTED_LOCATION":
       location = {
         ...action.location,
         url: action.source.url
       };
 
       if (action.source.url) {
@@ -144,25 +166,18 @@ function update(
         updateBlackBoxList(url, isBlackBoxed);
         return updateSources(state, [{ id, isBlackBoxed }]);
       }
       break;
 
     case "SET_PROJECT_DIRECTORY_ROOT":
       return updateProjectDirectoryRoot(state, action.url);
 
-    case "SET_WORKERS":
-      return addRelativeSourceThreads(state, action.workers);
-
     case "NAVIGATE":
-      const newState = initialSourcesState();
-      return addRelativeSourceThread(newState, action.mainThread);
-
-    case "CONNECT":
-      return addRelativeSourceThread(state, action.mainThread);
+      return initialSourcesState();
 
     case "SET_FOCUSED_SOURCE_ITEM":
       return { ...state, focusedItem: action.item };
   }
 
   return state;
 }
 
@@ -194,116 +209,164 @@ function getTextPropsFromAction(action) 
 function setSourceTextProps(state, action: LoadSourceAction): SourcesState {
   const source = getTextPropsFromAction(action);
   if (!source) {
     return state;
   }
   return updateSources(state, [source]);
 }
 
-function updateSources(state, sources) {
+function updateSources(state, sources, sourceActors) {
   const relativeSources = { ...state.relativeSources };
   for (const thread in relativeSources) {
     relativeSources[thread] = { ...relativeSources[thread] };
   }
 
   state = {
     ...state,
     sources: { ...state.sources },
-    relativeSources,
-    urls: { ...state.urls }
+    sourceActors: { ...state.sourceActors },
+    urls: { ...state.urls },
+    originalSources: { ...state.originalSources },
+    relativeSources
   };
 
-  return sources.reduce(
-    (newState, source) => updateSource(newState, source),
-    state
-  );
+  sources.forEach(source => updateSource(state, source));
+  if (sourceActors) {
+    sourceActors.forEach(sourceActor =>
+      updateForNewSourceActor(state, sourceActor)
+    );
+  }
+
+  return state;
+}
+
+function updateSourceUrl(state: SourcesState, source: Object) {
+  const existing = state.urls[source.url] || [];
+  if (!existing.includes(source.id)) {
+    state.urls[source.url] = [...existing, source.id];
+  }
+}
+
+function updateOriginalSources(state: SourcesState, source: Object) {
+  if (!isOriginalSource(source)) {
+    return;
+  }
+  const generatedId = originalToGeneratedId(source.id);
+  const existing = state.originalSources[generatedId] || [];
+  if (!existing.includes(source.id)) {
+    state.originalSources[generatedId] = [...existing, source.id];
+
+    // Update relative sources for any affected threads.
+    if (state.sourceActors[generatedId]) {
+      for (const sourceActor of state.sourceActors[generatedId]) {
+        updateRelativeSource(state, source, sourceActor);
+      }
+    }
+  }
 }
 
 function updateSource(state: SourcesState, source: Object) {
   if (!source.id) {
-    return state;
+    return;
   }
 
   const existingSource = state.sources[source.id];
   const updatedSource = existingSource
     ? { ...existingSource, ...source }
     : createSource(source);
 
   state.sources[source.id] = updatedSource;
 
-  const existingUrls = state.urls[source.url];
-  state.urls[source.url] = existingUrls
-    ? [...existingUrls, source.id]
-    : [source.id];
-
-  updateRelativeSource(
-    state.relativeSources,
-    updatedSource,
-    state.projectDirectoryRoot
-  );
-
-  return state;
+  updateSourceUrl(state, source);
+  updateOriginalSources(state, source);
 }
 
 function updateRelativeSource(
-  relativeSources: SourcesMapByThread,
-  source: Source,
-  root: string
-): SourcesMapByThread {
+  state: SourcesState,
+  source: Object,
+  sourceActor: SourceActor
+) {
+  const root = state.projectDirectoryRoot;
+
   if (!underRoot(source, root)) {
-    return relativeSources;
+    return;
   }
 
   const relativeSource: Source = ({
     ...source,
     relativeUrl: getRelativeUrl(source, root)
   }: any);
 
-  if (!relativeSources[source.thread]) {
-    relativeSources[source.thread] = {};
+  if (!state.relativeSources[sourceActor.thread]) {
+    state.relativeSources[sourceActor.thread] = {};
+  }
+  state.relativeSources[sourceActor.thread][source.id] = relativeSource;
+}
+
+function updateForNewSourceActor(
+  state: SourcesState,
+  sourceActor: SourceActor
+) {
+  const existing = state.sourceActors[sourceActor.source] || [];
+
+  // Do not allow duplicate source actors in the store.
+  if (existing.some(({ actor }) => actor == sourceActor.actor)) {
+    return;
   }
 
-  relativeSources[source.thread][source.id] = relativeSource;
+  state.sourceActors[sourceActor.source] = [...existing, sourceActor];
 
-  return relativeSources;
+  updateRelativeSource(state, state.sources[sourceActor.source], sourceActor);
 }
 
-function addRelativeSourceThread(state: SourcesState, thread: Thread) {
-  if (getRelativeSourcesForThread({ sources: state }, thread.actor)) {
-    return state;
-  }
-  return {
-    ...state,
-    relativeSources: { ...state.relativeSources, [thread.actor]: {} }
-  };
-}
-
-function addRelativeSourceThreads(state: SourcesState, workers: Thread[]) {
-  let newState = state;
-  for (const worker of workers) {
-    newState = addRelativeSourceThread(newState, worker);
+function updateWorkers(
+  state: SourcesState,
+  workers: WorkerList,
+  mainThread: ThreadId
+) {
+  // Clear out actors for any removed workers by regenerating source actor
+  // state for all remaining workers.
+  const sourceActors = [];
+  for (const actors: any of Object.values(state.sourceActors)) {
+    for (const sourceActor of actors) {
+      if (
+        workers.some(worker => worker.actor == sourceActor.thread) ||
+        mainThread == sourceActor.thread
+      ) {
+        sourceActors.push(sourceActor);
+      }
+    }
   }
 
-  return newState;
+  return updateSources(
+    { ...state, sourceActors: {}, relativeSources: {} },
+    [],
+    sourceActors
+  );
 }
 
 function updateProjectDirectoryRoot(state: SourcesState, root: string) {
   prefs.projectDirectoryRoot = root;
 
-  const relativeSources = getSourceList({ sources: state }).reduce(
-    (sources, source: Source) => updateRelativeSource(sources, source, root),
-    {}
-  );
+  const sourceActors = [];
+  for (const actors: any of Object.values(state.sourceActors)) {
+    actors.forEach(sourceActor => sourceActors.push(sourceActor));
+  }
 
-  return {
-    ...state,
-    projectDirectoryRoot: root,
-    relativeSources
-  };
+  return updateSources(
+    {
+      ...state,
+      projectDirectoryRoot: root,
+      sourceActors: {},
+      relativeSources: {}
+    },
+    [],
+    sourceActors
+  );
 }
 
 function updateBlackBoxList(url, isBlackBoxed) {
   const tabs = getBlackBoxList();
   const i = tabs.indexOf(url);
   if (i >= 0) {
     if (!isBlackBoxed) {
       tabs.splice(i, 1);
@@ -326,45 +389,52 @@ export function getBlackBoxList() {
 // module for the UI, and all of those selectors should take the
 // top-level app state, so we'd have to "wrap" them to automatically
 // pick off the piece of state we're interested in. It's impossible
 // (right now) to type those wrapped functions.
 type OuterState = { sources: SourcesState };
 
 const getSourcesState = (state: OuterState) => state.sources;
 
-export function getSource(state: OuterState, id: string) {
+export function getSource(state: OuterState, id: SourceId) {
   return getSourceInSources(getSources(state), id);
 }
 
 export function getSourceFromId(state: OuterState, id: string): Source {
   return getSourcesState(state).sources[id];
 }
 
+export function getSourceActors(state: OuterState, id: SourceId) {
+  return getSourcesState(state).sourceActors[id] || [];
+}
+
+export function hasSourceActor(state: OuterState, sourceActor: SourceActor) {
+  const existing = getSourceActors(state, sourceActor.source);
+  return existing.some(({ actor }) => actor == sourceActor.actor);
+}
+
 export function getOriginalSourceByURL(
   state: OuterState,
   url: string
 ): ?Source {
   return getOriginalSourceByUrlInSources(
     getSources(state),
     getUrls(state),
-    url,
-    ""
+    url
   );
 }
 
 export function getGeneratedSourceByURL(
   state: OuterState,
   url: string
 ): ?Source {
   return getGeneratedSourceByUrlInSources(
     getSources(state),
     getUrls(state),
-    url,
-    ""
+    url
   );
 }
 
 export function getSpecificSourceByURL(
   state: OuterState,
   url: string,
   isOriginal: boolean
 ): ?Source {
@@ -420,48 +490,42 @@ export function getPrettySource(state: O
 export function hasPrettySource(state: OuterState, id: string) {
   return !!getPrettySource(state, id);
 }
 
 function getSourceHelper(
   original: boolean,
   sources: SourcesMap,
   urls: UrlsMap,
-  url: string,
-  thread: string = ""
+  url: string
 ) {
   const foundSources = getSourcesByUrlInSources(sources, urls, url);
   if (!foundSources) {
     return null;
   }
 
-  return foundSources.find(
-    source =>
-      isOriginalSource(source) == original &&
-      (!thread || source.thread == thread)
-  );
+  return foundSources.find(source => isOriginalSource(source) == original);
 }
 
 export const getOriginalSourceByUrlInSources = getSourceHelper.bind(null, true);
 
 export const getGeneratedSourceByUrlInSources = getSourceHelper.bind(
   null,
   false
 );
 
 export function getSpecificSourceByUrlInSources(
   sources: SourcesMap,
   urls: UrlsMap,
   url: string,
-  isOriginal: boolean,
-  thread: string
+  isOriginal: boolean
 ) {
   return isOriginal
-    ? getOriginalSourceByUrlInSources(sources, urls, url, thread)
-    : getGeneratedSourceByUrlInSources(sources, urls, url, thread);
+    ? getOriginalSourceByUrlInSources(sources, urls, url)
+    : getGeneratedSourceByUrlInSources(sources, urls, url);
 }
 
 export function getSourceByUrlInSources(
   sources: SourcesMap,
   urls: UrlsMap,
   url: string
 ) {
   const foundSources = getSourcesByUrlInSources(sources, urls, url);
@@ -514,31 +578,28 @@ export function getSourceInSources(sourc
 export function getSources(state: OuterState) {
   return state.sources.sources;
 }
 
 export function getUrls(state: OuterState) {
   return state.sources.urls;
 }
 
-export function getSourceList(state: OuterState, thread?: string): Source[] {
-  const sourceList = (Object.values(getSources(state)): any);
-  return !thread
-    ? sourceList
-    : sourceList.filter(source => source.thread == thread);
+export function getSourceList(state: OuterState): Source[] {
+  return (Object.values(getSources(state)): any);
 }
 
 export function getRelativeSourcesList(state: OuterState): Source[] {
   return ((Object.values(getRelativeSources(state)): any).flatMap(
     Object.values
   ): any);
 }
 
-export function getSourceCount(state: OuterState, thread?: string) {
-  return getSourceList(state, thread).length;
+export function getSourceCount(state: OuterState) {
+  return getSourceList(state).length;
 }
 
 export const getSelectedLocation: Selector<?SourceLocation> = createSelector(
   getSourcesState,
   sources => sources.selectedLocation
 );
 
 export const getSelectedSource: Selector<?Source> = createSelector(
@@ -577,16 +638,16 @@ export const getRelativeSources: GetRela
     return relativeSources;
   }
 );
 
 export function getRelativeSourcesForThread(
   state: OuterState,
   thread: string
 ): SourcesMap {
-  return getRelativeSources(state)[thread];
+  return getRelativeSources(state)[thread] || {};
 }
 
 export function getFocusedSourceItem(state: OuterState): ?FocusItem {
   return state.sources.focusedItem;
 }
 
 export default update;
--- a/devtools/client/debugger/new/src/reducers/tabs.js
+++ b/devtools/client/debugger/new/src/reducers/tabs.js
@@ -26,32 +26,22 @@ import type { Action } from "../actions/
 import type { SourcesState } from "./sources";
 import type { Source } from "../types";
 import type { Selector } from "./types";
 
 export type Tab = {
   url: string,
   framework?: string | null,
   isOriginal: boolean,
-  sourceId?: string,
-  thread: string
+  sourceId?: string
 };
 export type TabList = Tab[];
 
-function isSimilarTab(
-  tab: Tab,
-  url: string,
-  isOriginal: boolean,
-  thread: string
-) {
-  return (
-    tab.url === url &&
-    tab.isOriginal === isOriginal &&
-    (!thread || !tab.thread || thread == tab.thread)
-  );
+function isSimilarTab(tab: Tab, url: string, isOriginal: boolean) {
+  return tab.url === url && tab.isOriginal === isOriginal;
 }
 
 function update(state: TabList = [], action: Action): TabList {
   switch (action.type) {
     case "ADD_TAB":
     case "UPDATE_TAB":
       return updateTabList(state, action);
 
@@ -68,20 +58,17 @@ function update(state: TabList = [], act
   }
 }
 
 export function removeSourceFromTabList(
   tabs: TabList,
   source: Source
 ): TabList {
   return tabs.filter(
-    tab =>
-      tab.url !== source.url ||
-      tab.isOriginal != isOriginalId(source.id) ||
-      (tab.thread && tab.thread !== source.thread)
+    tab => tab.url !== source.url || tab.isOriginal != isOriginalId(source.id)
   );
 }
 
 export function removeSourcesFromTabList(tabs: TabList, sources: Source[]) {
   return sources.reduce(
     (t, source) => removeSourceFromTabList(t, source),
     tabs
   );
@@ -89,26 +76,26 @@ export function removeSourcesFromTabList
 
 /**
  * Adds the new source to the tab list if it is not already there
  * @memberof reducers/tabs
  * @static
  */
 function updateTabList(
   tabs: TabList,
-  { url, framework = null, sourceId, isOriginal = false, thread = "" }
+  { url, framework = null, sourceId, isOriginal = false }
 ) {
   // Set currentIndex to -1 for URL-less tabs so that they aren't
   // filtered by isSimilarTab
   const currentIndex = url
-    ? tabs.findIndex(tab => isSimilarTab(tab, url, isOriginal, thread))
+    ? tabs.findIndex(tab => isSimilarTab(tab, url, isOriginal))
     : -1;
 
   if (currentIndex === -1) {
-    tabs = [{ url, framework, sourceId, isOriginal, thread }, ...tabs];
+    tabs = [{ url, framework, sourceId, isOriginal }, ...tabs];
   } else if (framework) {
     tabs[currentIndex].framework = framework;
   }
 
   asyncStore.tabs = persistTabs(tabs);
   return tabs;
 }
 
@@ -146,22 +133,17 @@ export function getNewSelectedSourceId(
   }
 
   const selectedTab = getSource(state, selectedLocation.sourceId);
   if (!selectedTab) {
     return "";
   }
 
   const matchingTab = availableTabs.find(tab =>
-    isSimilarTab(
-      tab,
-      selectedTab.url,
-      isOriginalId(selectedLocation.sourceId),
-      selectedTab.thread
-    )
+    isSimilarTab(tab, selectedTab.url, isOriginalId(selectedLocation.sourceId))
   );
 
   if (matchingTab) {
     const sources = state.sources.sources;
     if (!sources) {
       return "";
     }
 
@@ -184,18 +166,17 @@ export function getNewSelectedSourceId(
   const newSelectedTabIndex = Math.min(leftNeighborIndex, lastAvailbleTabIndex);
   const availableTab = availableTabs[newSelectedTabIndex];
 
   if (availableTab) {
     const tabSource = getSpecificSourceByUrlInSources(
       getSources(state),
       getUrls(state),
       availableTab.url,
-      availableTab.isOriginal,
-      availableTab.thread
+      availableTab.isOriginal
     );
 
     if (tabSource) {
       return tabSource.id;
     }
   }
 
   return "";
@@ -231,17 +212,16 @@ export const getSourcesForTabs: Selector
 );
 
 function getTabWithOrWithoutUrl(tab, sources, urls) {
   if (tab.url) {
     return getSpecificSourceByUrlInSources(
       sources,
       urls,
       tab.url,
-      tab.isOriginal,
-      tab.thread
+      tab.isOriginal
     );
   }
 
   return tab.sourceId ? sources[tab.sourceId] : null;
 }
 
 export default update;
--- a/devtools/client/debugger/new/src/reducers/tests/sources.spec.js
+++ b/devtools/client/debugger/new/src/reducers/tests/sources.spec.js
@@ -11,38 +11,45 @@ import update, { initialSourcesState, ge
 import { foobar } from "../../test/fixtures";
 import type { Source } from "../../types";
 import { prefs } from "../../utils/prefs";
 
 const fakeSources = foobar.sources.sources;
 
 const extenstionSource = {
   id: "extenstionId",
-  url: "http://example.com/script.js",
-  thread: "foo"
+  url: "http://example.com/script.js"
 };
 
 const firefoxExtensionSource = {
   id: "firefoxExtension",
-  url: "moz-extension://id/js/content.js",
-  thread: "foo"
+  url: "moz-extension://id/js/content.js"
 };
 
 const chromeExtensionSource = {
   id: "chromeExtension",
-  url: "chrome-extension://id/js/content.js",
-  thread: "foo"
+  url: "chrome-extension://id/js/content.js"
 };
 
 const mockedSources = [
   extenstionSource,
   firefoxExtensionSource,
   chromeExtensionSource
 ];
 
+const mockedSourceActors = [
+  { actor: "extensionId-actor", source: "extenstionId", thread: "foo" },
+  {
+    actor: "firefoxExtension-actor",
+    source: "firefoxExtension",
+    thread: "foo"
+  },
+  { actor: "chromeExtension-actor", source: "chromeExtension", thread: "foo" }
+];
+
 describe("sources reducer", () => {
   it("should work", () => {
     let state = initialSourcesState();
     state = update(state, {
       type: "ADD_SOURCE",
       // coercing to a Source for the purpose of this test
       source: ((fakeSources.fooSourceActor: any): Source)
     });
@@ -53,31 +60,33 @@ describe("sources reducer", () => {
 describe("sources selectors", () => {
   it("should return all extensions when chrome preference enabled", () => {
     prefs.chromeAndExtenstionsEnabled = true;
     let state = initialSourcesState();
     state = {
       sources: update(state, {
         type: "ADD_SOURCES",
         // coercing to a Source for the purpose of this test
-        sources: ((mockedSources: any): Source[])
+        sources: ((mockedSources: any): Source[]),
+        sourceActors: mockedSourceActors
       })
     };
     const selectedRelativeSources = getRelativeSources(state);
     const threadSources = selectedRelativeSources.foo;
     expect(Object.values(threadSources)).toHaveLength(3);
   });
 
   it("should omit all extensions when chrome preference enabled", () => {
     prefs.chromeAndExtenstionsEnabled = false;
     let state = initialSourcesState();
     state = {
       sources: update(state, {
         type: "ADD_SOURCES",
         // coercing to a Source for the purpose of this test
-        sources: ((mockedSources: any): Source[])
+        sources: ((mockedSources: any): Source[]),
+        sourceActors: mockedSourceActors
       })
     };
     const selectedRelativeSources = getRelativeSources(state);
     const threadSources = selectedRelativeSources.foo;
     expect(Object.values(threadSources)).toHaveLength(1);
   });
 });
--- a/devtools/client/debugger/new/src/selectors/test/__snapshots__/visibleColumnBreakpoints.spec.js.snap
+++ b/devtools/client/debugger/new/src/selectors/test/__snapshots__/visibleColumnBreakpoints.spec.js.snap
@@ -1,19 +1,31 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
 exports[`visible column breakpoints duplicate generated locations 1`] = `
 Array [
   Object {
     "breakpoint": Object {
+      "astLocation": null,
+      "disabled": false,
+      "generatedLocation": Object {
+        "column": 1,
+        "line": 1,
+        "sourceId": "foo",
+      },
+      "id": "breakpoint",
+      "loading": false,
       "location": Object {
         "column": 1,
         "line": 1,
         "sourceId": "foo",
       },
+      "options": Object {},
+      "originalText": "text",
+      "text": "text",
     },
     "location": Object {
       "column": 1,
       "line": 1,
       "sourceId": "foo",
     },
   },
   Object {
@@ -26,21 +38,33 @@ Array [
   },
 ]
 `;
 
 exports[`visible column breakpoints ignores single breakpoints 1`] = `
 Array [
   Object {
     "breakpoint": Object {
+      "astLocation": null,
+      "disabled": false,
+      "generatedLocation": Object {
+        "column": 1,
+        "line": 1,
+        "sourceId": "foo",
+      },
+      "id": "breakpoint",
+      "loading": false,
       "location": Object {
         "column": 1,
         "line": 1,
         "sourceId": "foo",
       },
+      "options": Object {},
+      "originalText": "text",
+      "text": "text",
     },
     "location": Object {
       "column": 1,
       "line": 1,
       "sourceId": "foo",
     },
   },
   Object {
@@ -53,21 +77,33 @@ Array [
   },
 ]
 `;
 
 exports[`visible column breakpoints only shows visible breakpoints 1`] = `
 Array [
   Object {
     "breakpoint": Object {
+      "astLocation": null,
+      "disabled": false,
+      "generatedLocation": Object {
+        "column": 1,
+        "line": 1,
+        "sourceId": "foo",
+      },
+      "id": "breakpoint",
+      "loading": false,
       "location": Object {
         "column": 1,
         "line": 1,
         "sourceId": "foo",
       },
+      "options": Object {},
+      "originalText": "text",
+      "text": "text",
     },
     "location": Object {
       "column": 1,
       "line": 1,
       "sourceId": "foo",
     },
   },
   Object {
@@ -80,21 +116,33 @@ Array [
   },
 ]
 `;
 
 exports[`visible column breakpoints simple 1`] = `
 Array [
   Object {
     "breakpoint": Object {
+      "astLocation": null,
+      "disabled": false,
+      "generatedLocation": Object {
+        "column": 1,
+        "line": 1,
+        "sourceId": "foo",
+      },
+      "id": "breakpoint",
+      "loading": false,
       "location": Object {
         "column": 1,
         "line": 1,
         "sourceId": "foo",
       },
+      "options": Object {},
+      "originalText": "text",
+      "text": "text",
     },
     "location": Object {
       "column": 1,
       "line": 1,
       "sourceId": "foo",
     },
   },
   Object {
--- a/devtools/client/debugger/new/src/selectors/test/getCallStackFrames.spec.js
+++ b/devtools/client/debugger/new/src/selectors/test/getCallStackFrames.spec.js
@@ -1,12 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
+// @flow
+
 import { getCallStackFrames } from "../getCallStackFrames";
 import { pullAt } from "lodash";
 
 describe("getCallStackFrames selector", () => {
   describe("library annotation", () => {
     it("annotates React frames", () => {
       const state = {
         frames: [
--- a/devtools/client/debugger/new/src/selectors/test/visibleColumnBreakpoints.spec.js
+++ b/devtools/client/debugger/new/src/selectors/test/visibleColumnBreakpoints.spec.js
@@ -1,87 +1,94 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
+// @flow
+
 import { getColumnBreakpoints } from "../visibleColumnBreakpoints";
+import { makeMockSource, makeMockBreakpoint } from "../../utils/test-mockup";
 
 function pp(line, column) {
   return {
-    location: { line, column },
-    generatedLocation: { line, column },
-    types: { break: true }
+    location: { sourceId: "", line, column },
+    generatedLocation: { sourceId: "", line, column },
+    types: { break: true, step: false }
   };
 }
 
+function defaultSource() {
+  return makeMockSource(undefined, "foo");
+}
+
 function bp(line, column) {
-  return { location: { line, column, sourceId: "foo" } };
+  return makeMockBreakpoint(defaultSource(), line, column);
 }
 
 describe("visible column breakpoints", () => {
   it("simple", () => {
     const viewport = {
       start: { line: 1, column: 0 },
       end: { line: 10, column: 10 }
     };
     const pausePoints = [pp(1, 1), pp(1, 5), pp(3, 1)];
     const breakpoints = [bp(1, 1), bp(4, 0), bp(4, 3)];
 
-    const selectedSource = { id: "foo" };
+    const selectedSource = defaultSource();
     const columnBps = getColumnBreakpoints(
       pausePoints,
       breakpoints,
       viewport,
       selectedSource
     );
     expect(columnBps).toMatchSnapshot();
   });
 
   it("duplicate generated locations", () => {
     const viewport = {
       start: { line: 1, column: 0 },
       end: { line: 10, column: 10 }
     };
     const pausePoints = [pp(1, 1), pp(1, 1), pp(1, 3)];
     const breakpoints = [bp(1, 1)];
-    const selectedSource = { id: "foo" };
+    const selectedSource = defaultSource();
     const columnBps = getColumnBreakpoints(
       pausePoints,
       breakpoints,
       viewport,
       selectedSource
     );
     expect(columnBps).toMatchSnapshot();
   });
 
   it("ignores single breakpoints", () => {
     const viewport = {
       start: { line: 1, column: 0 },
       end: { line: 10, column: 10 }
     };
-    const pausePoints = [pp(1, 1), pp(1, 3, pp(2, 1))];
+    const pausePoints = [pp(1, 1), pp(1, 3), pp(2, 1)];
     const breakpoints = [bp(1, 1)];
-    const selectedSource = { id: "foo" };
+    const selectedSource = defaultSource();
     const columnBps = getColumnBreakpoints(
       pausePoints,
       breakpoints,
       viewport,
       selectedSource
     );
     expect(columnBps).toMatchSnapshot();
   });
 
   it("only shows visible breakpoints", () => {
     const viewport = {
       start: { line: 1, column: 0 },
       end: { line: 10, column: 10 }
     };
     const pausePoints = [pp(1, 1), pp(1, 3), pp(20, 1)];
     const breakpoints = [bp(1, 1)];
-    const selectedSource = { id: "foo" };
+    const selectedSource = defaultSource();
 
     const columnBps = getColumnBreakpoints(
       pausePoints,
       breakpoints,
       viewport,
       selectedSource
     );
     expect(columnBps).toMatchSnapshot();
--- a/devtools/client/debugger/new/src/selectors/visibleColumnBreakpoints.js
+++ b/devtools/client/debugger/new/src/selectors/visibleColumnBreakpoints.js
@@ -5,17 +5,17 @@
 // @flow
 
 import { groupBy, sortedUniqBy } from "lodash";
 import { createSelector } from "reselect";
 
 import { getViewport, getSelectedSource } from "../selectors";
 import { getVisibleBreakpoints } from "./visibleBreakpoints";
 import { getVisiblePausePoints } from "./visiblePausePoints";
-import { makeLocationId } from "../utils/breakpoint";
+import { makeBreakpointId } from "../utils/breakpoint";
 import type { Selector, PausePoint } from "../reducers/types";
 
 import type {
   SourceLocation,
   PartialPosition,
   Breakpoint,
   Range,
   Source
@@ -113,17 +113,17 @@ export function getColumnBreakpoints(
       breakpointMap[location.line] &&
       // 3. check that the breakpoint is visible
       viewport &&
       contains(location, viewport)
   );
 
   // 4. Only show one column breakpoint per generated location
   columnBreakpoints = sortedUniqBy(columnBreakpoints, ({ generatedLocation }) =>
-    makeLocationId(generatedLocation)
+    makeBreakpointId(generatedLocation)
   );
 
   // 5. Check that there is atleast one other possible breakpoint on the line
   const lineCount = getLineCount(columnBreakpoints);
   columnBreakpoints = columnBreakpoints.filter(
     ({ location: { line } }) => lineCount[line] > 1
   );
 
--- a/devtools/client/debugger/new/src/types.js
+++ b/devtools/client/debugger/new/src/types.js
@@ -16,16 +16,18 @@ export type Mode =
       name: string,
       typescript?: boolean,
       base?: {
         name: string,
         typescript: boolean
       }
     };
 
+export type ThreadId = string;
+
 /**
  * Breakpoint ID
  *
  * @memberof types
  * @static
  */
 export type BreakpointId = string;
 
@@ -40,16 +42,22 @@ export type SourceId = string;
 /**
  * Actor ID
  *
  * @memberof types
  * @static
  */
 export type ActorId = string;
 
+export type SourceActorLocation = {|
+  +sourceActor: SourceActor,
+  +line: number,
+  +column?: number
+|};
+
 /**
  * Source File Location
  *
  * @memberof types
  * @static
  */
 export type SourceLocation = {|
   +sourceId: SourceId,
@@ -84,39 +92,47 @@ export type PendingLocation = {
 
 export type ASTLocation = {|
   +name: ?string,
   +offset: PartialPosition,
   +index: number
 |};
 
 /**
- * Breakpoint
+ * Breakpoint is associated with a Source.
  *
  * @memberof types
  * @static
  */
 export type Breakpoint = {|
   +id: BreakpointId,
   +location: SourceLocation,
   +astLocation: ?ASTLocation,
   +generatedLocation: SourceLocation,
   +loading: boolean,
   +disabled: boolean,
   +text: string,
   +originalText: string,
   +options: BreakpointOptions
 |};
 
+/**
+ * Options for a breakpoint that can be modified by the user.
+ */
 export type BreakpointOptions = {
   hidden?: boolean,
   condition?: string,
   logValue?: string
 };
 
+export type BreakpointActor = {|
+  +actor: ActorId,
+  +source: SourceActor
+|};
+
 /**
  * XHR Breakpoint
  * @memberof types
  * @static
  */
 export type XHRBreakpoint = {|
   +path: string,
   +method: "GET" | "POST" | "DELETE" | "ANY",
@@ -128,17 +144,17 @@ export type XHRBreakpoint = {|
 /**
  * Breakpoint Result is the return from an add/modify Breakpoint request
  *
  * @memberof types
  * @static
  */
 export type BreakpointResult = {
   id: ActorId,
-  actualLocation: SourceLocation
+  actualLocation: SourceActorLocation
 };
 
 /**
  * PendingBreakpoint
  *
  * @memberof types
  * @static
  */
@@ -162,16 +178,17 @@ export type FrameId = string;
 
 /**
  * Frame
  * @memberof types
  * @static
  */
 export type Frame = {
   id: FrameId,
+  thread: string,
   displayName: string,
   location: SourceLocation,
   generatedLocation: SourceLocation,
   source?: Source,
   scope: Scope,
   // FIXME Define this type more clearly
   this: Object,
   framework?: string,
@@ -301,19 +318,18 @@ export type Grip = {
 /**
  * BaseSource
  *
  * @memberof types
  * @static
  */
 
 type BaseSource = {|
-  +id: string,
+  +id: SourceId,
   +url: string,
-  +thread: string,
   +sourceMapURL?: string,
   +isBlackBoxed: boolean,
   +isPrettyPrinted: boolean,
   +contentType?: string,
   +error?: string,
   +loadedState: "unloaded" | "loading" | "loaded",
   +relativeUrl: string,
   introductionUrl: ?string
@@ -342,16 +358,22 @@ export type JsSource = {|
 export type WasmSource = {|
   ...BaseSource,
   +isWasm: true,
   +text?: {| binary: Object |}
 |};
 
 export type Source = JsSource | WasmSource;
 
+export type SourceActor = {|
+  +actor: ActorId,
+  +source: SourceId,
+  +thread: ThreadId
+|};
+
 /**
  * Script
  * This describes scripts which are sent to the debug server to be eval'd
  * @memberof types
  * @static
  * FIXME: This needs a real type definition
  */
 export type Script = any;
--- a/devtools/client/debugger/new/src/utils/bootstrap.js
+++ b/devtools/client/debugger/new/src/utils/bootstrap.js
@@ -16,16 +16,18 @@ import * as prettyPrint from "../workers
 import * as parser from "../workers/parser";
 
 import configureStore from "../actions/utils/create-store";
 import reducers from "../reducers";
 import * as selectors from "../selectors";
 import App from "../components/App";
 import { asyncStore, prefs } from "./prefs";
 
+import type { Panel } from "../client/firefox/types";
+
 function renderPanel(component, store) {
   const root = document.createElement("div");
   root.className = "launchpad-root theme-body";
   root.style.setProperty("flex", "1");
   const mount = document.querySelector("#mount");
   if (!mount) {
     return;
   }
@@ -34,24 +36,25 @@ function renderPanel(component, store) {
   ReactDOM.render(
     React.createElement(Provider, { store }, React.createElement(component)),
     root
   );
 }
 
 export function bootstrapStore(
   client: any,
-  { services, toolboxActions }: any,
+  sourceMaps: Object,
+  panel: Panel,
   initialState: Object
 ) {
   const createStore = configureStore({
     log: prefs.logging || isTesting(),
     timing: isDevelopment(),
     makeThunkArgs: (args, state) => {
-      return { ...args, client, ...services, ...toolboxActions };
+      return { ...args, client, sourceMaps, panel };
     }
   });
 
   const store = createStore(combineReducers(reducers), initialState);
   store.subscribe(() => updatePrefs(store.getState()));
 
   const actions = bindActionCreators(
     require("../actions").default,
--- a/devtools/client/debugger/new/src/utils/breakpoint/index.js
+++ b/devtools/client/debugger/new/src/utils/breakpoint/index.js
@@ -12,17 +12,19 @@ import { isGenerated } from "../source";
 import assert from "../assert";
 import { features } from "../prefs";
 import { getSelectedLocation } from "../source-maps";
 
 export { getASTLocation, findScopeByName } from "./astBreakpointLocation";
 
 import type {
   Source,
+  SourceActor,
   SourceLocation,
+  SourceActorLocation,
   PendingLocation,
   Breakpoint,
   PendingBreakpoint
 } from "../../types";
 
 import type { State } from "../../reducers/types";
 
 // Return the first argument that is a string, or null if nothing is a
@@ -40,17 +42,18 @@ export function locationMoved(
   location: SourceLocation,
   newLocation: SourceLocation
 ) {
   return (
     location.line !== newLocation.line || location.column !== newLocation.column
   );
 }
 
-export function makeLocationId(location: SourceLocation) {
+// The ID for a Breakpoint is derived from its location in its Source.
+export function makeBreakpointId(location: SourceLocation) {
   const { sourceId, line, column } = location;
   const columnString = column || "";
   return `${sourceId}:${line}:${columnString}`;
 }
 
 export function getLocationWithoutColumn(location: SourceLocation) {
   const { sourceId, line } = location;
   return `${sourceId}:${line}`;
@@ -60,16 +63,34 @@ export function makePendingLocationId(lo
   assertPendingLocation(location);
   const { sourceUrl, line, column } = location;
   const sourceUrlString = sourceUrl || "";
   const columnString = column || "";
 
   return `${sourceUrlString}:${line}:${columnString}`;
 }
 
+export function makeSourceActorLocation(
+  sourceActor: SourceActor,
+  location: SourceLocation
+) {
+  return {
+    sourceActor,
+    line: location.line,
+    column: location.column
+  };
+}
+
+// The ID for a BreakpointActor is derived from its location in its SourceActor.
+export function makeBreakpointActorId(location: SourceActorLocation) {
+  const { sourceActor, line, column } = location;
+  const columnString = column || "";
+  return `${sourceActor.actor}:${line}:${columnString}`;
+}
+
 export function assertBreakpoint(breakpoint: Breakpoint) {
   assertLocation(breakpoint.location);
   assertLocation(breakpoint.generatedLocation);
 }
 
 export function assertPendingBreakpoint(pendingBreakpoint: PendingBreakpoint) {
   assertPendingLocation(pendingBreakpoint.location);
   assertPendingLocation(pendingBreakpoint.generatedLocation);
@@ -126,29 +147,28 @@ export function createBreakpoint(
   overrides: Object = {}
 ): Breakpoint {
   const {
     condition,
     disabled,
     hidden,
     generatedLocation,
     astLocation,
-    id,
     text,
     originalText,
     logValue
   } = overrides;
 
   const defaultASTLocation = {
     name: undefined,
     offset: location,
     index: 0
   };
   const properties = {
-    id,
+    id: makeBreakpointId(location),
     options: {
       condition: condition || null,
       logValue: logValue || null,
       hidden: hidden || false
     },
     disabled: disabled || false,
     loading: false,
     astLocation: astLocation || defaultASTLocation,
--- a/devtools/client/debugger/new/src/utils/source-queue.js
+++ b/devtools/client/debugger/new/src/utils/source-queue.js
@@ -1,16 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
 import { throttle } from "lodash";
-import type { Source } from "../types";
+import type { CreateSourceResult } from "../client/firefox/types";
 
 let newSources;
 let queuedSources;
 let currentWork;
 
 async function dispatchNewSources() {
   const sources = queuedSources;
   queuedSources = [];
@@ -19,20 +19,20 @@ async function dispatchNewSources() {
 
 const queue = throttle(dispatchNewSources, 100);
 
 export default {
   initialize: (actions: Object) => {
     newSources = actions.newSources;
     queuedSources = [];
   },
-  queue: (source: Source) => {
+  queue: (source: CreateSourceResult) => {
     queuedSources.push(source);
     queue();
   },
-  queueSources: (sources: Source[]) => {
+  queueSources: (sources: CreateSourceResult[]) => {
     queuedSources = queuedSources.concat(sources);
     queue();
   },
 
   flush: () => Promise.all([queue.flush(), currentWork]),
   clear: () => queue.cancel()
 };
--- a/devtools/client/debugger/new/src/utils/sources-tree/addToTree.js
+++ b/devtools/client/debugger/new/src/utils/sources-tree/addToTree.js
@@ -77,17 +77,16 @@ function findOrCreateNode(
 }
 
 /*
  * walk the source tree to the final node for a given url,
  * adding new nodes along the way
  */
 function traverseTree(
   url: ParsedURL,
-  thread: string,
   tree: TreeDirectory,
   debuggeeHost: ?string
 ): TreeNode {
   const parts = url.path.split("/").filter(p => p !== "");
   parts.unshift(url.group);
 
   let path = "";
   return parts.reduce((subTree, part, index) => {
@@ -162,13 +161,13 @@ export function addToTree(
   projectRoot: string
 ) {
   const url = getURL(source, debuggeeHost);
 
   if (isInvalidUrl(url, source)) {
     return;
   }
 
-  const finalNode = traverseTree(url, source.thread, tree, debuggeeHost);
+  const finalNode = traverseTree(url, tree, debuggeeHost);
 
   // $FlowIgnore
   finalNode.contents = addSourceToNode(finalNode, url, source);
 }
--- a/devtools/client/debugger/new/src/utils/test-head.js
+++ b/devtools/client/debugger/new/src/utils/test-head.js
@@ -59,32 +59,43 @@ function makeFrame({ id, sourceId }: Obj
   return {
     id,
     scope: { bindings: { variables: {}, arguments: [] } },
     location: { sourceId, line: 4 },
     ...opts
   };
 }
 
-/**
- * @memberof utils/test-head
- * @static
- */
-function makeSource(name: string, props: any = {}) {
+function makeSourceRaw(name: string, props: any = {}) {
   return {
     id: name,
     loadedState: "unloaded",
     url: `http://localhost:8000/examples/${name}`,
     ...props
   };
 }
 
+/**
+ * @memberof utils/test-head
+ * @static
+ */
+function makeSource(name: string, props: any = {}) {
+  return {
+    source: makeSourceRaw(name, props),
+    sourceActor: {
+      actor: `${name}-actor`,
+      source: name,
+      thread: "FakeThread"
+    }
+  };
+}
+
 function makeOriginalSource(name: string, props?: Object) {
-  const source = makeSource(name, props);
-  return { ...source, id: `${name}/originalSource` };
+  const source = makeSourceRaw(name, props);
+  return { source: { ...source, id: `${name}/originalSource` } };
 }
 
 function makeFuncLocation(startLine, endLine) {
   if (!endLine) {
     endLine = startLine + 1;
   }
   return {
     start: {
--- a/devtools/client/debugger/new/src/utils/test-mockup.js
+++ b/devtools/client/debugger/new/src/utils/test-mockup.js
@@ -29,33 +29,31 @@ function makeMockSource(
   url: string = "url",
   id: SourceId = "source",
   contentType: string = "text/javascript",
   text: string = ""
 ): JsSource {
   return {
     id,
     url,
-    thread: "FakeThread",
     isBlackBoxed: false,
     isPrettyPrinted: false,
     loadedState: text ? "loaded" : "unloaded",
     relativeUrl: url,
     introductionUrl: null,
     isWasm: false,
     contentType,
     text
   };
 }
 
 function makeMockWasmSource(text: {| binary: Object |}): WasmSource {
   return {
     id: "wasm-source-id",
     url: "url",
-    thread: "FakeThread",
     isBlackBoxed: false,
     isPrettyPrinted: false,
     loadedState: "unloaded",
     relativeUrl: "url",
     introductionUrl: null,
     isWasm: true,
     text
   };
--- a/devtools/client/debugger/new/src/workers/parser/mapAwaitExpression.js
+++ b/devtools/client/debugger/new/src/workers/parser/mapAwaitExpression.js
@@ -2,42 +2,180 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
 import generate from "@babel/generator";
 import * as t from "@babel/types";
 
-import { hasNode } from "./utils/ast";
+import { hasNode, replaceNode } from "./utils/ast";
 import { isTopLevel } from "./utils/helpers";
 
 function hasTopLevelAwait(ast: Object): boolean {
   const hasAwait = hasNode(
     ast,
     (node, ancestors, b) => t.isAwaitExpression(node) && isTopLevel(ancestors)
   );
 
   return hasAwait;
 }
 
-function wrapExpressionFromAst(ast): string {
+// translates new bindings `var a = 3` into `a = 3`.
+function translateDeclarationIntoAssignment(node: Object): Object[] {
+  return node.declarations.reduce((acc, declaration) => {
+    // Don't translate declaration without initial assignment (e.g. `var a;`)
+    if (!declaration.init) {
+      return acc;
+    }
+    acc.push(
+      t.expressionStatement(
+        t.assignmentExpression("=", declaration.id, declaration.init)
+      )
+    );
+    return acc;
+  }, []);
+}
+
+/**
+ * Given an AST, compute its last statement and replace it with a
+ * return statement.
+ */
+function addReturnNode(ast: Object): Object {
   const statements = ast.program.body;
   const lastStatement = statements[statements.length - 1];
-  const body = statements
+  return statements
     .slice(0, -1)
     .concat(t.returnStatement(lastStatement.expression));
+}
 
-  const newAst = t.expressionStatement(
+function getDeclarations(node: Object) {
+  const { kind, declarations } = node;
+  const declaratorNodes = declarations.reduce((acc, d) => {
+    const declarators = getVariableDeclarators(d.id);
+    return acc.concat(declarators);
+  }, []);
+
+  // We can't declare const variables outside of the async iife because we
+  // wouldn't be able to re-assign them. As a workaround, we transform them
+  // to `let` which should be good enough for those case.
+  return t.variableDeclaration(
+    kind === "const" ? "let" : kind,
+    declaratorNodes
+  );
+}
+
+function getVariableDeclarators(node: Object): Object[] | Object {
+  if (t.isIdentifier(node)) {
+    return t.variableDeclarator(t.identifier(node.name));
+  }
+
+  if (t.isObjectProperty(node)) {
+    return getVariableDeclarators(node.value);
+  }
+  if (t.isRestElement(node)) {
+    return getVariableDeclarators(node.argument);
+  }
+
+  if (t.isAssignmentPattern(node)) {
+    return getVariableDeclarators(node.left);
+  }
+
+  if (t.isArrayPattern(node)) {
+    return node.elements.reduce(
+      (acc, element) => acc.concat(getVariableDeclarators(element)),
+      []
+    );
+  }
+  if (t.isObjectPattern(node)) {
+    return node.properties.reduce(
+      (acc, property) => acc.concat(getVariableDeclarators(property)),
+      []
+    );
+  }
+  return [];
+}
+
+/**
+ * Given an AST and an array of variableDeclaration nodes, return a new AST with
+ * all the declarations at the top of the AST.
+ */
+function addTopDeclarationNodes(ast: Object, declarationNodes: Object[]) {
+  const statements = [];
+  declarationNodes.forEach(declarationNode => {
+    statements.push(getDeclarations(declarationNode));
+  });
+  statements.push(ast);
+  return t.program(statements);
+}
+
+/**
+ * Given an AST, return an object of the following shape:
+ *   - newAst: {AST} the AST where variable declarations were transformed into
+ *             variable assignments
+ *   - declarations: {Array<Node>} An array of all the declaration nodes needed
+ *                   outside of the async iife.
+ */
+function translateDeclarationsIntoAssignment(
+  ast: Object
+): { newAst: Object, declarations: Node[] } {
+  const declarations = [];
+  t.traverse(ast, (node, ancestors) => {
+    const parent = ancestors[ancestors.length - 1];
+
+    if (
+      t.isWithStatement(node) ||
+      !isTopLevel(ancestors) ||
+      t.isAssignmentExpression(node) ||
+      !t.isVariableDeclaration(node) ||
+      t.isForStatement(parent.node) ||
+      !Array.isArray(node.declarations) ||
+      node.declarations.length === 0
+    ) {
+      return;
+    }
+
+    const newNodes = translateDeclarationIntoAssignment(node);
+    replaceNode(ancestors, newNodes);
+    declarations.push(node);
+  });
+
+  return {
+    newAst: ast,
+    declarations
+  };
+}
+
+/**
+ * Given an AST, wrap its body in an async iife, transform variable declarations
+ * in assignments and move the variable declarations outside of the async iife.
+ * Example: With the AST for the following expression: `let a = await 123`, the
+ * function will return:
+ * let a;
+ * (async => {
+ *   return a = await 123;
+ * })();
+ */
+function wrapExpressionFromAst(ast: Object): string {
+  // Transform let and var declarations into assignments, and get back an array
+  // of variable declarations.
+  let { newAst, declarations } = translateDeclarationsIntoAssignment(ast);
+  const body = addReturnNode(newAst);
+
+  // Create the async iife.
+  newAst = t.expressionStatement(
     t.callExpression(
       t.arrowFunctionExpression([], t.blockStatement(body), true),
       []
     )
   );
 
+  // Now let's put all the variable declarations at the top of the async iife.
+  newAst = addTopDeclarationNodes(newAst, declarations);
+
   return generate(newAst).code;
 }
 
 export default function mapTopLevelAwait(
   expression: string,
   ast?: Object
 ): string {
   if (!ast) {
--- a/devtools/client/debugger/new/src/workers/parser/mapBindings.js
+++ b/devtools/client/debugger/new/src/workers/parser/mapBindings.js
@@ -1,14 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
+import { replaceNode } from "./utils/ast";
 import { isTopLevel } from "./utils/helpers";
 
 import generate from "@babel/generator";
 import * as t from "@babel/types";
 
 function getAssignmentTarget(node, bindings) {
   if (t.isObjectPattern(node)) {
     for (const property of node.properties) {
@@ -70,30 +71,16 @@ function globalizeDeclaration(node, bind
 function globalizeAssignment(node, bindings) {
   return t.assignmentExpression(
     node.operator,
     getAssignmentTarget(node.left, bindings),
     node.right
   );
 }
 
-function replaceNode(ancestors, node) {
-  const parent = ancestors[ancestors.length - 1];
-
-  if (typeof parent.index === "number") {
-    if (Array.isArray(node)) {
-      parent.node[parent.key].splice(parent.index, 1, ...node);
-    } else {
-      parent.node[parent.key][parent.index] = node;
-    }
-  } else {
-    parent.node[parent.key] = node;
-  }
-}
-
 export default function mapExpressionBindings(
   expression: string,
   ast?: Object,
   bindings: string[] = []
 ): string {
   let isMapped = false;
   let shouldUpdate = true;
 
--- a/devtools/client/debugger/new/src/workers/parser/tests/contains.spec.js
+++ b/devtools/client/debugger/new/src/workers/parser/tests/contains.spec.js
@@ -1,12 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
+// @flow
+
 import {
   containsPosition,
   containsLocation,
   nodeContainsPosition
 } from "../utils/contains";
 
 function getTestLoc() {
   return {
@@ -16,16 +18,24 @@ function getTestLoc() {
     },
     end: {
       line: 12,
       column: 10
     }
   };
 }
 
+// AstPosition.column is typed as a number, but many parts of this test set it
+// to undefined. Using zero instead causes test failures, and allowing it to be
+// undefined causes many flow errors in code manipulating AstPosition.
+// Fake a coercion of undefined to number as a workaround for now.
+function undefinedColumn(): number {
+  return (undefined: any);
+}
+
 function startPos(lineOffset, columnOffset) {
   const { start } = getTestLoc();
   return {
     line: start.line + lineOffset,
     column: start.column + columnOffset
   };
 }
 
@@ -35,24 +45,26 @@ function endPos(lineOffset, columnOffset
     line: end.line + lineOffset,
     column: end.column + columnOffset
   };
 }
 
 function startLine(lineOffset = 0) {
   const { start } = getTestLoc();
   return {
-    line: start.line + lineOffset
+    line: start.line + lineOffset,
+    column: undefinedColumn()
   };
 }
 
 function endLine(lineOffset = 0) {
   const { end } = getTestLoc();
   return {
-    line: end.line + lineOffset
+    line: end.line + lineOffset,
+    column: undefinedColumn()
   };
 }
 
 function testContains(pos, bool) {
   const loc = getTestLoc();
   expect(containsPosition(loc, pos)).toEqual(bool);
 }
 
@@ -92,46 +104,46 @@ describe("containsPosition", () => {
 
     it("should contain position on the same end line", () =>
       testContains(endLine(0), true));
   });
 
   describe("location without the column criterion", () => {
     it("should contain position on the same start line", () => {
       const loc = getTestLoc();
-      loc.start.column = undefined;
+      loc.start.column = undefinedColumn();
       const pos = {
         line: loc.start.line,
         column: 1
       };
       expect(containsPosition(loc, pos)).toEqual(true);
     });
 
     it("should contain position on the same end line", () => {
       const loc = getTestLoc();
-      loc.end.column = undefined;
+      loc.end.column = undefinedColumn();
       const pos = {
         line: loc.end.line,
         column: 1
       };
       expect(containsPosition(loc, pos)).toEqual(true);
     });
   });
 
   describe("location and postion both without the column criterion", () => {
     it("should contain position on the same start line", () => {
       const loc = getTestLoc();
-      loc.start.column = undefined;
+      loc.start.column = undefinedColumn();
       const pos = startLine();
       expect(containsPosition(loc, pos)).toEqual(true);
     });
 
     it("should contain position on the same end line", () => {
       const loc = getTestLoc();
-      loc.end.column = undefined;
+      loc.end.column = undefinedColumn();
       const pos = endLine();
       expect(containsPosition(loc, pos)).toEqual(true);
     });
   });
 });
 
 describe("containsLocation", () => {
   describe("locations both with the column criteria", () => {
@@ -191,59 +203,59 @@ describe("containsLocation", () => {
       };
       expect(containsLocation(locA, locB)).toEqual(true);
     });
   });
 
   describe("location A without the column criterion", () => {
     it("should contain location on the same start line", () => {
       const locA = getTestLoc();
-      locA.start.column = undefined;
+      locA.start.column = undefinedColumn();
       const locB = getTestLoc();
       expect(containsLocation(locA, locB)).toEqual(true);
     });
 
     it("should contain location on the same end line", () => {
       const locA = getTestLoc();
-      locA.end.column = undefined;
+      locA.end.column = undefinedColumn();
       const locB = getTestLoc();
       expect(containsLocation(locA, locB)).toEqual(true);
     });
   });
 
   describe("location B without the column criterion", () => {
     it("should contain location on the same start line", () => {
       const locA = getTestLoc();
       const locB = getTestLoc();
-      locB.start.column = undefined;
+      locB.start.column = undefinedColumn();
       expect(containsLocation(locA, locB)).toEqual(true);
     });
 
     it("should contain location on the same end line", () => {
       const locA = getTestLoc();
       const locB = getTestLoc();
-      locB.end.column = undefined;
+      locB.end.column = undefinedColumn();
       expect(containsLocation(locA, locB)).toEqual(true);
     });
   });
 
   describe("locations both without the column criteria", () => {
     it("should contain location on the same start line", () => {
       const locA = getTestLoc();
       const locB = getTestLoc();
-      locA.start.column = undefined;
-      locB.start.column = undefined;
+      locA.start.column = undefinedColumn();
+      locB.start.column = undefinedColumn();
       expect(containsLocation(locA, locB)).toEqual(true);
     });
 
     it("should contain location on the same end line", () => {
       const locA = getTestLoc();
       const locB = getTestLoc();
-      locA.end.column = undefined;
-      locB.end.column = undefined;
+      locA.end.column = undefinedColumn();
+      locB.end.column = undefinedColumn();
       expect(containsLocation(locA, locB)).toEqual(true);
     });
   });
 });
 
 describe("nodeContainsPosition", () => {
   describe("node and position both with the column criteria", () => {
     it("should contian position within the range", () =>
@@ -268,45 +280,45 @@ describe("nodeContainsPosition", () => {
     it(`should contain position on the same end line and
         within the end column`, () =>
       testContainsPosition(endPos(0, -1), true));
   });
 
   describe("node without the column criterion", () => {
     it("should contain position on the same start line", () => {
       const loc = getTestLoc();
-      loc.start.column = undefined;
+      loc.start.column = undefinedColumn();
       const pos = startPos(0, -1);
       expect(nodeContainsPosition({ loc }, pos)).toEqual(true);
     });
 
     it("should contain position on the same end line", () => {
       const loc = getTestLoc();
-      loc.end.column = undefined;
+      loc.end.column = undefinedColumn();
       const pos = startPos(0, 1);
       expect(nodeContainsPosition({ loc }, pos)).toEqual(true);
     });
   });
 
   describe("position without the column criterion", () => {
     it("should contain position on the same start line", () =>
       testContainsPosition(startLine(), true));
 
     it("should contain position on the same end line", () =>
       testContainsPosition(endLine(), true));
   });
 
   describe("node and position both without the column criteria", () => {
     it("should contain position on the same start line", () => {
       const loc = getTestLoc();
-      loc.start.column = undefined;
+      loc.start.column = undefinedColumn();
       const pos = startLine();
       expect(nodeContainsPosition({ loc }, pos)).toEqual(true);
     });
 
     it("should contain position on the same end line", () => {
       const loc = getTestLoc();
-      loc.end.column = undefined;
+      loc.end.column = undefinedColumn();
       const pos = endLine();
       expect(nodeContainsPosition({ loc }, pos)).toEqual(true);
     });
   });
 });
--- a/devtools/client/debugger/new/src/workers/parser/tests/findOutOfScopeLocations.spec.js
+++ b/devtools/client/debugger/new/src/workers/parser/tests/findOutOfScopeLocations.spec.js
@@ -1,13 +1,15 @@
 /* eslint max-nested-callbacks: ["error", 4]*/
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
+// @flow
+
 import findOutOfScopeLocations from "../findOutOfScopeLocations";
 
 import { getSource } from "./helpers";
 import { setSource } from "../sources";
 
 function formatLines(actual) {
   return actual
     .map(
--- a/devtools/client/debugger/new/src/workers/parser/tests/framework.spec.js
+++ b/devtools/client/debugger/new/src/workers/parser/tests/framework.spec.js
@@ -1,12 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
+// @flow
+
 import { getFramework } from "../frameworks";
 import { getSource, getOriginalSource } from "./helpers";
 import { setSource } from "../sources";
 
 describe("Parser.frameworks", () => {
   describe("no framework", () => {
     it("is undefined when no framework", () => {
       const source = getOriginalSource("frameworks/plainJavascript");
--- a/devtools/client/debugger/new/src/workers/parser/tests/getScopes.spec.js
+++ b/devtools/client/debugger/new/src/workers/parser/tests/getScopes.spec.js
@@ -1,13 +1,15 @@
 /* eslint max-nested-callbacks: ["error", 4]*/
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
+// @flow
+
 import getScopes from "../getScopes";
 import { setSource } from "../sources";
 import { getOriginalSource } from "./helpers";
 import cases from "jest-in-case";
 
 cases(
   "Parser.getScopes",
   ({ name, file, type, locations }) => {
--- a/devtools/client/debugger/new/src/workers/parser/tests/getSymbols.spec.js
+++ b/devtools/client/debugger/new/src/workers/parser/tests/getSymbols.spec.js
@@ -1,13 +1,15 @@
 /* eslint max-nested-callbacks: ["error", 4]*/
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
+// @flow
+
 import { formatSymbols } from "../utils/formatSymbols";
 import { getSource, getOriginalSource } from "./helpers";
 import cases from "jest-in-case";
 
 cases(
   "Parser.getSymbols",
   ({ name, file, original, type }) => {
     const source = original
--- a/devtools/client/debugger/new/src/workers/parser/tests/mapBindings.spec.js
+++ b/devtools/client/debugger/new/src/workers/parser/tests/mapBindings.spec.js
@@ -1,12 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
+// @flow
+
 import mapExpressionBindings from "../mapBindings";
 import { parseConsoleScript } from "../utils/ast";
 import cases from "jest-in-case";
 
 const prettier = require("prettier");
 
 function format(code) {
   return prettier.format(code, { semi: false, parser: "babylon" });
--- a/devtools/client/debugger/new/src/workers/parser/tests/mapExpression.spec.js
+++ b/devtools/client/debugger/new/src/workers/parser/tests/mapExpression.spec.js
@@ -1,31 +1,28 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
+// @flow
+
 import mapExpression from "../mapExpression";
 import { format } from "prettier";
 import cases from "jest-in-case";
 
 function test({
   expression,
   newExpression,
   bindings,
   mappings,
-  shouldMapExpression,
+  shouldMapBindings,
   expectedMapped,
   parseExpression = true
 }) {
-  const res = mapExpression(
-    expression,
-    mappings,
-    bindings,
-    shouldMapExpression
-  );
+  const res = mapExpression(expression, mappings, bindings, shouldMapBindings);
 
   if (parseExpression) {
     expect(
       format(res.expression, {
         parser: "babylon"
       })
     ).toEqual(format(newExpression, { parser: "babylon" }));
   } else {
@@ -42,206 +39,206 @@ function formatAwait(body) {
 describe("mapExpression", () => {
   cases("mapExpressions", test, [
     {
       name: "await",
       expression: "await a()",
       newExpression: formatAwait("return await a()"),
       bindings: [],
       mappings: {},
-      shouldMapExpression: true,
+      shouldMapBindings: true,
       expectedMapped: {
         await: true,
         bindings: false,
         originalExpression: false
       }
     },
     {
       name: "await (multiple statements)",
       expression: "const x = await a(); x + x",
       newExpression: formatAwait("self.x = await a(); return x + x;"),
       bindings: [],
       mappings: {},
-      shouldMapExpression: true,
+      shouldMapBindings: true,
       expectedMapped: {
         await: true,
         bindings: true,
         originalExpression: false
       }
     },
     {
       name: "await (inner)",
       expression: "async () => await a();",
       newExpression: "async () => await a();",
       bindings: [],
       mappings: {},
-      shouldMapExpression: true,
+      shouldMapBindings: true,
       expectedMapped: {
         await: false,
         bindings: false,
         originalExpression: false
       }
     },
     {
       name: "await (multiple awaits)",
       expression: "const x = await a(); await b(x)",
       newExpression: formatAwait("self.x = await a(); return await b(x);"),
       bindings: [],
       mappings: {},
-      shouldMapExpression: true,
+      shouldMapBindings: true,
       expectedMapped: {
         await: true,
         bindings: true,
         originalExpression: false
       }
     },
     {
       name: "await (assignment)",
       expression: "let x = await sleep(100, 2)",
       newExpression: formatAwait("return (self.x = await sleep(100, 2))"),
       bindings: [],
       mappings: {},
-      shouldMapExpression: true,
+      shouldMapBindings: true,
       expectedMapped: {
         await: true,
         bindings: true,
         originalExpression: false
       }
     },
     {
       name: "await (destructuring)",
       expression: "const { a, c: y } = await b()",
       newExpression: formatAwait(
         "return ({ a: self.a, c: self.y } = await b())"
       ),
       bindings: [],
       mappings: {},
-      shouldMapExpression: true,
+      shouldMapBindings: true,
       expectedMapped: {
         await: true,
         bindings: true,
         originalExpression: false
       }
     },
     {
       name: "await (array destructuring)",
       expression: "const [a, y] = await b();",
       newExpression: formatAwait("return ([self.a, self.y] = await b())"),
       bindings: [],
       mappings: {},
-      shouldMapExpression: true,
+      shouldMapBindings: true,
       expectedMapped: {
         await: true,
         bindings: true,
         originalExpression: false
       }
     },
     {
       name: "await (mixed destructuring)",
       expression: "const [{ a }] = await b();",
       newExpression: formatAwait("return ([{ a: self.a }] = await b())"),
       bindings: [],
       mappings: {},
-      shouldMapExpression: true,
+      shouldMapBindings: true,
       expectedMapped: {
         await: true,
         bindings: true,
         originalExpression: false
       }
     },
     {
       name: "await (destructuring, multiple statements)",
       expression: "const { a, c: y } = await b(), { x } = await y()",
       newExpression: formatAwait(`
         ({ a: self.a, c: self.y } = await b())
         return ({ x: self.x } = await y());
       `),
       bindings: [],
       mappings: {},
-      shouldMapExpression: true,
+      shouldMapBindings: true,
       expectedMapped: {
         await: true,
         bindings: true,
         originalExpression: false
       }
     },
     {
       name: "await (destructuring, bindings)",
       expression: "const { a, c: y } = await b();",
       newExpression: formatAwait("return ({ a, c: y } = await b())"),
       bindings: ["a", "y"],
       mappings: {},
-      shouldMapExpression: true,
+      shouldMapBindings: true,
       expectedMapped: {
         await: true,
         bindings: true,
         originalExpression: false
       }
     },
     {
       name: "await (array destructuring, bindings)",
       expression: "const [a, y] = await b();",
       newExpression: formatAwait("return ([a, y] = await b())"),
       bindings: ["a", "y"],
       mappings: {},
-      shouldMapExpression: true,
+      shouldMapBindings: true,
       expectedMapped: {
         await: true,
         bindings: true,
         originalExpression: false
       }
     },
     {
       name: "await (mixed destructuring, bindings)",
       expression: "const [{ a }] = await b();",
       newExpression: formatAwait("return ([{ a }] = await b())"),
       bindings: ["a"],
       mappings: {},
-      shouldMapExpression: true,
+      shouldMapBindings: true,
       expectedMapped: {
         await: true,
         bindings: true,
         originalExpression: false
       }
     },
     {
       name: "await (destructuring with defaults, bindings)",
       expression: "const { c, a = 5 } = await b();",
       newExpression: formatAwait("return ({ c: self.c, a = 5 } = await b())"),
       bindings: ["a", "y"],
       mappings: {},
-      shouldMapExpression: true,
+      shouldMapBindings: true,
       expectedMapped: {
         await: true,
         bindings: true,
         originalExpression: false
       }
     },
     {
       name: "await (array destructuring with defaults, bindings)",
       expression: "const [a, y = 10] = await b();",
       newExpression: formatAwait("return ([a, y = 10] = await b())"),
       bindings: ["a", "y"],
       mappings: {},
-      shouldMapExpression: true,
+      shouldMapBindings: true,
       expectedMapped: {
         await: true,
         bindings: true,
         originalExpression: false
       }
     },
     {
       name: "await (mixed destructuring with defaults, bindings)",
       expression: "const [{ c = 5 }, a = 5] = await b();",
       newExpression: formatAwait(
         "return ([ { c: self.c = 5 }, a = 5] = await b())"
       ),
       bindings: ["a"],
       mappings: {},
-      shouldMapExpression: true,
+      shouldMapBindings: true,
       expectedMapped: {
         await: true,
         bindings: true,
         originalExpression: false
       }
     },
     {
       name: "await (nested destructuring, bindings)",
@@ -249,34 +246,34 @@ describe("mapExpression", () => {
       newExpression: formatAwait(`
        return ({
           a,
           c: { y }
         } = await b());
     `),
       bindings: ["a", "y"],
       mappings: {},
-      shouldMapExpression: true,
+      shouldMapBindings: true,
       expectedMapped: {
         await: true,
         bindings: true,
         originalExpression: false
       }
     },
     {
       name: "await (nested destructuring with defaults)",
       expression: "const { a, c: { y = 5 } = {} } = await b();",
       newExpression: formatAwait(`return ({
         a: self.a,
         c: { y: self.y = 5 } = {},
       } = await b());
     `),
       bindings: [],
       mappings: {},
-      shouldMapExpression: true,
+      shouldMapBindings: true,
       expectedMapped: {
         await: true,
         bindings: true,
         originalExpression: false
       }
     },
     {
       name: "await (very nested destructuring with defaults)",
@@ -289,189 +286,381 @@ describe("mapExpression", () => {
             y: { z: self.z = 10, b: self.b } = {
               b: 5
             }
           }
         } = await b());
     `),
       bindings: [],
       mappings: {},
-      shouldMapExpression: true,
+      shouldMapBindings: true,
       expectedMapped: {
         await: true,
         bindings: true,
         originalExpression: false
       }
     },
     {
       name: "await (with SyntaxError)",
       expression: "await new Promise())",
       newExpression: formatAwait("await new Promise())"),
       parseExpression: false,
       bindings: [],
       mappings: {},
-      shouldMapExpression: true,
+      shouldMapBindings: true,
+      expectedMapped: {
+        await: true,
+        bindings: false,
+        originalExpression: false
+      }
+    },
+    {
+      name: "await (no bindings, let assignment)",
+      expression: "let a = await 123;",
+      newExpression: `let a;
+
+        (async () => {
+          return a = await 123;
+        })()`,
+      shouldMapBindings: false,
+      expectedMapped: {
+        await: true,
+        bindings: false,
+        originalExpression: false
+      }
+    },
+    {
+      name: "await (no bindings, var assignment)",
+      expression: "var a = await 123;",
+      newExpression: `var a;
+
+        (async () => {
+          return a = await 123;
+        })()`,
+      shouldMapBindings: false,
+      expectedMapped: {
+        await: true,
+        bindings: false,
+        originalExpression: false
+      }
+    },
+    {
+      name: "await (no bindings, const assignment)",
+      expression: "const a = await 123;",
+      newExpression: `let a;
+
+        (async () => {
+          return a = await 123;
+        })()`,
+      shouldMapBindings: false,
+      expectedMapped: {
+        await: true,
+        bindings: false,
+        originalExpression: false
+      }
+    },
+    {
+      name: "await (no bindings, multiple assignments)",
+      expression: "let a = 1, b, c = 3; b = await 123; a + b + c",
+      newExpression: `let a, b, c;
+
+        (async () => {
+          a = 1;
+          c = 3;
+          b = await 123;
+          return a + b + c;
+        })()`,
+      shouldMapBindings: false,
+      expectedMapped: {
+        await: true,
+        bindings: false,
+        originalExpression: false
+      }
+    },
+    {
+      name: "await (no bindings, object destructuring)",
+      expression: "let {a, b, c} = await x;",
+      newExpression: `let a, b, c;
+
+        (async () => {
+          return ({a, b, c} = await x);
+        })()`,
+      shouldMapBindings: false,
+      expectedMapped: {
+        await: true,
+        bindings: false,
+        originalExpression: false
+      }
+    },
+    {
+      name: "await (no bindings, object destructuring with rest)",
+      expression: "let {a, ...rest} = await x;",
+      newExpression: `let a, rest;
+
+        (async () => {
+          return ({a, ...rest} = await x);
+        })()`,
+      shouldMapBindings: false,
+      expectedMapped: {
+        await: true,
+        bindings: false,
+        originalExpression: false
+      }
+    },
+    {
+      name:
+        "await (no bindings, object destructuring with renaming and default)",
+      expression: "let {a: hello, b, c: world, d: $ = 4} = await x;",
+      newExpression: `let hello, b, world, $;
+
+        (async () => {
+          return ({a: hello, b, c: world, d: $ = 4} = await x);
+        })()`,
+      shouldMapBindings: false,
+      expectedMapped: {
+        await: true,
+        bindings: false,
+        originalExpression: false
+      }
+    },
+    {
+      name:
+        "await (no bindings, nested object destructuring + renaming + default)",
+      expression: `let {
+          a: hello, c: { y: { z = 10, b: bill, d: [e, f = 20] }}
+        } = await x; z;`,
+      newExpression: `let hello, z, bill, e, f;
+
+        (async () => {
+          ({ a: hello, c: { y: { z = 10, b: bill, d: [e, f = 20] }}} = await x);
+          return z;
+        })()`,
+      shouldMapBindings: false,
+      expectedMapped: {
+        await: true,
+        bindings: false,
+        originalExpression: false
+      }
+    },
+    {
+      name: "await (no bindings, array destructuring)",
+      expression: "let [a, b, c] = await x; c;",
+      newExpression: `let a, b, c;
+
+        (async () => {
+          [a, b, c] = await x;
+          return c;
+        })()`,
+      shouldMapBindings: false,
+      expectedMapped: {
+        await: true,
+        bindings: false,
+        originalExpression: false
+      }
+    },
+    {
+      name: "await (no bindings, array destructuring with default)",
+      expression: "let [a, b = 1, c = 2] = await x; c;",
+      newExpression: `let a, b, c;
+
+        (async () => {
+          [a, b = 1, c = 2] = await x;
+          return c;
+        })()`,
+      shouldMapBindings: false,
+      expectedMapped: {
+        await: true,
+        bindings: false,
+        originalExpression: false
+      }
+    },
+    {
+      name: "await (no bindings, array destructuring with default and rest)",
+      expression: "let [a, b = 1, c = 2, ...rest] = await x; rest;",
+      newExpression: `let a, b, c, rest;
+
+        (async () => {
+          [a, b = 1, c = 2, ...rest] = await x;
+          return rest;
+        })()`,
+      shouldMapBindings: false,
+      expectedMapped: {
+        await: true,
+        bindings: false,
+        originalExpression: false
+      }
+    },
+    {
+      name: "await (no bindings, nested array destructuring with default)",
+      expression: "let [a, b = 1, [c = 2, [d = 3, e = 4]]] = await x; c;",
+      newExpression: `let a, b, c, d, e;
+
+        (async () => {
+          [a, b = 1, [c = 2, [d = 3, e = 4]]] = await x;
+          return c;
+        })()`,
+      shouldMapBindings: false,
       expectedMapped: {
         await: true,
         bindings: false,
         originalExpression: false
       }
     },
     {
       name: "simple",
       expression: "a",
       newExpression: "a",
       bindings: [],
       mappings: {},
-      shouldMapExpression: true,
+      shouldMapBindings: true,
       expectedMapped: {
         await: false,
         bindings: false,
         originalExpression: false
       }
     },
     {
       name: "mappings",
       expression: "a",
       newExpression: "_a",
       bindings: [],
       mappings: {
         a: "_a"
       },
-      shouldMapExpression: true,
+      shouldMapBindings: true,
       expectedMapped: {
         await: false,
         bindings: false,
         originalExpression: true
       }
     },
     {
       name: "declaration",
       expression: "var a = 3;",
       newExpression: "self.a = 3",
       bindings: [],
       mappings: {},
-      shouldMapExpression: true,
+      shouldMapBindings: true,
       expectedMapped: {
         await: false,
         bindings: true,
         originalExpression: false
       }
     },
     {
       name: "declaration + destructuring",
       expression: "var { a } = { a: 3 };",
       newExpression: "({ a: self.a } = {\n a: 3 \n})",
       bindings: [],
       mappings: {},
-      shouldMapExpression: true,
+      shouldMapBindings: true,
       expectedMapped: {
         await: false,
         bindings: true,
         originalExpression: false
       }
     },
     {
       name: "bindings",
       expression: "var a = 3;",
       newExpression: "a = 3",
       bindings: ["a"],
       mappings: {},
-      shouldMapExpression: true,
+      shouldMapBindings: true,
       expectedMapped: {
         await: false,
         bindings: true,
         originalExpression: false
       }
     },
     {
       name: "bindings + destructuring",
       expression: "var { a } = { a: 3 };",
       newExpression: "({ a } = { \n a: 3 \n })",
       bindings: ["a"],
       mappings: {},
-      shouldMapExpression: true,
+      shouldMapBindings: true,
       expectedMapped: {
         await: false,
         bindings: true,
         originalExpression: false
       }
     },
     {
       name: "bindings + destructuring + rest",
       expression: "var { a, ...foo } = {}",
       newExpression: "({ a, ...self.foo } = {})",
       bindings: ["a"],
       mappings: {},
-      shouldMapExpression: true,
+      shouldMapBindings: true,
       expectedMapped: {
         await: false,
         bindings: true,
         originalExpression: false
       }
     },
     {
       name: "bindings + array destructuring + rest",
       expression: "var [a, ...foo] = []",
       newExpression: "([a, ...self.foo] = [])",
       bindings: ["a"],
       mappings: {},
-      shouldMapExpression: true,
+      shouldMapBindings: true,
       expectedMapped: {
         await: false,
         bindings: true,
         originalExpression: false
       }
     },
     {
       name: "bindings + mappings",
       expression: "a = 3;",
       newExpression: "self.a = 3",
       bindings: ["_a"],
       mappings: { a: "_a" },
-      shouldMapExpression: true,
+      shouldMapBindings: true,
       expectedMapped: {
         await: false,
         bindings: true,
         originalExpression: false
       }
     },
     {
       name: "bindings + mappings + destructuring",
       expression: "var { a } = { a: 4 }",
       newExpression: "({ a: self.a } = {\n a: 4 \n})",
       bindings: ["_a"],
       mappings: { a: "_a" },
-      shouldMapExpression: true,
+      shouldMapBindings: true,
       expectedMapped: {
         await: false,
         bindings: true,
         originalExpression: false
       }
     },
     {
       name: "bindings without mappings",
       expression: "a = 3;",
       newExpression: "a = 3",
       bindings: [],
       mappings: { a: "_a" },
-      shouldMapExpression: false,
+      shouldMapBindings: false,
       expectedMapped: {
         await: false,
         bindings: false,
         originalExpression: false
       }
     },
     {
       name: "object destructuring + bindings without mappings",
       expression: "({ a } = {});",
       newExpression: "({ a: _a } = {})",
       bindings: [],
       mappings: { a: "_a" },
-      shouldMapExpression: false,
+      shouldMapBindings: false,
       expectedMapped: {
         await: false,
         bindings: false,
         originalExpression: true
       }
     }
   ]);
 });
--- a/devtools/client/debugger/new/src/workers/parser/tests/mapOriginalExpression.spec.js
+++ b/devtools/client/debugger/new/src/workers/parser/tests/mapOriginalExpression.spec.js
@@ -1,22 +1,24 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
+// @flow
+
 import mapExpression from "../mapExpression";
 import { format } from "prettier";
 
 const formatOutput = output =>
   format(output, {
     parser: "babylon"
   });
 
 const mapOriginalExpression = (expression, mappings) =>
-  mapExpression(expression, mappings, null, false, false).expression;
+  mapExpression(expression, mappings, [], false, false).expression;
 
 describe("mapOriginalExpression", () => {
   it("simple", () => {
     const generatedExpression = mapOriginalExpression("a + b;", {
       a: "foo",
       b: "bar"
     });
     expect(generatedExpression).toEqual("foo + bar;");
--- a/devtools/client/debugger/new/src/workers/parser/tests/pausePoints.spec.js
+++ b/devtools/client/debugger/new/src/workers/parser/tests/pausePoints.spec.js
@@ -1,13 +1,15 @@
 /* eslint max-nested-callbacks: ["error", 4]*/
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
+// @flow
+
 import cases from "jest-in-case";
 import {
   formatPausePoints,
   convertToList
 } from "../../../utils/pause/pausePoints";
 
 import { getPausePoints } from "../pausePoints";
 import { getSource, getOriginalSource } from "./helpers";
@@ -16,17 +18,20 @@ import { setSource } from "../sources";
 cases(
   "Parser.pausePoints",
   ({ name, file, original, type }) => {
     const source = original
       ? getOriginalSource(file, type)
       : getSource(file, type);
 
     setSource(source);
-    const nodes = convertToList(getPausePoints(source.id));
+
+    // The coercion here is needed because getPausePoints and convertToList
+    // operate on two incompatible definitions of PausePointsMap
+    const nodes = convertToList((getPausePoints(source.id): any));
     expect(formatPausePoints(source.text, nodes)).toMatchSnapshot();
   },
   [
     { name: "control-flow", file: "control-flow" },
     { name: "flow", file: "flow", original: true },
     { name: "calls", file: "calls" },
     { name: "statements", file: "statements" },
     { name: "modules", file: "modules", original: true },
--- a/devtools/client/debugger/new/src/workers/parser/tests/sources.spec.js
+++ b/devtools/client/debugger/new/src/workers/parser/tests/sources.spec.js
@@ -1,22 +1,26 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
+// @flow
+
 import { hasSource, getSource, setSource, clearSources } from "../sources";
 
 import type { Source } from "../../../types";
 
 describe("sources", () => {
   it("hasSource", () => {
     const sourceId = "some.source.id";
     const source: Source = {
       id: sourceId,
       url: "http://example.org/some.source.js",
+      introductionUrl: "",
+      relativeUrl: "",
       isBlackBoxed: false,
       isPrettyPrinted: false,
       isWasm: false,
       loadedState: "loaded"
     };
 
     expect(hasSource(sourceId)).toEqual(false);
     setSource(source);
--- a/devtools/client/debugger/new/src/workers/parser/tests/steps.spec.js
+++ b/devtools/client/debugger/new/src/workers/parser/tests/steps.spec.js
@@ -1,12 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
+// @flow
+
 import { getNextStep } from "../steps";
 import { getSource } from "./helpers";
 import { setSource } from "../sources";
 
 describe("getNextStep", () => {
   describe("await", () => {
     it("first await call", () => {
       const source = getSource("async");
--- a/devtools/client/debugger/new/src/workers/parser/tests/validate.spec.js
+++ b/devtools/client/debugger/new/src/workers/parser/tests/validate.spec.js
@@ -1,12 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
+// @flow
+
 import { hasSyntaxError } from "../validate";
 
 describe("has syntax error", () => {
   it("should return false", () => {
     expect(hasSyntaxError("foo")).toEqual(false);
   });
 
   it("should return the error object for the invalid expression", () => {
--- a/devtools/client/debugger/new/src/workers/parser/utils/ast.js
+++ b/devtools/client/debugger/new/src/workers/parser/utils/ast.js
@@ -187,8 +187,22 @@ export function hasNode(rootNode: Node, 
     });
   } catch (e) {
     if (e.message === "MATCH") {
       return true;
     }
   }
   return false;
 }
+
+export function replaceNode(ancestors: Object[], node: Object) {
+  const parent = ancestors[ancestors.length - 1];
+
+  if (typeof parent.index === "number") {
+    if (Array.isArray(node)) {
+      parent.node[parent.key].splice(parent.index, 1, ...node);
+    } else {
+      parent.node[parent.key][parent.index] = node;
+    }
+  } else {
+    parent.node[parent.key] = node;
+  }
+}
--- a/devtools/client/debugger/new/src/workers/parser/utils/tests/ast.spec.js
+++ b/devtools/client/debugger/new/src/workers/parser/utils/tests/ast.spec.js
@@ -1,18 +1,22 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
+// @flow
+
 import { getAst } from "../ast";
 import { setSource } from "../../sources";
 import cases from "jest-in-case";
 
+import { makeMockSource } from "../../../../utils/test-mockup";
+
 function createSource(contentType) {
-  return { id: "foo", text: "2", contentType };
+  return makeMockSource(undefined, "foo", contentType, &q