Merge mozilla-central to cedar. a=merge default tip
authorMike de Boer <mdeboer@mozilla.com>
Fri, 01 Feb 2019 11:05:07 +0100
changeset 456402 b62cac80b403038aa9c9f7da5a987dc04838121c
parent 456236 69d31255cbbf710ef5376b910f98f088691c2bd4 (current diff)
parent 456401 d58901c5036ffa06da6144f22a31479116ee0835 (diff)
push id19
push usermdeboer@mozilla.com
push dateFri, 01 Feb 2019 10:05:45 +0000
reviewersmerge
milestone67.0a1
Merge mozilla-central to cedar. a=merge
browser/components/urlbar/tests/browser/browser_canonizeURL.js
browser/components/urlbar/tests/legacy/browser_canonizeURL.js
browser/components/urlbar/tests/legacy/browser_locationBarCommand.js
browser/components/urlbar/tests/legacy/browser_urlbarAboutHomeLoading.js
browser/components/urlbar/tests/legacy/browser_urlbarStop.js
dom/canvas/WebGLExtensionBase.cpp
dom/ipc/ContentParent.cpp
gfx/harfbuzz/README
gfx/tests/reftest/1523776-ref.html
gfx/tests/reftest/1523776.html
js/src/jit-test/tests/wasm/spec/harness/wast.js
mobile/android/chrome/content/browser.js
testing/web-platform/mozilla/tests/wasm/js/harness/wast.js
toolkit/components/extensions/Extension.jsm
--- a/accessible/base/MarkupMap.h
+++ b/accessible/base/MarkupMap.h
@@ -93,28 +93,30 @@ MARKUPMAP(
       nsIContent* firstChild = aElement->GetFirstChild();
       if (firstChild) {
         // Render it if it is a text node.
         if (firstChild->IsText()) {
           return new HyperTextAccessibleWrap(aElement, aContext->Document());
         }
         // Check to see if first child has an inline frame.
         nsIFrame* firstChildFrame = firstChild->GetPrimaryFrame();
-        if (firstChildFrame && firstChildFrame->IsInlineFrame()) {
+        if (firstChildFrame && (firstChildFrame->IsInlineFrame() ||
+                                firstChildFrame->IsBrFrame())) {
           return new HyperTextAccessibleWrap(aElement, aContext->Document());
         }
         nsIContent* lastChild = aElement->GetLastChild();
         if (lastChild && lastChild != firstChild) {
           // Render it if it is a text node.
           if (lastChild->IsText()) {
             return new HyperTextAccessibleWrap(aElement, aContext->Document());
           }
           // Check to see if last child has an inline frame.
           nsIFrame* lastChildFrame = lastChild->GetPrimaryFrame();
-          if (lastChildFrame && lastChildFrame->IsInlineFrame()) {
+          if (lastChildFrame && (lastChildFrame->IsInlineFrame() ||
+                                 lastChildFrame->IsBrFrame())) {
             return new HyperTextAccessibleWrap(aElement, aContext->Document());
           }
         }
       }
       return nullptr;
     },
     roles::SECTION)
 
--- a/accessible/tests/mochitest/tree/test_divs.html
+++ b/accessible/tests/mochitest/tree/test_divs.html
@@ -186,16 +186,35 @@ function doTest() {
             { TEXT_LEAF: [] }, // c
             { TEXT_LEAF: [] }, // d
           ], // end children second inner div
         }, // end second inner div
       ], // end children outer div
     };
   testAccessibleTree("c10", tree);
 
+  // c11: A div must be exposed if it contains a br element.
+  tree =
+    { role: ROLE_SECTION, // outer div
+      children: [
+        { role: ROLE_SECTION, // First line
+          children: [
+            { TEXT_LEAF: [] }, // text
+          ], // end children first line
+        }, // end first line
+        { SECTION: [] }, // second, blank, line
+        { role: ROLE_SECTION, // Third line
+          children: [
+            { TEXT_LEAF: [] }, // text
+          ], // end children third line
+        }, // end third line
+      ], // end children outer div
+    };
+  testAccessibleTree("c11", tree);
+
   SimpleTest.finish();
 }
 
 SimpleTest.waitForExplicitFinish();
 addA11yLoadEvent(doTest);
 </script>
 </head>
 <body>
@@ -226,10 +245,13 @@ addA11yLoadEvent(doTest);
   <div id="c7"><div>foo<div>bar</div>baz</div></div>
 
   <!-- Expose all divs due to them all being text block breakers. -->
   <div id="c8"><div>foo<div><div>bar</div>baz</div></div></div>
   <div id="c9"><div><div><div>a</div>b</div>c</div></div>
 
   <!-- Both inner divs need to be rendered so there is a break after b. -->
   <div id="c10"><div><b>a</b>b</div><div><b>c</b><b>d</b></div></div>
+
+  <!-- Div must be rendered if it contains a br -->
+  <div id="c11"><div>first line.</div><div><br /></div><div>third line</div></div>
 </body>
 </html>
--- a/browser/actors/PluginChild.jsm
+++ b/browser/actors/PluginChild.jsm
@@ -239,47 +239,66 @@ class PluginChild extends ActorChild {
    * Adjust the style in which the overlay will be displayed. It might be adjusted
    * based on its size, or if there's some other element covering all corners of
    * the overlay.
    *
    * This function will handle adjusting the style of the overlay, but will
    * not handle hiding it. That is done by setVisibility with the return value
    * from this function.
    *
+   * @param {Element} plugin  The plug-in element
+   * @param {Element} overlay The overlay element inside the UA Shadow DOM of
+   *                          the plug-in element
+   * @param {boolean} flushLayout Allow flush layout during computation and
+   *                              adjustment.
    * @returns A value from OVERLAY_DISPLAY.
    */
-  computeAndAdjustOverlayDisplay(plugin, overlay) {
+  computeAndAdjustOverlayDisplay(plugin, overlay, flushLayout) {
     let fallbackType = plugin.pluginFallbackType;
     if (plugin.pluginFallbackTypeOverride !== undefined) {
       fallbackType = plugin.pluginFallbackTypeOverride;
     }
     if (fallbackType == Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY_QUIET) {
       return OVERLAY_DISPLAY.HIDDEN;
     }
 
     // If the overlay size is 0, we haven't done layout yet. Presume that
     // plugins are visible until we know otherwise.
-    if (overlay.scrollWidth == 0) {
+    if (flushLayout && overlay.scrollWidth == 0) {
       return OVERLAY_DISPLAY.FULL;
     }
 
     let overlayDisplay = OVERLAY_DISPLAY.FULL;
+    let contentWindow = plugin.ownerGlobal;
+    let cwu = contentWindow.windowUtils;
 
     // Is the <object>'s size too small to hold what we want to show?
-    let pluginRect = plugin.getBoundingClientRect();
+    let pluginRect = flushLayout ? plugin.getBoundingClientRect() :
+                                   cwu.getBoundsWithoutFlushing(plugin);
     let pluginWidth = Math.ceil(pluginRect.width);
     let pluginHeight = Math.ceil(pluginRect.height);
 
+    let layoutNeedsFlush = !flushLayout &&
+      cwu.needsFlush(cwu.FLUSH_STYLE) &&
+      cwu.needsFlush(cwu.FLUSH_LAYOUT);
+
     // We must set the attributes while here inside this function in order
     // for a possible re-style to occur, which will make the scrollWidth/Height
     // checks below correct. Otherwise, we would be requesting e.g. a TINY
     // overlay here, but the default styling would be used, and that would make
     // it overflow, causing it to change to BLANK instead of remaining as TINY.
 
-    if (pluginWidth <= 32 || pluginHeight <= 32) {
+    if (layoutNeedsFlush) {
+      // Set the content to be oversized when we the overlay size is 0,
+      // so that we could receive an overflow event afterwards when there is
+      // a layout.
+      overlayDisplay = OVERLAY_DISPLAY.FULL;
+      overlay.setAttribute("sizing", "oversized");
+      overlay.removeAttribute("notext");
+    } else if (pluginWidth <= 32 || pluginHeight <= 32) {
       overlay.setAttribute("sizing", "blank");
       overlayDisplay = OVERLAY_DISPLAY.BLANK;
     } else if (pluginWidth <= 80 || pluginHeight <= 60) {
       overlayDisplay = OVERLAY_DISPLAY.TINY;
       overlay.setAttribute("sizing", "tiny");
       overlay.setAttribute("notext", "notext");
     } else if (pluginWidth <= 120 || pluginHeight <= 80) {
       overlayDisplay = OVERLAY_DISPLAY.REDUCED;
@@ -290,16 +309,23 @@ class PluginChild extends ActorChild {
       overlay.removeAttribute("sizing");
       overlay.setAttribute("notext", "notext");
     } else {
       overlayDisplay = OVERLAY_DISPLAY.FULL;
       overlay.removeAttribute("sizing");
       overlay.removeAttribute("notext");
     }
 
+    // The hit test below only works with correct layout information,
+    // don't do it if layout needs flush.
+    // We also don't want to access scrollWidth/scrollHeight if
+    // the layout needs flush.
+    if (layoutNeedsFlush) {
+      return overlayDisplay;
+    }
 
     // XXX bug 446693. The text-shadow on the submitted-report text at
     //     the bottom causes scrollHeight to be larger than it should be.
     let overflows = (overlay.scrollWidth > pluginWidth) ||
                     (overlay.scrollHeight - 5 > pluginHeight);
     if (overflows) {
       overlay.setAttribute("sizing", "blank");
       return OVERLAY_DISPLAY.BLANK;
@@ -314,19 +340,16 @@ class PluginChild extends ActorChild {
     let centerX = left + (right - left) / 2;
     let centerY = top + (bottom - top) / 2;
     let points = [[left, top],
                    [left, bottom],
                    [right, top],
                    [right, bottom],
                    [centerX, centerY]];
 
-    let contentWindow = plugin.ownerGlobal;
-    let cwu = contentWindow.windowUtils;
-
     for (let [x, y] of points) {
       if (x < 0 || y < 0) {
         continue;
       }
       let el = cwu.elementFromPoint(x, y, true, true);
       if (el === plugin) {
         return overlayDisplay;
       }
@@ -500,20 +523,21 @@ class PluginChild extends ActorChild {
         break;
     }
 
     // Show the in-content UI if it's not too big. The crashed plugin handler already did this.
     let overlay = this.getPluginUI(plugin, "main");
     if (eventType != "PluginCrashed") {
       if (overlay != null) {
         this.setVisibility(plugin, overlay,
-                           this.computeAndAdjustOverlayDisplay(plugin, overlay));
+                           this.computeAndAdjustOverlayDisplay(plugin, overlay, false));
+
         let resizeListener = () => {
           this.setVisibility(plugin, overlay,
-            this.computeAndAdjustOverlayDisplay(plugin, overlay));
+            this.computeAndAdjustOverlayDisplay(plugin, overlay, true));
         };
         plugin.addEventListener("overflow", resizeListener);
         plugin.addEventListener("underflow", resizeListener);
       }
     }
 
     let closeIcon = this.getPluginUI(plugin, "closeIcon");
     if (closeIcon) {
@@ -921,24 +945,25 @@ class PluginChild extends ActorChild {
     this.addLinkClickCallback(helpIcon, "openHelpPage");
 
     let crashText = this.getPluginUI(plugin, "crashedText");
     crashText.textContent = message;
 
     let link = this.getPluginUI(plugin, "reloadLink");
     this.addLinkClickCallback(link, "reloadPage");
 
-    let overlayDisplayState = this.computeAndAdjustOverlayDisplay(plugin, overlay);
+    // This might trigger force reflow, but plug-in crashing code path shouldn't be hot.
+    let overlayDisplayState = this.computeAndAdjustOverlayDisplay(plugin, overlay, true);
 
     // Is the <object>'s size too small to hold what we want to show?
     if (overlayDisplayState != OVERLAY_DISPLAY.FULL) {
       // First try hiding the crash report submission UI.
       statusDiv.removeAttribute("status");
 
-      overlayDisplayState = this.computeAndAdjustOverlayDisplay(plugin, overlay);
+      overlayDisplayState = this.computeAndAdjustOverlayDisplay(plugin, overlay, true);
     }
     this.setVisibility(plugin, overlay, overlayDisplayState);
 
     let doc = plugin.ownerDocument;
     let runID = plugin.runID;
 
     if (overlayDisplayState == OVERLAY_DISPLAY.FULL) {
       doc.mozNoPluginCrashedNotification = true;
--- a/browser/app/blocklist.xml
+++ b/browser/app/blocklist.xml
@@ -1,10 +1,10 @@
 <?xml version='1.0' encoding='UTF-8'?>
-<blocklist lastupdate="1548198338831" xmlns="http://www.mozilla.org/2006/addons-blocklist">
+<blocklist lastupdate="1548699177099" xmlns="http://www.mozilla.org/2006/addons-blocklist">
   <emItems>
     <emItem blockID="i334" id="{0F827075-B026-42F3-885D-98981EE7B1AE}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i1211" id="flvto@hotger.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
@@ -2463,16 +2463,24 @@
     <emItem blockID="1e2ae4c0-66cd-40bc-9cf6-5ca0ce9548f7" id="hlper@xero.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="87c17ce6-aaef-4d47-a662-588efff34041" id="/^((\{feff5ea4-ed4a-46a3-9331-12fec01d52a9\})|(\{8ffc339c-0ca1-46e3-acb3-3bfd889f9a21\})|(\{1fe481b5-baad-44e9-b410-082cf0f2acbb\})|(\{92c85192-b325-4599-82e2-a110f193eae6\})|(\{5bc21266-26f1-469a-bc1a-a49d7b70ecb9\})|(\{dc2dd143-f2e7-4f46-a8ff-4dc1a985e889\})|(\{c1233bb6-31a9-4c7d-8469-f8f44adee9ba\})|(\{d0d48905-1065-43dc-ab96-434d100602ed\})|(\{11deae99-2675-4d5e-86cd-7bd55b88daf2\})|(\{a7014e8e-eacf-4ba0-9047-c897c4ed3387\})|(\{b9c545a5-0ffa-490a-8071-2fee09478cfe\})|(\{39e8eebb-4d9e-4a03-93a8-4468fdd7a186\})|(\{8ccc00b1-2010-4155-a07c-f4d7c4d6dec2\})|(\{a1056511-4919-43b7-a9e5-ac2b770de810\})|(\{90a6da19-9165-44c1-819c-e3b53409f9c9\})|(\{e3862078-8f9f-4f8e-99dc-55ba558f0619\})|(\{d89fcf34-2615-4efc-a267-1e83ab6a19d0\})|(\{588151ce-eab4-4acf-83a7-bb5ccaf4d867\})|(\{6ab6312d-5fd4-42a9-ab10-08b954e53f9d\})|(\{24a6cbde-be68-4b7d-9f1b-d4d5dfd178a3\})|(\{55ae1a08-105f-4f7f-9d4e-e448b517db2b\})|(\{74fe9d83-df17-4812-bd3f-27b84b0086b7\})|(\{21140e51-713a-4bf8-865b-e2ee07282409\})|(\{24f39610-2958-4dc8-a73b-75cc9665bffa\})|(\{c50a45a5-efa4-44af-8946-6f20e4748d47\})|(\{41d0b7e0-0d93-4f67-bed9-da6d7688ad16\})|(\{c2bee222-2163-4c0f-89f5-4ac502f06810\})|(\{4be4602e-2b20-473f-8f2b-85e8c53d17dc\})|(\{dec2e9b5-e787-4fb5-a7bc-5894f80f7367\})|(\{179526e1-1824-49f7-afb3-49fdaadaa503\})|(\{4f07d826-ca9e-4370-a508-b984f54758de\})|(\{d0558df2-714f-4793-9d85-d2d648de4f2e\})|(\{daf1a69b-f47b-4936-bd25-5ac21f4e27ec\}))$/">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
+    <emItem blockID="62c54642-73ab-4641-b5c2-47e4ae29bbc5" id="{4e47160d-ec83-417c-ab01-cda978378d9e}">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="3"/>
+    </emItem>
+    <emItem blockID="97c4ee31-4009-40de-ae02-f1b349c87d01" id="/^((\{a9bc520e-68ab-49c2-a8df-75a0349d54fd\})|(\{bfc5d009-c6bd-4526-92ce-a9d27102f7f2\}))$/">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="3"/>
+    </emItem>
   </emItems>
   <pluginItems>
     <pluginItem blockID="p332">
       <match exp="libflashplayer\.so" name="filename"/>
       <match exp="^Shockwave Flash 11.(0|1) r[0-9]{1,3}$" name="description"/>
       <infoURL>https://get.adobe.com/flashplayer/</infoURL>
       <versionRange severity="0" vulnerabilitystatus="1">
         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
--- a/browser/base/content/aboutNetError-new.xhtml
+++ b/browser/base/content/aboutNetError-new.xhtml
@@ -185,39 +185,39 @@
             <p><a href="https://support.mozilla.org/kb/what-does-your-connection-is-not-secure-mean" id="learnMoreLink" target="new" data-telemetry-id="learn_more_link">&errorReporting.learnMore;</a></p>
           </div>
         </div>
 
         <!-- UI for option to report certificate errors to Mozilla. Removed on
              init for other error types .-->
         <div id="prefChangeContainer" class="button-container">
           <p>&prefReset.longDesc;</p>
-          <button id="prefResetButton" class="primary" autocomplete="off">&prefReset.label;</button>
+          <button id="prefResetButton" class="primary">&prefReset.label;</button>
         </div>
 
         <div id="certErrorAndCaptivePortalButtonContainer" class="button-container">
-          <button id="returnButton" class="primary" autocomplete="off" data-telemetry-id="return_button_top">&returnToPreviousPage1.label;</button>
-          <button id="openPortalLoginPageButton" class="primary" autocomplete="off">&openPortalLoginPage.label2;</button>
-          <button id="errorTryAgain" class="primary" autocomplete="off">&retry.label;</button>
-          <button id="advancedButton" data-telemetry-id="advanced_button" autocomplete="off">&advanced2.label;</button>
-          <button id="moreInformationButton" autocomplete="off">&moreInformation.label;</button>
+          <button id="returnButton" class="primary" data-telemetry-id="return_button_top">&returnToPreviousPage1.label;</button>
+          <button id="openPortalLoginPageButton" class="primary">&openPortalLoginPage.label2;</button>
+          <button id="errorTryAgain" class="primary">&retry.label;</button>
+          <button id="advancedButton" data-telemetry-id="advanced_button">&advanced2.label;</button>
+          <button id="moreInformationButton">&moreInformation.label;</button>
         </div>
       </div>
 
       <div id="netErrorButtonContainer" class="button-container">
-        <button id="errorTryAgain" class="primary" autocomplete="off">&retry.label;</button>
+        <button id="errorTryAgain" class="primary">&retry.label;</button>
       </div>
 
       <div id="advancedPanelContainer">
         <div id="badCertAdvancedPanel" class="advanced-panel">
           <p id="badCertTechnicalInfo"/>
           <a id="viewCertificate" href="javascript:void(0)">&viewCertificate.label;</a>
           <div id="advancedPanelButtonContainer" class="button-container">
-            <button id="advancedPanelReturnButton" class="primary" autocomplete="off" data-telemetry-id="return_button_adv">&returnToPreviousPage1.label;</button>
-            <button id="advancedPanelErrorTryAgain" class="primary" autocomplete="off">&retry.label;</button>
+            <button id="advancedPanelReturnButton" class="primary" data-telemetry-id="return_button_adv">&returnToPreviousPage1.label;</button>
+            <button id="advancedPanelErrorTryAgain" class="primary">&retry.label;</button>
             <div class="exceptionDialogButtonContainer">
               <button id="exceptionDialogButton" data-telemetry-id="exception_button">&securityOverride.exceptionButton1Label;</button>
             </div>
           </div>
         </div>
 
         <div id="certificateErrorReporting">
             <p class="toggle-container-with-text">
--- a/browser/base/content/aboutNetError.xhtml
+++ b/browser/base/content/aboutNetError.xhtml
@@ -145,36 +145,36 @@
           <p class="toggle-container-with-text">
             <input type="checkbox" id="automaticallyReportInFuture" role="checkbox"  data-telemetry-id="auto_report_cb"/>
             <label for="automaticallyReportInFuture" id="automaticallyReportInFuture">&errorReporting.automatic2;</label>
           </p>
         </div>
 
         <div id="prefChangeContainer" class="button-container">
           <p>&prefReset.longDesc;</p>
-          <button id="prefResetButton" class="primary" autocomplete="off">&prefReset.label;</button>
+          <button id="prefResetButton" class="primary">&prefReset.label;</button>
         </div>
 
         <div id="certErrorAndCaptivePortalButtonContainer" class="button-container">
-          <button id="returnButton" class="primary" autocomplete="off" data-telemetry-id="return_button_top">&returnToPreviousPage.label;</button>
-          <button id="openPortalLoginPageButton" class="primary" autocomplete="off">&openPortalLoginPage.label2;</button>
-          <button id="advancedButton" data-telemetry-id="advanced_button" autocomplete="off">&advanced.label;</button>
-          <button id="moreInformationButton" autocomplete="off">&moreInformation.label;</button>
+          <button id="returnButton" class="primary" data-telemetry-id="return_button_top">&returnToPreviousPage.label;</button>
+          <button id="openPortalLoginPageButton" class="primary">&openPortalLoginPage.label2;</button>
+          <button id="advancedButton" data-telemetry-id="advanced_button">&advanced.label;</button>
+          <button id="moreInformationButton">&moreInformation.label;</button>
         </div>
       </div>
 
       <div id="netErrorButtonContainer" class="button-container">
-        <button id="errorTryAgain" class="primary" autocomplete="off">&retry.label;</button>
+        <button id="errorTryAgain" class="primary">&retry.label;</button>
       </div>
 
       <div id="advancedPanelContainer">
         <div id="badCertAdvancedPanel" class="advanced-panel">
           <p id="badCertTechnicalInfo"/>
           <div id="advancedPanelButtonContainer" class="button-container">
-            <button id="advancedPanelErrorTryAgain" class="primary" autocomplete="off">&retry.label;</button>
+            <button id="advancedPanelErrorTryAgain" class="primary">&retry.label;</button>
           </div>
           <div class="exceptionDialogButtonContainer">
             <button id="exceptionDialogButton" data-telemetry-id="exception_button">&securityOverride.exceptionButtonLabel;</button>
           </div>
         </div>
 
         <div id="certificateErrorDebugInformation">
           <button id="copyToClipboard" data-telemetry-id="clipboard_button_top">&certerror.copyToClipboard.label;</button>
--- a/browser/base/content/tabbrowser.js
+++ b/browser/base/content/tabbrowser.js
@@ -1612,17 +1612,17 @@ window._gBrowser = {
         throw new Error("Cannot set an opener on a browser which should be remote!");
       }
       if (!isRemote && aBrowser.contentWindow.opener != opener) {
         throw new Error("Cannot change opener on an already non-remote browser!");
       }
     }
 
     // Abort if we're not going to change anything
-    let oldRemoteType = aBrowser.getAttribute("remoteType");
+    let oldRemoteType = aBrowser.remoteType;
     if (isRemote == aShouldBeRemote && !newFrameloader &&
         (!isRemote || oldRemoteType == remoteType)) {
       return false;
     }
 
     let tab = this.getTabForBrowser(aBrowser);
     // aBrowser needs to be inserted now if it hasn't been already.
     this._insertBrowser(tab);
@@ -1760,17 +1760,17 @@ window._gBrowser = {
 
     return true;
   },
 
   updateBrowserRemotenessByURL(aBrowser, aURL, aOptions = {}) {
     if (!gMultiProcessBrowser)
       return this.updateBrowserRemoteness(aBrowser, false);
 
-    let oldRemoteType = aBrowser.getAttribute("remoteType") || null;
+    let oldRemoteType = aBrowser.remoteType;
 
     aOptions.remoteType =
       E10SUtils.getRemoteTypeForURI(aURL,
         gMultiProcessBrowser,
         oldRemoteType,
         aBrowser.currentURI);
 
     // If this URL can't load in the current browser then flip it to the
@@ -2301,16 +2301,17 @@ window._gBrowser = {
     postData,
     preferredRemoteType,
     referrerPolicy,
     referrerURI,
     relatedToCurrent,
     sameProcessAsFrameLoader,
     skipAnimation,
     skipBackgroundNotify,
+    title,
     triggeringPrincipal,
     userContextId,
     recordExecution,
     replayExecution,
   } = {}) {
     // all callers of addTab that pass a params object need to pass
     // a valid triggeringPrincipal.
     if (!triggeringPrincipal) {
@@ -2544,16 +2545,23 @@ window._gBrowser = {
 
         if (lazyBrowserURI) {
           // Lazy browser must be explicitly registered so tab will appear as
           // a switch-to-tab candidate in autocomplete.
           this.UrlbarProviderOpenTabs.registerOpenTab(lazyBrowserURI.spec,
                                                       userContextId);
           b.registeredOpenURI = lazyBrowserURI;
         }
+        SessionStore.setTabState(t, {
+          entries: [{
+            url: lazyBrowserURI ? lazyBrowserURI.spec : "about:blank",
+            title,
+            triggeringPrincipal_base64: Utils.serializePrincipal(triggeringPrincipal),
+          }],
+        });
       } else {
         this._insertBrowser(t, true);
       }
     } catch (e) {
       Cu.reportError("Failed to create tab");
       Cu.reportError(e);
       t.remove();
       if (t.linkedBrowser) {
@@ -3804,42 +3812,47 @@ window._gBrowser = {
    */
   adoptTab(aTab, aIndex, aSelectTab) {
     // Swap the dropped tab with a new one we create and then close
     // it in the other window (making it seem to have moved between
     // windows). We also ensure that the tab we create to swap into has
     // the same remote type and process as the one we're swapping in.
     // This makes sure we don't get a short-lived process for the new tab.
     let linkedBrowser = aTab.linkedBrowser;
+    let createLazyBrowser = !aTab.linkedPanel;
     let params = {
       eventDetail: { adoptedTab: aTab },
       preferredRemoteType: linkedBrowser.remoteType,
       sameProcessAsFrameLoader: linkedBrowser.frameLoader,
       skipAnimation: true,
       index: aIndex,
+      createLazyBrowser,
+      allowInheritPrincipal: createLazyBrowser,
     };
 
     let numPinned = this._numPinnedTabs;
     if (aIndex < numPinned || (aTab.pinned && aIndex == numPinned)) {
       params.pinned = true;
     }
 
     if (aTab.hasAttribute("usercontextid")) {
       // new tab must have the same usercontextid as the old one
       params.userContextId = aTab.getAttribute("usercontextid");
     }
     let newTab = this.addWebTab("about:blank", params);
     let newBrowser = this.getBrowserForTab(newTab);
 
     aTab.parentNode._finishAnimateTabMove();
 
-    // Stop the about:blank load.
-    newBrowser.stop();
-    // Make sure it has a docshell.
-    newBrowser.docShell;
+    if (!createLazyBrowser) {
+      // Stop the about:blank load.
+      newBrowser.stop();
+      // Make sure it has a docshell.
+      newBrowser.docShell;
+    }
 
     if (!this.swapBrowsersAndCloseOther(newTab, aTab)) {
       // Swapping wasn't permitted. Bail out.
       this.removeTab(newTab);
       return null;
     }
 
     if (aSelectTab) {
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -1902,21 +1902,21 @@
       ]]></handler>
     </handlers>
   </binding>
 
   <binding id="tabbrowser-tab"
            extends="chrome://global/content/bindings/tabbox.xml#tab">
     <content context="tabContextMenu">
       <xul:stack class="tab-stack" flex="1">
-        <xul:vbox xbl:inherits="selected=visuallyselected,fadein"
+        <xul:vbox xbl:inherits="selected=visuallyselected,fadein,multiselected"
                   class="tab-background">
           <xul:hbox xbl:inherits="selected=visuallyselected,multiselected,before-multiselected"
                     class="tab-line"/>
-          <xul:spacer flex="1"/>
+          <xul:spacer flex="1" class="tab-background-inner"/>
           <xul:hbox class="tab-bottom-line"/>
         </xul:vbox>
         <xul:hbox xbl:inherits="pinned,bursting,notselectedsinceload"
                   anonid="tab-loading-burst"
                   class="tab-loading-burst"/>
         <xul:hbox xbl:inherits="pinned,selected=visuallyselected,titlechanged,attention"
                   class="tab-content" align="center">
           <xul:hbox xbl:inherits="fadein,pinned,busy,progress,selected=visuallyselected"
--- a/browser/base/content/test/plugins/browser_pluginnotification.js
+++ b/browser/base/content/test/plugins/browser_pluginnotification.js
@@ -344,16 +344,20 @@ add_task(async function() {
 
   await ContentTask.spawn(gTestBrowser, null, async function() {
     let doc = content.document;
     let div = doc.getElementById("container");
     div.style.display = "block";
   });
 
   await ContentTask.spawn(gTestBrowser, null, async function() {
+    // Waiting for layout to flush and the overlay layout to compute
+    await new Promise(resolve => content.requestAnimationFrame(resolve));
+    await new Promise(resolve => content.requestAnimationFrame(resolve));
+
     let doc = content.document;
     let plugin = doc.getElementById("test");
     let mainBox = plugin.openOrClosedShadowRoot.getElementById("main");
     let overlayRect = mainBox.getBoundingClientRect();
     Assert.ok(overlayRect.width == 200 && overlayRect.height == 200,
       "Test 20c, plugin should have overlay dims of 200px");
   });
 
--- a/browser/base/content/test/tabs/browser_multiselect_tabs_move_to_new_window_contextmenu.js
+++ b/browser/base/content/test/tabs/browser_multiselect_tabs_move_to_new_window_contextmenu.js
@@ -39,8 +39,57 @@ add_task(async function test() {
   is(gBrowser.visibleTabs.length, 2, "Two tabs now in the old window");
   is(gBrowser2.visibleTabs.length, 3, "Three tabs in the new window");
   is(gBrowser2.visibleTabs.indexOf(gBrowser2.selectedTab), 1,
     "Previously active tab is still the active tab in the new window");
 
   BrowserTestUtils.closeWindow(newWindow);
   BrowserTestUtils.removeTab(tab4);
 });
+
+add_task(async function testLazyTabs() {
+  let params = {createLazyBrowser: true};
+  let tabs = [];
+  let numTabs = 4;
+  for (let i = 0; i < numTabs; ++i) {
+    tabs.push(BrowserTestUtils.addTab(gBrowser, `http://example.com/?${i}`, params));
+  }
+
+  await BrowserTestUtils.switchTab(gBrowser, tabs[0]);
+  for (let i = 1; i < numTabs; ++i) {
+    await triggerClickOn(tabs[i], { ctrlKey: true });
+  }
+
+  isnot(tabs[0].linkedPanel, "", `Tab 0 shouldn't be lazy`);
+  for (let i = 1; i < numTabs; ++i) {
+    is(tabs[i].linkedPanel, "", `Tab ${i} should be lazy`);
+  }
+
+  is(gBrowser.multiSelectedTabsCount, numTabs, `${numTabs} multiselected tabs`);
+  for (let i = 0; i < numTabs; ++i) {
+    ok(tabs[i].multiselected, `Tab ${i} should be multiselected`);
+  }
+
+  let newWindow = gBrowser.replaceTabsWithWindow(tabs[0]);
+
+  await TestUtils.waitForCondition(() => newWindow.gBrowser, `Wait for gBrowser`);
+  await TestUtils.waitForCondition(() => newWindow.gBrowser.visibleTabs.length == numTabs,
+    `Wait for all ${numTabs} tabs to get moved to the new window`);
+  tabs = newWindow.gBrowser.tabs;
+
+  isnot(tabs[0].linkedPanel, "", `Tab 0 should continue not being lazy`);
+  // FIXME: bug 1521923 - First inactive tab to be moved into another window loses laziness
+  todo_is(tabs[1].linkedPanel, "", `Tab 1 should continue being lazy`);
+  for (let i = 2; i < numTabs; ++i) {
+    is(tabs[i].linkedPanel, "", `Tab ${i} should continue being lazy`);
+  }
+
+  is(tabs[0].linkedBrowser.currentURI.spec, `http://example.com/?0`,
+    `Tab 0 should have the right URL`);
+  todo_is(SessionStore.getLazyTabValue(tabs[1], "url"), `http://example.com/?1`,
+    `Tab 1 should have the right lazy URL`);
+  for (let i = 2; i < numTabs; ++i) {
+    is(SessionStore.getLazyTabValue(tabs[i], "url"), `http://example.com/?${i}`,
+      `Tab ${i} should have the right lazy URL`);
+  }
+
+  BrowserTestUtils.closeWindow(newWindow);
+});
--- a/browser/base/content/test/tabs/browser_multiselect_tabs_reopen_in_container.js
+++ b/browser/base/content/test/tabs/browser_multiselect_tabs_reopen_in_container.js
@@ -51,17 +51,17 @@ add_task(async function testReopen() {
   await SpecialPowers.pushPrefEnv({"set": [
       [PREF_PRIVACY_USER_CONTEXT_ENABLED, true],
       [PREF_MULTISELECT_TABS, true],
   ]});
 
   let tab1 = await addTab("http://mochi.test:8888/1");
   let tab2 = await addTab("http://mochi.test:8888/2");
   let tab3 = await addTab("http://mochi.test:8888/3");
-  let tab4 = await addTab("http://mochi.test:8888/3", {createLazyBrowser: true});
+  let tab4 = BrowserTestUtils.addTab(gBrowser, "http://mochi.test:8888/3", {createLazyBrowser: true});
 
   await BrowserTestUtils.switchTab(gBrowser, tab1);
 
   await triggerClickOn(tab2, { ctrlKey: true });
   await triggerClickOn(tab4, { ctrlKey: true });
 
   for (let tab of [tab1, tab2, tab3, tab4]) {
     ok(!tab.hasAttribute("usercontextid"),
--- a/browser/components/extensions/parent/ext-find.js
+++ b/browser/components/extensions/parent/ext-find.js
@@ -1,30 +1,41 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 /* global tabTracker */
 "use strict";
 
+ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
+                               "resource://gre/modules/PrivateBrowsingUtils.jsm");
+
+var {
+  ExtensionError,
+} = ExtensionUtils;
+
 /**
  * runFindOperation
  * Utility for `find` and `highlightResults`.
  *
+ * @param {BaseContext} context - context the find operation runs in.
  * @param {object} params - params to pass to message sender.
  * @param {string} message - identifying component of message name.
  *
  * @returns {Promise} a promise that will be resolved or rejected based on the
  *          data received by the message listener.
  */
-function runFindOperation(params, message) {
+function runFindOperation(context, params, message) {
   let {tabId} = params;
   let tab = tabId ? tabTracker.getTab(tabId) : tabTracker.activeTab;
   let browser = tab.linkedBrowser;
   let mm = browser.messageManager;
   tabId = tabId || tabTracker.getId(tab);
-
+  if (!context.privateBrowsingAllowed &&
+      PrivateBrowsingUtils.isBrowserPrivate(browser)) {
+    return Promise.reject({message: `Unable to search: ${tabId}`});
+  }
   // We disallow find in about: urls.
   if (tab.linkedBrowser.contentPrincipal.isSystemPrincipal ||
       (["about", "chrome", "resource"].includes(tab.linkedBrowser.currentURI.scheme) &&
       tab.linkedBrowser.currentURI.spec != "about:blank")) {
     return Promise.reject({message: `Unable to search: ${tabId}`});
   }
 
   return new Promise((resolve, reject) => {
@@ -68,17 +79,17 @@ this.find = class extends ExtensionAPI {
          * @returns {object} data received by the message listener that includes:
          *   {number} count - number of results found.
          *   {array} rangeData (if opted) - serialized representation of ranges found.
          *   {array} rectData (if opted) - rect data of ranges found.
          */
         find(queryphrase, params) {
           params = params || {};
           params.queryphrase = queryphrase;
-          return runFindOperation(params, "CollectResults");
+          return runFindOperation(context, params, "CollectResults");
         },
 
         /**
          * browser.find.highlightResults
          * Highlights range(s) found in previous browser.find.find.
          *
          * @param {object} params optional - may contain any of the following properties,
          *   all of which are optional:
@@ -88,26 +99,29 @@ this.find = class extends ExtensionAPI {
          *
          * @returns {string} - data received by the message listener that may be:
          *   "Success" - Highlighting succeeded.
          *   "OutOfRange" - The index supplied was out of range.
          *   "NoResults" - There were no search results to highlight.
          */
         highlightResults(params) {
           params = params || {};
-          return runFindOperation(params, "HighlightResults");
+          return runFindOperation(context, params, "HighlightResults");
         },
 
         /**
          * browser.find.removeHighlighting
          * Removes all highlighting from previous search.
          *
          * @param {number} tabId optional
          *                 Tab to clear highlighting in.  Defaults to the active tab.
          */
         removeHighlighting(tabId) {
           let tab = tabId ? tabTracker.getTab(tabId) : tabTracker.activeTab;
+          if (!context.privateBrowsingAllowed && PrivateBrowsingUtils.isBrowserPrivate(tab.linkedBrowser)) {
+            throw new ExtensionError(`Invalid tab ID: ${tabId}`);
+          }
           tab.linkedBrowser.messageManager.sendAsyncMessage("ext-Finder:clearHighlighting");
         },
       },
     };
   }
 };
--- a/browser/components/extensions/parent/ext-tabs.js
+++ b/browser/components/extensions/parent/ext-tabs.js
@@ -9,18 +9,16 @@ ChromeUtils.defineModuleGetter(this, "Ex
 ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
                                "resource://gre/modules/PrivateBrowsingUtils.jsm");
 ChromeUtils.defineModuleGetter(this, "PromiseUtils",
                                "resource://gre/modules/PromiseUtils.jsm");
 ChromeUtils.defineModuleGetter(this, "Services",
                                "resource://gre/modules/Services.jsm");
 ChromeUtils.defineModuleGetter(this, "SessionStore",
                                "resource:///modules/sessionstore/SessionStore.jsm");
-ChromeUtils.defineModuleGetter(this, "Utils",
-                               "resource://gre/modules/sessionstore/Utils.jsm");
 
 XPCOMUtils.defineLazyGetter(this, "strBundle", function() {
   return Services.strings.createBundle("chrome://global/locale/extensions.properties");
 });
 
 var {
   ExtensionError,
 } = ExtensionUtils;
@@ -629,25 +627,16 @@ this.tabs = class extends ExtensionAPI {
               }
               options.createLazyBrowser = true;
             } else if (createProperties.title) {
               return Promise.reject({message: `Title may only be set for discarded tabs.`});
             }
 
             options.triggeringPrincipal = principal;
             let nativeTab = window.gBrowser.addTab(url, options);
-            if (createProperties.discarded) {
-              SessionStore.setTabState(nativeTab, {
-                entries: [{
-                  url: url,
-                  title: options.title,
-                  triggeringPrincipal_base64: Utils.serializePrincipal(principal),
-                }],
-              });
-            }
 
             if (active) {
               window.gBrowser.selectedTab = nativeTab;
               if (!createProperties.url) {
                 window.focusAndSelectUrlBar();
               }
             }
 
--- a/browser/components/extensions/test/browser/browser_ext_find.js
+++ b/browser/components/extensions/test/browser/browser_ext_find.js
@@ -28,17 +28,17 @@ function waitForMessage(messageManager, 
   return new Promise(resolve => {
     messageManager.addMessageListener(topic, function messageListener(message) {
       messageManager.removeMessageListener(topic, messageListener);
       resolve(message);
     });
   });
 }
 
-add_task(async function testDuplicatePinnedTab() {
+add_task(async function testFind() {
   async function background() {
     function awaitLoad(tabId, url) {
       return new Promise(resolve => {
         browser.tabs.onUpdated.addListener(function listener(tabId_, changed, tab) {
           if (tabId == tabId_ && changed.status == "complete" && tab.url == url) {
             browser.tabs.onUpdated.removeListener(listener);
             resolve();
           }
@@ -172,8 +172,77 @@ add_task(async function testAboutFind() 
     background,
   });
 
   await extension.startup();
   await extension.awaitMessage("done");
   await extension.unload();
   BrowserTestUtils.removeTab(tab);
 });
+
+add_task(async function testIncognitoFind() {
+  await SpecialPowers.pushPrefEnv({set: [["extensions.allowPrivateBrowsingByDefault", false]]});
+
+  async function background() {
+    await browser.test.assertRejects(
+      browser.find.find("banana"),
+      /Unable to search:/,
+      "Should not be able to search private window");
+    await browser.test.assertRejects(
+      browser.find.highlightResults(),
+      /Unable to search:/,
+      "Should not be able to highlight in private window");
+    await browser.test.assertRejects(
+      browser.find.removeHighlighting(),
+      /Invalid tab ID:/,
+      "Should not be able to remove highlight in private window");
+
+    browser.test.sendMessage("done");
+  }
+
+  let privateWin = await BrowserTestUtils.openNewBrowserWindow({private: true});
+  await BrowserTestUtils.loadURI(privateWin.gBrowser.selectedBrowser, "http://example.com");
+  await BrowserTestUtils.browserLoaded(privateWin.gBrowser.selectedBrowser);
+
+  let extension = ExtensionTestUtils.loadExtension({
+    manifest: {
+      "permissions": ["find", "tabs"],
+    },
+    background,
+  });
+
+  await extension.startup();
+  await extension.awaitMessage("done");
+  await extension.unload();
+  await BrowserTestUtils.closeWindow(privateWin);
+});
+
+add_task(async function testIncognitoFindAllowed() {
+  await SpecialPowers.pushPrefEnv({set: [["extensions.allowPrivateBrowsingByDefault", false]]});
+
+  // We're only testing we can make the calls in a private window,
+  // testFind above tests full functionality.
+  async function background() {
+    await browser.find.find("banana");
+    await browser.find.highlightResults({rangeIndex: 0});
+    await browser.find.removeHighlighting();
+
+    browser.test.sendMessage("done");
+  }
+
+  let url = "http://example.com/browser/browser/components/extensions/test/browser/file_find_frames.html";
+  let privateWin = await BrowserTestUtils.openNewBrowserWindow({private: true});
+  await BrowserTestUtils.loadURI(privateWin.gBrowser.selectedBrowser, url);
+  await BrowserTestUtils.browserLoaded(privateWin.gBrowser.selectedBrowser);
+
+  let extension = ExtensionTestUtils.loadExtension({
+    manifest: {
+      "permissions": ["find", "tabs"],
+    },
+    background,
+    incognitoOverride: "spanning",
+  });
+
+  await extension.startup();
+  await extension.awaitMessage("done");
+  await extension.unload();
+  await BrowserTestUtils.closeWindow(privateWin);
+});
--- a/browser/components/urlbar/UrlbarInput.jsm
+++ b/browser/components/urlbar/UrlbarInput.jsm
@@ -55,16 +55,17 @@ class UrlbarInput {
     this.controller.setInput(this);
     this.view = new UrlbarView(this);
     this.valueIsTyped = false;
     this.userInitiatedFocus = false;
     this.isPrivate = PrivateBrowsingUtils.isWindowPrivate(this.window);
     this.lastQueryContextPromise = Promise.resolve();
     this._untrimmedValue = "";
     this._suppressStartQuery = false;
+    this._actionOverrideKeyCount = 0;
 
     // Forward textbox methods and properties.
     const METHODS = ["addEventListener", "removeEventListener",
       "setAttribute", "hasAttribute", "removeAttribute", "getAttribute",
       "select"];
     const READ_ONLY_PROPERTIES = ["inputField", "editor"];
     const READ_WRITE_PROPERTIES = ["placeholder", "readOnly",
       "selectionStart", "selectionEnd"];
@@ -629,18 +630,20 @@ class UrlbarInput {
 
   _toggleNoActions(event) {
     if (event.keyCode == KeyEvent.DOM_VK_SHIFT ||
         event.keyCode == KeyEvent.DOM_VK_ALT ||
         event.keyCode == (AppConstants.platform == "macosx" ?
                             KeyEvent.DOM_VK_META :
                             KeyEvent.DOM_VK_CONTROL)) {
       if (event.type == "keydown") {
+        this._actionOverrideKeyCount++;
         this.setAttribute("noactions", "true");
-      } else {
+      } else if (this._actionOverrideKeyCount &&
+                 --this._actionOverrideKeyCount == 0) {
         this.removeAttribute("noactions");
       }
     }
   }
 
   /**
    * Get the url to load for the search query and records in telemetry that it
    * is being loaded.
--- a/browser/components/urlbar/UrlbarView.jsm
+++ b/browser/components/urlbar/UrlbarView.jsm
@@ -309,16 +309,20 @@ class UrlbarView {
 
     let secondary = this._createElement("span");
     secondary.className = "urlbarView-secondary";
     switch (result.type) {
       case UrlbarUtils.RESULT_TYPE.TAB_SWITCH:
         secondary.classList.add("urlbarView-action");
         secondary.textContent = bundle.GetStringFromName("switchToTab2");
         break;
+      case UrlbarUtils.RESULT_TYPE.REMOTE_TAB:
+        secondary.classList.add("urlbarView-action");
+        secondary.textContent = result.payload.device;
+        break;
       case UrlbarUtils.RESULT_TYPE.SEARCH:
         secondary.classList.add("urlbarView-action");
         secondary.textContent =
           bundle.formatStringFromName("searchWithEngine",
                                       [result.payload.engine], 1);
         break;
       default:
         if (resultIndex == 0) {
--- a/browser/components/urlbar/tests/browser/browser.ini
+++ b/browser/components/urlbar/tests/browser/browser.ini
@@ -5,16 +5,18 @@
 [DEFAULT]
 prefs=browser.urlbar.quantumbar=true
 tags=quantumbar
 support-files =
   dummy_page.html
   head.js
   head-common.js
 
+[browser_canonizeURL.js]
+[browser_locationBarCommand.js]
 [browser_locationBarExternalLoad.js]
 [browser_moz_action_link.js]
 [browser_populateAfterPushState.js]
 [browser_redirect_error.js]
 support-files = redirect_error.sjs
 [browser_switchToTabHavingURI_aOpenParams.js]
 [browser_urlbar_blanking.js]
 support-files =
@@ -27,24 +29,26 @@ support-files =
 run-if = e10s
 [browser_urlbar_searchsettings.js]
 [browser_urlbar_speculative_connect.js]
 support-files =
   searchSuggestionEngine2.xml
   searchSuggestionEngine.sjs
 [browser_urlbar_speculative_connect_not_with_client_cert.js]
 [browser_urlbar_whereToOpen.js]
+[browser_urlbarAboutHomeLoading.js]
 [browser_urlbarCopying.js]
 subsuite = clipboard
 support-files =
   authenticate.sjs
 [browser_urlbarCutting.js]
 [browser_urlbarEnter.js]
 [browser_urlbarFocusedCmdK.js]
 [browser_urlbarHashChangeProxyState.js]
+[browser_UrlbarInput_autofill.js]
 [browser_UrlbarInput_formatValue.js]
 [browser_UrlbarInput_hiddenFocus.js]
 [browser_UrlbarInput_overflow.js]
 [browser_UrlbarInput_tooltip.js]
 [browser_UrlbarInput_trimURLs.js]
 subsuite = clipboard
 [browser_UrlbarInput_unit.js]
 support-files = empty.xul
@@ -52,15 +56,16 @@ support-files = empty.xul
 [browser_urlbarPlaceholder.js]
 support-files =
   searchSuggestionEngine.xml
   searchSuggestionEngine.sjs
 [browser_urlbarRevert.js]
 [browser_urlbarSearchSingleWordNotification.js]
 [browser_URLBarSetURI.js]
 skip-if = (os == "linux" || os == "mac") && debug # bug 970052, bug 970053
+[browser_urlbarStop.js]
 [browser_urlbarUpdateForDomainCompletion.js]
 [browser_userTypedValue.js]
 support-files = file_userTypedValue.html
 [browser_wyciwyg_urlbarCopying.js]
 subsuite = clipboard
 support-files =
   test_wyciwyg_copying.html
new file mode 100644
--- /dev/null
+++ b/browser/components/urlbar/tests/browser/browser_UrlbarInput_autofill.js
@@ -0,0 +1,35 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests the UrlbarInput.autofill() method.  It needs to be tested in the actual
+ * browser because UrlbarInput relies on the trimURL() function defined in the
+ * browser window's global scope, and this test hits the path that calls it.
+ */
+
+"use strict";
+
+add_task(async function test() {
+  let tests = [
+    // [initial value, autofill value, expected selected substring in new value]
+    ["foo", "foobar", "bar"],
+    ["FOO", "foobar", "bar"],
+    ["fOo", "foobar", "bar"],
+    ["foo", "quuxbar", ""],
+    ["FOO", "quuxbar", ""],
+    ["fOo", "quuxbar", ""],
+  ];
+
+  gURLBar.focus();
+  for (let [initial, autofill, expectedSelected] of tests) {
+    gURLBar.value = initial;
+    gURLBar.autofill(autofill);
+    let expectedValue = initial + expectedSelected;
+    Assert.equal(gURLBar.value, expectedValue,
+      "The input value should be correct");
+    Assert.equal(gURLBar.selectionStart, initial.length,
+      "The start of the selection should be correct");
+    Assert.equal(gURLBar.selectionEnd, expectedValue.length,
+      "The end of the selection should be correct");
+  }
+});
--- a/browser/components/urlbar/tests/browser/browser_UrlbarInput_unit.js
+++ b/browser/components/urlbar/tests/browser/browser_UrlbarInput_unit.js
@@ -4,19 +4,16 @@
 /**
  * These tests unit test the functionality of UrlbarController by stubbing out the
  * model and providing stubs to be called.
  */
 
 "use strict";
 
 let fakeController;
-let generalListener;
-let input;
-let inputOptions;
 
 /**
  * Asserts that the query context has the expected values.
  *
  * @param {UrlbarQueryContext} context
  * @param {object} expectedValues The expected values for the UrlbarQueryContext.
  */
 function assertContextMatches(context, expectedValues) {
@@ -32,105 +29,169 @@ function assertContextMatches(context, e
 /**
  * Checks the result of a startQuery call on the controller.
  *
  * @param {object} stub The sinon stub that should have been called with the
  *                      UrlbarQueryContext.
  * @param {object} expectedQueryContextProps
  *                   An object consisting of name/value pairs to check against the
  *                   UrlbarQueryContext properties.
+ * @param {number} callIndex The expected zero-based index of the call.  Useful
+ *                           when startQuery is called multiple times.
  */
-function checkStartQueryCall(stub, expectedQueryContextProps) {
-  Assert.equal(stub.callCount, 1,
+function checkStartQueryCall(stub, expectedQueryContextProps, callIndex = 0) {
+  Assert.equal(stub.callCount, callIndex + 1,
     "Should have called startQuery on the controller");
 
-  let args = stub.args[0];
+  let args = stub.args[callIndex];
   Assert.equal(args.length, 1,
     "Should have called startQuery with one argument");
 
   let queryContext = args[0];
   Assert.ok(queryContext instanceof UrlbarQueryContext,
     "Should have been passed a UrlbarQueryContext");
 
   for (let [name, value] of Object.entries(expectedQueryContextProps)) {
     Assert.deepEqual(queryContext[name],
      value, `Should have the correct value for queryContext.${name}`);
   }
 }
 
-add_task(async function setup() {
-  sandbox = sinon.sandbox.create();
-
-  fakeController = new UrlbarController({
-    browserWindow: window,
-  });
-
-  sandbox.stub(fakeController, "startQuery");
-  sandbox.stub(PrivateBrowsingUtils, "isWindowPrivate").returns(false);
-
+/**
+ * Opens a new empty chrome window and clones the textbox and panel into it.
+ *
+ * @param {function} callback Called after the window is opened and loaded.
+ *                   It's passed a new UrlbarInput object.
+ */
+async function withNewWindow(callback) {
   // Open a new window, so we don't affect other tests by adding extra
   // UrbarInput wrappers around the urlbar.
   let gTestRoot = getRootDirectory(gTestPath);
 
   let win = window.openDialog(gTestRoot + "empty.xul",
                     "", "chrome");
   await BrowserTestUtils.waitForEvent(win, "load");
 
   win.gBrowser = {};
 
-  registerCleanupFunction(async () => {
-    await BrowserTestUtils.closeWindow(win);
-    sandbox.restore();
-  });
-
   // Clone the elements into the new window, so we get exact copies without having
   // to replicate the xul.
   let doc = win.document;
   let textbox = doc.importNode(document.getElementById("urlbar"), true);
   doc.documentElement.appendChild(textbox);
   let panel = doc.importNode(document.getElementById("urlbar-results"), true);
   doc.documentElement.appendChild(panel);
 
-  inputOptions = {
+  let inputOptions = {
     textbox,
     panel,
     controller: fakeController,
   };
 
-  input = new UrlbarInput(inputOptions);
+  let input = new UrlbarInput(inputOptions);
+
+  await callback(input);
+
+  await BrowserTestUtils.closeWindow(win);
+
+  // Clean up a bunch of things so we don't leak `win`.
+  input.textbox = null;
+  input.panel = null;
+  input.window = null;
+  input.document = null;
+  input.view = null;
+
+  Assert.ok(fakeController.view);
+  fakeController.removeQueryListener(fakeController.view);
+  fakeController.view = null;
+}
+
+add_task(async function setup() {
+  sandbox = sinon.sandbox.create();
+
+  fakeController = new UrlbarController({
+    browserWindow: window,
+  });
+
+  sandbox.stub(fakeController, "startQuery");
+  sandbox.stub(PrivateBrowsingUtils, "isWindowPrivate").returns(false);
+
+  registerCleanupFunction(async () => {
+    sandbox.restore();
+  });
 });
 
-add_task(function test_input_starts_query() {
-  input.inputField.value = "search";
-  input.handleEvent({
-    target: input.inputField,
-    type: "input",
-  });
+add_task(async function test_input_starts_query() {
+  await withNewWindow(input => {
+    input.inputField.value = "search";
+    input.handleEvent({
+      target: input.inputField,
+      type: "input",
+    });
 
-  checkStartQueryCall(fakeController.startQuery, {
-    searchString: "search",
-    isPrivate: false,
-    maxResults: UrlbarPrefs.get("maxRichResults"),
+    checkStartQueryCall(fakeController.startQuery, {
+      searchString: "search",
+      isPrivate: false,
+      maxResults: UrlbarPrefs.get("maxRichResults"),
+    });
+
+    sandbox.resetHistory();
   });
-
-  sandbox.resetHistory();
 });
 
-add_task(function test_input_with_private_browsing() {
+add_task(async function test_input_with_private_browsing() {
   PrivateBrowsingUtils.isWindowPrivate.returns(true);
 
-  // Rather than using the global input here, we create a new instance which
-  // will use the updated return value of the private browsing stub.
-  let privateInput = new UrlbarInput(inputOptions);
+  await withNewWindow(privateInput => {
+    privateInput.inputField.value = "search";
+    privateInput.handleEvent({
+      target: privateInput.inputField,
+      type: "input",
+    });
 
-  privateInput.inputField.value = "search";
-  privateInput.handleEvent({
-    target: privateInput.inputField,
-    type: "input",
+    checkStartQueryCall(fakeController.startQuery, {
+      searchString: "search",
+      isPrivate: true,
+    });
+
+    sandbox.resetHistory();
   });
+});
 
-  checkStartQueryCall(fakeController.startQuery, {
-    searchString: "search",
-    isPrivate: true,
+add_task(async function test_autofill_disabled_on_prefix_search() {
+  await withNewWindow(input => {
+    // search for "autofill" -- autofill should be enabled
+    input.inputField.value = "autofill";
+    input.handleEvent({
+      target: input.inputField,
+      type: "input",
+    });
+    checkStartQueryCall(fakeController.startQuery, {
+      searchString: "autofill",
+      enableAutofill: true,
+    });
+
+    // search for "auto" -- autofill should be disabled since the previous
+    // search string starts with the new search string
+    input.inputField.value = "auto";
+    input.handleEvent({
+      target: input.inputField,
+      type: "input",
+    });
+    checkStartQueryCall(fakeController.startQuery, {
+      searchString: "auto",
+      enableAutofill: false,
+    }, 1);
+
+    // search for "autofill" again -- autofill should be enabled
+    input.inputField.value = "autofill";
+    input.handleEvent({
+      target: input.inputField,
+      type: "input",
+    });
+    checkStartQueryCall(fakeController.startQuery, {
+      searchString: "autofill",
+      enableAutofill: true,
+    }, 2);
+
+    sandbox.resetHistory();
   });
-
-  sandbox.resetHistory();
 });
rename from browser/components/urlbar/tests/legacy/browser_canonizeURL.js
rename to browser/components/urlbar/tests/browser/browser_canonizeURL.js
--- a/browser/components/urlbar/tests/legacy/browser_canonizeURL.js
+++ b/browser/components/urlbar/tests/browser/browser_canonizeURL.js
@@ -1,11 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
+/**
+ * Tests turning non-url-looking values typed in the input field into proper URLs.
+ */
+
 const TEST_ENGINE_BASENAME = "searchSuggestionEngine.xml";
 
 add_task(async function checkCtrlWorks() {
   let testcases = [
     ["example", "http://www.example.com/", { ctrlKey: true }],
     // Check that a direct load is not overwritten by a previous canonization.
     ["http://example.com/test/", "http://example.com/test/", {}],
 
rename from browser/components/urlbar/tests/legacy/browser_locationBarCommand.js
rename to browser/components/urlbar/tests/browser/browser_locationBarCommand.js
--- a/browser/components/urlbar/tests/legacy/browser_locationBarCommand.js
+++ b/browser/components/urlbar/tests/browser/browser_locationBarCommand.js
@@ -1,11 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
+/**
+ * This test is designed to ensure that the correct command/operation happens
+ * when pressing enter with various key combinations in the urlbar.
+ */
+
 const TEST_VALUE = "example.com";
 const START_VALUE = "example.org";
 
 add_task(async function setup() {
   await SpecialPowers.pushPrefEnv({
     set: [["browser.altClickSave", true],
           ["browser.urlbar.autoFill", false]],
   });
rename from browser/components/urlbar/tests/legacy/browser_urlbarAboutHomeLoading.js
rename to browser/components/urlbar/tests/browser/browser_urlbarAboutHomeLoading.js
--- a/browser/components/urlbar/tests/legacy/browser_urlbarAboutHomeLoading.js
+++ b/browser/components/urlbar/tests/browser/browser_urlbarAboutHomeLoading.js
@@ -1,8 +1,15 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * This tests ensures the urlbar is cleared properly when about:home is visited.
+ */
+
 "use strict";
 
 const {SessionSaver} = ChromeUtils.import("resource:///modules/sessionstore/SessionSaver.jsm");
 const {TabStateFlusher} = ChromeUtils.import("resource:///modules/sessionstore/TabStateFlusher.jsm");
 
 /**
  * Test what happens if loading a URL that should clear the
  * location bar after a parent process URL.
rename from browser/components/urlbar/tests/legacy/browser_urlbarStop.js
rename to browser/components/urlbar/tests/browser/browser_urlbarStop.js
--- a/browser/components/urlbar/tests/legacy/browser_urlbarStop.js
+++ b/browser/components/urlbar/tests/browser/browser_urlbarStop.js
@@ -1,8 +1,16 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * This tests ensures the urlbar reflects the correct value if a page load is
+ * stopped immediately after loading.
+ */
+
 "use strict";
 
 const goodURL = "http://mochi.test:8888/";
 const badURL = "http://mochi.test:8888/whatever.html";
 
 add_task(async function() {
   gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, goodURL);
   await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
--- a/browser/components/urlbar/tests/legacy/browser.ini
+++ b/browser/components/urlbar/tests/legacy/browser.ini
@@ -25,20 +25,18 @@ skip-if = (verify && !debug && (os == 'w
 [browser_autocomplete_cursor.js]
 skip-if = verify
 [browser_autocomplete_edit_completed.js]
 [browser_autocomplete_enter_race.js]
 [browser_autocomplete_no_title.js]
 [browser_autocomplete_readline_navigation.js]
 skip-if = os != "mac" # Mac only feature
 [browser_autocomplete_tag_star_visibility.js]
-[browser_canonizeURL.js]
 [browser_dragdropURL.js]
 [browser_keyword_select_and_type.js]
-[browser_locationBarCommand.js]
 [browser_new_tab_urlbar_reset.js]
 [browser_pasteAndGo.js]
 subsuite = clipboard
 [browser_remotetab.js]
 [browser_removeUnsafeProtocolsFromURLBarPaste.js]
 subsuite = clipboard
 [browser_search_favicon.js]
 [browser_switchtab_copy.js]
@@ -53,17 +51,16 @@ support-files =
 [browser_tabMatchesInAwesomebar_perwindowpb.js]
 skip-if = os == 'linux' # Bug 1104755
 [browser_urlbarAddonIframe.js]
 support-files =
   ../browser/Panel.jsm
   ../browser/urlbarAddonIframe.html
   ../browser/urlbarAddonIframe.js
   ../browser/urlbarAddonIframeContentScript.js
-[browser_urlbarAboutHomeLoading.js]
 [browser_urlbarAutofillPreserveCase.js]
 [browser_urlbarAutoFillTrimURLs.js]
 [browser_urlbarDecode.js]
 [browser_urlbarDelete.js]
 [browser_urlbarEnterAfterMouseOver.js]
 skip-if = os == "linux" # Bug 1073339 - Investigate autocomplete test unreliability on Linux/e10s
 [browser_urlbarKeepStateAcrossTabSwitches.js]
 [browser_urlbarOneOffs.js]
@@ -89,17 +86,16 @@ support-files =
 [browser_urlbarSearchSuggestions_opt-out.js]
 support-files =
   ../browser/searchSuggestionEngine.xml
   ../browser/searchSuggestionEngine.sjs
 [browser_urlbarSearchTelemetry.js]
 support-files =
   ../browser/searchSuggestionEngine.xml
   ../browser/searchSuggestionEngine.sjs
-[browser_urlbarStop.js]
 [browser_urlbarTokenAlias.js]
 [browser_urlbar_autoFill_backspaced.js]
 [browser_urlbar_canonize_on_autofill.js]
 [browser_urlbar_remove_match.js]
 [browser_urlbar_stop_pending.js]
 support-files =
   ../browser/slow-page.sjs
 [browser_urlbarStopSearchOnSelection.js]
@@ -108,24 +104,27 @@ support-files =
   ../browser/searchSuggestionEngine.sjs
 [browser_urlbarValueOnTabSwitch.js]
 
 
 # These are tests that are already running with QuantumBar, but we want to run them
 # against both the legacy urlbar and the new QuantumBar. The references in this
 # directory will run them against the old urlbar as per the pref above.
 
+[../browser/browser_canonizeURL.js]
 [../browser/browser_URLBarSetURI.js]
 skip-if = (os == "linux" || os == "mac") && debug # bug 970052, bug 970053
+[../browser/browser_locationBarCommand.js]
 [../browser/browser_locationBarExternalLoad.js]
 [../browser/browser_moz_action_link.js]
 [../browser/browser_populateAfterPushState.js]
 [../browser/browser_redirect_error.js]
 support-files = ../browser/redirect_error.sjs
 [../browser/browser_switchToTabHavingURI_aOpenParams.js]
+[../browser/browser_urlbarAboutHomeLoading.js]
 [../browser/browser_urlbarCopying.js]
 subsuite = clipboard
 support-files =
   ../browser/authenticate.sjs
 [../browser/browser_urlbarCutting.js]
 [../browser/browser_urlbar_blanking.js]
 support-files =
   ../browser/file_blank_but_not_blank.html
@@ -138,16 +137,17 @@ support-files =
 [../browser/browser_urlbarFocusedCmdK.js]
 [../browser/browser_urlbarHashChangeProxyState.js]
 [../browser/browser_urlbarPlaceholder.js]
 support-files =
   ../browser/searchSuggestionEngine.xml
   ../browser/searchSuggestionEngine.sjs
 [../browser/browser_urlbarRevert.js]
 [../browser/browser_urlbarSearchSingleWordNotification.js]
+[../browser/browser_urlbarStop.js]
 [../browser/browser_urlbarUpdateForDomainCompletion.js]
 [../browser/browser_urlbar_searchsettings.js]
 [../browser/browser_urlbar_speculative_connect.js]
 support-files =
   ../browser/searchSuggestionEngine2.xml
   ../browser/searchSuggestionEngine.sjs
 [../browser/browser_urlbar_speculative_connect_not_with_client_cert.js]
 [../browser/browser_urlbar_remoteness_switch.js]
--- a/browser/config/mozconfigs/win32/rusttests
+++ b/browser/config/mozconfigs/win32/rusttests
@@ -1,5 +1,7 @@
 MOZ_AUTOMATION_BUILD_SYMBOLS=0
 MOZ_AUTOMATION_PACKAGE_TESTS=0
 MOZ_AUTOMATION_L10N_CHECK=0
 
 . "$topsrcdir/browser/config/mozconfigs/win32/nightly"
+
+unset ENABLE_CLANG_PLUGIN
--- a/browser/config/mozconfigs/win32/rusttests-debug
+++ b/browser/config/mozconfigs/win32/rusttests-debug
@@ -1,5 +1,7 @@
 MOZ_AUTOMATION_BUILD_SYMBOLS=0
 MOZ_AUTOMATION_PACKAGE_TESTS=0
 MOZ_AUTOMATION_L10N_CHECK=0
 
 . "$topsrcdir/browser/config/mozconfigs/win32/debug"
+
+unset ENABLE_CLANG_PLUGIN
--- a/browser/config/mozconfigs/win64/rusttests
+++ b/browser/config/mozconfigs/win64/rusttests
@@ -1,5 +1,7 @@
 MOZ_AUTOMATION_BUILD_SYMBOLS=0
 MOZ_AUTOMATION_PACKAGE_TESTS=0
 MOZ_AUTOMATION_L10N_CHECK=0
 
 . "$topsrcdir/browser/config/mozconfigs/win64/nightly"
+
+unset ENABLE_CLANG_PLUGIN
--- a/browser/config/mozconfigs/win64/rusttests-debug
+++ b/browser/config/mozconfigs/win64/rusttests-debug
@@ -1,5 +1,7 @@
 MOZ_AUTOMATION_BUILD_SYMBOLS=0
 MOZ_AUTOMATION_PACKAGE_TESTS=0
 MOZ_AUTOMATION_L10N_CHECK=0
 
 . "$topsrcdir/browser/config/mozconfigs/win64/debug"
+
+unset ENABLE_CLANG_PLUGIN
--- a/browser/themes/shared/tabs.inc.css
+++ b/browser/themes/shared/tabs.inc.css
@@ -452,21 +452,22 @@
   opacity: 0;
 }
 
 .tabbrowser-arrowscrollbox > .arrowscrollbox-overflow-start-indicator,
 .tabbrowser-arrowscrollbox > .arrowscrollbox-overflow-end-indicator {
   transition: opacity 150ms ease;
 }
 
-.tabbrowser-tab:not([visuallyselected=true]),
+.tabbrowser-tab:not([visuallyselected=true]):not([multiselected]),
 .tabbrowser-tab:-moz-lwtheme {
   color: inherit;
 }
 
+.tabbrowser-tab[multiselected]:-moz-lwtheme,
 .tabbrowser-tab[visuallyselected=true]:-moz-lwtheme {
   color: var(--lwt-tab-text, var(--toolbar-color));
 }
 
 .tab-line {
   height: 2px;
 }
 
@@ -488,52 +489,60 @@
 %ifdef MENUBAR_CAN_AUTOHIDE
 #toolbar-menubar:not([autohide=true]) + #TabsToolbar .tabbrowser-tab > .tab-stack > .tab-background,
 %endif
 :root:not([tabsintitlebar]) .tab-background,
 :root[extradragspace] .tab-background {
   border-top-style: solid;
 }
 
+.tab-background[multiselected=true],
 .tab-background[selected=true] {
   border-top-color: var(--tabs-border-color);
   background-color: var(--toolbar-bgcolor);
   background-image: var(--toolbar-bgimage);
   background-repeat: repeat-x;
 }
 
+/* Add a translucent color (current text color at .2 alpha) on top of multiselected tabs */
+.tab-background[multiselected=true]:not([selected=true]) > .tab-background-inner {
+  background: currentColor;
+  opacity: .2;
+}
+
 .tab-line[multiselected],
 .tab-line[selected=true] {
   background-color: var(--tab-line-color);
 }
 
 /*
  * LightweightThemeConsumer will set the current lightweight theme's header
  * image to the lwt-header-image variable, used in each of the following rulesets.
  */
 
 /* Lightweight theme on tabs */
+#tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background[multiselected=true]:-moz-lwtheme,
 #tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background[selected=true]:-moz-lwtheme {
   background-attachment: scroll, scroll, fixed;
   background-color: transparent;
   background-image: linear-gradient(var(--lwt-selected-tab-background-color, transparent), var(--lwt-selected-tab-background-color, transparent)),
                     linear-gradient(var(--toolbar-bgcolor), var(--toolbar-bgcolor)),
                     var(--lwt-header-image, none);
   background-position: 0 0, 0 0, right top;
   background-repeat: repeat-x, repeat-x, no-repeat;
   background-size: auto 100%, auto 100%, auto auto;
 }
 
 /* Tab hover */
 
-.tabbrowser-tab:hover > .tab-stack > .tab-background:not([selected=true]) {
+.tabbrowser-tab:hover > .tab-stack > .tab-background:not([selected=true]):not([multiselected]) {
   background-color: rgba(0,0,0,.1);
 }
 
-#TabsToolbar[brighttext] .tabbrowser-tab:hover > .tab-stack > .tab-background:not([selected=true]) {
+#TabsToolbar[brighttext] .tabbrowser-tab:hover > .tab-stack > .tab-background:not([selected=true]):not([multiselected]) {
   background-color: rgba(255,255,255,.1);
 }
 
 .tab-line:not([selected=true]):not([multiselected]) {
   opacity: 0;
   transform: scaleX(0);
   transition: transform 250ms var(--animation-easing-function), opacity 250ms var(--animation-easing-function);
 }
@@ -543,31 +552,20 @@
   opacity: 1;
   transform: none;
 }
 
 #TabsToolbar[brighttext] .tabbrowser-tab:hover > .tab-stack > .tab-background > .tab-line:not([selected=true]):not([multiselected]) {
   background-color: rgba(255,255,255,.2);
 }
 
-.tabbrowser-tab:hover > .tab-stack > .tab-background > .tab-line:not([selected=true])[multiselected],
-#TabsToolbar[brighttext] .tabbrowser-tab:hover > .tab-stack > .tab-background > .tab-line:not([selected=true])[multiselected] {
+.tabbrowser-tab:hover > .tab-stack > .tab-background > .tab-line[multiselected]:not([selected=true]) {
   opacity: 0.5;
 }
 
-/* Tab multi-selected */
-
-.tabbrowser-tab[multiselected] > .tab-stack > .tab-background:not([selected=true]) {
-  background-color: rgba(0,0,0,.1);
-}
-
-#TabsToolbar[brighttext] .tabbrowser-tab[multiselected] > .tab-stack > .tab-background:not([selected=true]) {
-  background-color: rgba(255,255,255,.1);
-}
-
 /* Pinned tabs */
 
 /* Pinned tab separators need position: absolute when positioned (during overflow). */
 #tabbrowser-tabs[positionpinnedtabs] > .tabbrowser-tab[pinned]::after {
   position: absolute;
   top: 0;
   bottom: 0;
   right: 0;
--- a/browser/themes/shared/urlbar-autocomplete.inc.css
+++ b/browser/themes/shared/urlbar-autocomplete.inc.css
@@ -101,32 +101,36 @@
 }
 
 .urlbarView-secondary::before {
   content: "\2014";
   color: var(--panel-disabled-color);
   margin: 0 .4em;
 }
 
+.urlbarView-title:empty + .urlbarView-secondary::before {
+  display: none;
+}
+
 .urlbarView-secondary {
   color: var(--urlbar-popup-action-color);
   font-size: 0.9em;
 }
 
 .urlbarView-url {
   color: var(--urlbar-popup-url-color);
 }
 
 .urlbarView-title > strong,
 .urlbarView-url > strong,
 .urlbarView-tag > strong {
   font-weight: 600;
 }
 
-.urlbarView-row[selected] > .urlbarView-row-inner > .urlbarView-title::after,
+.urlbarView-row[selected] > .urlbarView-row-inner > .urlbarView-secondary::before,
 .urlbarView-row[selected] > .urlbarView-row-inner > .urlbarView-secondary {
   color: inherit;
 }
 
 .urlbarView-tags {
   display: inline-block;
 }
 
--- a/browser/themes/shared/urlbar-searchbar.inc.css
+++ b/browser/themes/shared/urlbar-searchbar.inc.css
@@ -73,16 +73,22 @@
 }
 
 #urlbar:not(.hidden-focus):-moz-lwtheme[focused="true"],
 #navigator-toolbox .searchbar-textbox:-moz-lwtheme[focused="true"] {
   background-color: var(--lwt-toolbar-field-focus, var(--lwt-toolbar-field-background-color, white));
   color: var(--lwt-toolbar-field-focus-color, var(--lwt-toolbar-field-color, black));
 }
 
+:root[lwt-selection] .urlbar-input:-moz-lwtheme::selection,
+:root[lwt-selection] .searchbar-textbox:-moz-lwtheme::selection {
+  background-color: var(--lwt-toolbar-field-highlight, Highlight);
+  color: var(--lwt-toolbar-field-highlight-text, HighlightText);
+}
+
 :root[uidensity=compact] #urlbar,
 :root[uidensity=compact] .searchbar-textbox {
   min-height: 26px;
   margin-top: 3px;
   margin-bottom: 3px;
 }
 
 :root[uidensity=touch] #urlbar,
--- a/browser/themes/windows/browser-aero.css
+++ b/browser/themes/windows/browser-aero.css
@@ -84,18 +84,21 @@
           /* Calculated to match the opacity change of Windows Explorer
              titlebar text change for inactive windows. */
           opacity: .6;
         }
       }
 
       @media (-moz-windows-default-theme: 0) {
         :root[tabsintitlebar] {
+          -moz-appearance: -moz-win-glass;
+        }
+
+        :root[tabsintitlebar]:not(:-moz-lwtheme) {
           background-color: transparent;
-          -moz-appearance: -moz-win-glass;
         }
 
         /* On win10, if we don't set this on the entire browser container including
          * the sidebar, if the sidebar is open the accent color bleeds through in
          * the titlebar */
         :root[tabsintitlebar] #browser {
           -moz-appearance: -moz-win-exclude-glass;
         }
--- a/browser/themes/windows/compacttheme.css
+++ b/browser/themes/windows/compacttheme.css
@@ -46,19 +46,18 @@
 
     /* Keep showing the correct color inside the tabs. */
     .tabbrowser-tab {
       color: var(--lwt-text-color) !important;
     }
 
     /* Because we're forcing the tabs toolbar to be [brighttext] to
      * get white toolbar button icons, we need to manually set the
-     * correct color for the tab hover and multiselect state for the light theme. */
-    .tabbrowser-tab:hover > .tab-stack > .tab-background:not([selected=true]):-moz-lwtheme-darktext,
-    .tabbrowser-tab[multiselected] > .tab-stack > .tab-background:not([selected=true]):-moz-lwtheme-darktext {
+     * correct color for the tab hover state for the light theme. */
+    .tabbrowser-tab:hover > .tab-stack > .tab-background:not([selected=true]):not([multiselected]):-moz-lwtheme-darktext {
       background-color: rgba(0,0,0,.1) !important;
     }
     .tabbrowser-tab:hover > .tab-stack > .tab-background > .tab-line:not([selected=true]):not([multiselected]):-moz-lwtheme-darktext {
       background-color: rgba(0,0,0,.2) !important;
     }
   }
 }
 
--- a/build/autoconf/hooks.m4
+++ b/build/autoconf/hooks.m4
@@ -10,8 +10,22 @@ define([AC_INIT_PREPARE],
 test "x$prefix" = xNONE && prefix=$ac_default_prefix
 # Let make expand exec_prefix.
 test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
 ])
 
 dnl Print error messages in config.log as well as stderr
 define([AC_MSG_ERROR],
 [{ echo "configure: error: $1" 1>&2; echo "configure: error: $1" 1>&5; exit 1; }])
+
+dnl Divert AC_TRY_COMPILER to make ac_cv_prog_*_works actually cached.
+dnl This will allow to just skip the test when python configure has set
+dnl the value for us. But since ac_cv_prog_*_cross is calculated at the same
+dnl time, and has a different meaning as in python configure, we only want to
+dnl use its value to display whether a cross-compile is happening. We forbid
+dnl configure tests that would rely on ac_cv_prog_*_cross autoconf meaning
+dnl (being able to execute the product of compilation), which are already bad
+dnl for cross compiles anyways, so it's a win to get rid of them.
+define([_MOZ_AC_TRY_COMPILER], defn([AC_TRY_COMPILER]))
+define([AC_TRY_COMPILER], [AC_CACHE_VAL($2, _MOZ_AC_TRY_COMPILER($1, $2, $3))])
+
+define([AC_TRY_RUN], [m4_fatal([AC_TRY_RUN is forbidden])])
+define([AC_CHECK_FILE], [m4_fatal([AC_CHECK_FILE is forbidden])])
--- a/build/moz.configure/init.configure
+++ b/build/moz.configure/init.configure
@@ -581,16 +581,42 @@ def vcs_repository(build_env, vcs_checko
 @depends_if(vcs_repository)
 @checking('for sparse checkout')
 def vcs_sparse_checkout(repo):
     return repo.sparse_checkout_present()
 
 
 set_config('VCS_SPARSE_CHECKOUT', vcs_sparse_checkout)
 
+# The application/project to build
+# ==============================================================
+option('--enable-application', nargs=1, env='MOZ_BUILD_APP',
+       help='Application to build. Same as --enable-project.')
+
+
+@depends('--enable-application')
+def application(app):
+    if app:
+        return app
+
+
+imply_option('--enable-project', application)
+
+
+@depends(check_build_environment)
+def default_project(build_env):
+    if build_env.topobjdir.endswith('/js/src'):
+        return 'js'
+    return 'browser'
+
+
+option('--enable-project', nargs=1, default=default_project,
+       help='Project to build')
+
+
 # Host and target systems
 # ==============================================================
 option('--host', nargs=1, help='Define the system type performing the build')
 
 option('--target', nargs=1,
        help='Define the system type where the resulting executables will be '
             'used')
 
@@ -787,26 +813,36 @@ def real_host(value, shell):
         host = config_sub(shell, value[0])
 
     return split_triplet(host)
 
 
 host = help_host_target | real_host
 
 
-@depends('--target', real_host, shell)
+@depends('--target', real_host, shell, '--enable-project')
 @checking('for target system type', lambda t: t.alias)
-def real_target(value, host, shell):
+def real_target(value, host, shell, project):
+    if project:
+        project = project[0]
     if not value:
+        if project == 'mobile/android':
+            return split_triplet('armv7a-unknown-linux-androideabi')
         return host
     # If --target was only given a cpu arch, expand it with the
-    # non-cpu part of the host.
+    # non-cpu part of the host. For mobile/android, expand it with
+    # unknown-linux-android.
     target = value[0]
     if '-' not in target:
-        cpu, rest = host.alias.split('-', 1)
+        if project == 'mobile/android':
+            rest = 'unknown-linux-android'
+            if target.startswith('arm'):
+                rest += 'eabi'
+        else:
+            cpu, rest = host.alias.split('-', 1)
         return split_triplet('-'.join((target, rest)))
 
     return split_triplet(config_sub(shell, target))
 
 
 target = help_host_target | real_target
 
 
@@ -1002,41 +1038,16 @@ set_define('XP_FREEBSD', target_is_freeb
 @depends(target)
 def target_is_solaris(target):
     if target.kernel == 'SunOS':
         return True
 
 
 set_define('XP_SOLARIS', target_is_solaris)
 
-# The application/project to build
-# ==============================================================
-option('--enable-application', nargs=1, env='MOZ_BUILD_APP',
-       help='Application to build. Same as --enable-project.')
-
-
-@depends('--enable-application')
-def application(app):
-    if app:
-        return app
-
-
-imply_option('--enable-project', application)
-
-
-@depends(check_build_environment)
-def default_project(build_env):
-    if build_env.topobjdir.endswith('/js/src'):
-        return 'js'
-    return 'browser'
-
-
-option('--enable-project', nargs=1, default=default_project,
-       help='Project to build')
-
 
 @depends('--enable-project', '--with-external-source-dir',
          check_build_environment, '--help')
 @imports(_from='os.path', _import='exists')
 def include_project_configure(project, external_source_dir, build_env, help):
     if not project:
         die('--enable-project is required.')
 
--- a/build/moz.configure/toolchain.configure
+++ b/build/moz.configure/toolchain.configure
@@ -1180,21 +1180,23 @@ def compiler(language, host_or_target, c
     add_old_configure_assignment(var, depends_if(valid_compiler)(
         lambda x: list(x.wrapper) + [x.compiler] + list(x.flags)))
 
     if host_or_target is target:
         add_old_configure_assignment('ac_cv_prog_%s' % var, depends_if(valid_compiler)(
             lambda x: list(x.wrapper) + [x.compiler] + list(x.flags)))
         # We check that it works in python configure already.
         add_old_configure_assignment('ac_cv_prog_%s_works' % var.lower(), 'yes')
+        add_old_configure_assignment(
+            'ac_cv_prog_%s_cross' % var.lower(),
+            depends(cross_compiling)(lambda x: 'yes' if x else 'no'))
         gcc_like = depends(valid_compiler.type)(lambda x: 'yes' if x in ('gcc', 'clang') else 'no')
         add_old_configure_assignment('ac_cv_prog_%s_g' % var.lower(), gcc_like)
         if language == 'C':
             add_old_configure_assignment('ac_cv_prog_gcc', gcc_like)
-            add_old_configure_assignment('ac_cv_prog_gcc_traditional', 'no')
         if language == 'C++':
             add_old_configure_assignment('ac_cv_prog_gxx', gcc_like)
 
 
     # Set CC_TYPE/CC_VERSION/HOST_CC_TYPE/HOST_CC_VERSION to allow
     # old-configure to do some of its still existing checks.
     if language == 'C':
         set_config(
--- a/config/external/icu/common/sources.mozbuild
+++ b/config/external/icu/common/sources.mozbuild
@@ -3,66 +3,58 @@ SOURCES += [
    '/intl/icu/source/common/appendable.cpp',
    '/intl/icu/source/common/bmpset.cpp',
    '/intl/icu/source/common/brkeng.cpp',
    '/intl/icu/source/common/brkiter.cpp',
    '/intl/icu/source/common/bytesinkutil.cpp',
    '/intl/icu/source/common/bytestream.cpp',
    '/intl/icu/source/common/bytestrie.cpp',
    '/intl/icu/source/common/bytestriebuilder.cpp',
-   '/intl/icu/source/common/bytestrieiterator.cpp',
    '/intl/icu/source/common/caniter.cpp',
    '/intl/icu/source/common/characterproperties.cpp',
    '/intl/icu/source/common/chariter.cpp',
    '/intl/icu/source/common/charstr.cpp',
    '/intl/icu/source/common/cmemory.cpp',
-   '/intl/icu/source/common/cstr.cpp',
    '/intl/icu/source/common/cstring.cpp',
-   '/intl/icu/source/common/cwchar.cpp',
    '/intl/icu/source/common/dictbe.cpp',
    '/intl/icu/source/common/dictionarydata.cpp',
    '/intl/icu/source/common/dtintrv.cpp',
    '/intl/icu/source/common/edits.cpp',
    '/intl/icu/source/common/errorcode.cpp',
    '/intl/icu/source/common/filteredbrk.cpp',
    '/intl/icu/source/common/filterednormalizer2.cpp',
-   '/intl/icu/source/common/icudataver.cpp',
-   '/intl/icu/source/common/icuplug.cpp',
    '/intl/icu/source/common/loadednormalizer2impl.cpp',
    '/intl/icu/source/common/locavailable.cpp',
    '/intl/icu/source/common/locbased.cpp',
    '/intl/icu/source/common/locdispnames.cpp',
    '/intl/icu/source/common/locdspnm.cpp',
    '/intl/icu/source/common/locid.cpp',
    '/intl/icu/source/common/loclikely.cpp',
    '/intl/icu/source/common/locmap.cpp',
    '/intl/icu/source/common/locresdata.cpp',
    '/intl/icu/source/common/locutil.cpp',
    '/intl/icu/source/common/messagepattern.cpp',
    '/intl/icu/source/common/normalizer2.cpp',
    '/intl/icu/source/common/normalizer2impl.cpp',
    '/intl/icu/source/common/normlzr.cpp',
    '/intl/icu/source/common/parsepos.cpp',
    '/intl/icu/source/common/patternprops.cpp',
-   '/intl/icu/source/common/pluralmap.cpp',
    '/intl/icu/source/common/propname.cpp',
-   '/intl/icu/source/common/propsvec.cpp',
    '/intl/icu/source/common/punycode.cpp',
    '/intl/icu/source/common/putil.cpp',
    '/intl/icu/source/common/rbbi.cpp',
    '/intl/icu/source/common/rbbi_cache.cpp',
    '/intl/icu/source/common/rbbidata.cpp',
    '/intl/icu/source/common/rbbinode.cpp',
    '/intl/icu/source/common/rbbirb.cpp',
    '/intl/icu/source/common/rbbiscan.cpp',
    '/intl/icu/source/common/rbbisetb.cpp',
    '/intl/icu/source/common/rbbistbl.cpp',
    '/intl/icu/source/common/rbbitblb.cpp',
    '/intl/icu/source/common/resbund.cpp',
-   '/intl/icu/source/common/resbund_cnv.cpp',
    '/intl/icu/source/common/resource.cpp',
    '/intl/icu/source/common/ruleiter.cpp',
    '/intl/icu/source/common/schriter.cpp',
    '/intl/icu/source/common/serv.cpp',
    '/intl/icu/source/common/servlk.cpp',
    '/intl/icu/source/common/servlkf.cpp',
    '/intl/icu/source/common/servls.cpp',
    '/intl/icu/source/common/servnotf.cpp',
@@ -72,63 +64,50 @@ SOURCES += [
    '/intl/icu/source/common/simpleformatter.cpp',
    '/intl/icu/source/common/static_unicode_sets.cpp',
    '/intl/icu/source/common/stringpiece.cpp',
    '/intl/icu/source/common/stringtriebuilder.cpp',
    '/intl/icu/source/common/uarrsort.cpp',
    '/intl/icu/source/common/ubidi.cpp',
    '/intl/icu/source/common/ubidi_props.cpp',
    '/intl/icu/source/common/ubidiln.cpp',
-   '/intl/icu/source/common/ubiditransform.cpp',
    '/intl/icu/source/common/ubidiwrt.cpp',
    '/intl/icu/source/common/ubrk.cpp',
    '/intl/icu/source/common/ucase.cpp',
    '/intl/icu/source/common/ucasemap.cpp',
    '/intl/icu/source/common/ucasemap_titlecase_brkiter.cpp',
-   '/intl/icu/source/common/ucat.cpp',
    '/intl/icu/source/common/uchar.cpp',
    '/intl/icu/source/common/ucharstrie.cpp',
    '/intl/icu/source/common/ucharstriebuilder.cpp',
    '/intl/icu/source/common/ucharstrieiterator.cpp',
    '/intl/icu/source/common/uchriter.cpp',
    '/intl/icu/source/common/ucln_cmn.cpp',
    '/intl/icu/source/common/ucmndata.cpp',
    '/intl/icu/source/common/ucnv.cpp',
-   '/intl/icu/source/common/ucnv2022.cpp',
    '/intl/icu/source/common/ucnv_bld.cpp',
    '/intl/icu/source/common/ucnv_cb.cpp',
    '/intl/icu/source/common/ucnv_cnv.cpp',
-   '/intl/icu/source/common/ucnv_ct.cpp',
    '/intl/icu/source/common/ucnv_err.cpp',
-   '/intl/icu/source/common/ucnv_ext.cpp',
    '/intl/icu/source/common/ucnv_io.cpp',
-   '/intl/icu/source/common/ucnv_lmb.cpp',
-   '/intl/icu/source/common/ucnv_set.cpp',
    '/intl/icu/source/common/ucnv_u16.cpp',
    '/intl/icu/source/common/ucnv_u32.cpp',
    '/intl/icu/source/common/ucnv_u7.cpp',
    '/intl/icu/source/common/ucnv_u8.cpp',
    '/intl/icu/source/common/ucnvbocu.cpp',
-   '/intl/icu/source/common/ucnvdisp.cpp',
-   '/intl/icu/source/common/ucnvhz.cpp',
-   '/intl/icu/source/common/ucnvisci.cpp',
    '/intl/icu/source/common/ucnvlat1.cpp',
-   '/intl/icu/source/common/ucnvmbcs.cpp',
    '/intl/icu/source/common/ucnvscsu.cpp',
-   '/intl/icu/source/common/ucnvsel.cpp',
    '/intl/icu/source/common/ucol_swp.cpp',
    '/intl/icu/source/common/ucptrie.cpp',
    '/intl/icu/source/common/ucurr.cpp',
    '/intl/icu/source/common/udata.cpp',
    '/intl/icu/source/common/udatamem.cpp',
    '/intl/icu/source/common/udataswp.cpp',
    '/intl/icu/source/common/uenum.cpp',
    '/intl/icu/source/common/uhash.cpp',
    '/intl/icu/source/common/uhash_us.cpp',
-   '/intl/icu/source/common/uidna.cpp',
    '/intl/icu/source/common/uinit.cpp',
    '/intl/icu/source/common/uinvchar.cpp',
    '/intl/icu/source/common/uiter.cpp',
    '/intl/icu/source/common/ulist.cpp',
    '/intl/icu/source/common/uloc.cpp',
    '/intl/icu/source/common/uloc_keytype.cpp',
    '/intl/icu/source/common/uloc_tag.cpp',
    '/intl/icu/source/common/umapfile.cpp',
@@ -144,45 +123,39 @@ SOURCES += [
    '/intl/icu/source/common/uniset_props.cpp',
    '/intl/icu/source/common/unisetspan.cpp',
    '/intl/icu/source/common/unistr.cpp',
    '/intl/icu/source/common/unistr_case.cpp',
    '/intl/icu/source/common/unistr_case_locale.cpp',
    '/intl/icu/source/common/unistr_cnv.cpp',
    '/intl/icu/source/common/unistr_props.cpp',
    '/intl/icu/source/common/unistr_titlecase_brkiter.cpp',
-   '/intl/icu/source/common/unorm.cpp',
    '/intl/icu/source/common/unormcmp.cpp',
    '/intl/icu/source/common/uobject.cpp',
    '/intl/icu/source/common/uprops.cpp',
-   '/intl/icu/source/common/ures_cnv.cpp',
    '/intl/icu/source/common/uresbund.cpp',
    '/intl/icu/source/common/uresdata.cpp',
-   '/intl/icu/source/common/usc_impl.cpp',
    '/intl/icu/source/common/uscript.cpp',
    '/intl/icu/source/common/uscript_props.cpp',
    '/intl/icu/source/common/uset.cpp',
    '/intl/icu/source/common/uset_props.cpp',
    '/intl/icu/source/common/usetiter.cpp',
-   '/intl/icu/source/common/ushape.cpp',
    '/intl/icu/source/common/usprep.cpp',
    '/intl/icu/source/common/ustack.cpp',
    '/intl/icu/source/common/ustr_cnv.cpp',
    '/intl/icu/source/common/ustr_titlecase_brkiter.cpp',
-   '/intl/icu/source/common/ustr_wcs.cpp',
    '/intl/icu/source/common/ustrcase.cpp',
    '/intl/icu/source/common/ustrcase_locale.cpp',
    '/intl/icu/source/common/ustrenum.cpp',
    '/intl/icu/source/common/ustrfmt.cpp',
    '/intl/icu/source/common/ustring.cpp',
    '/intl/icu/source/common/ustrtrns.cpp',
    '/intl/icu/source/common/utext.cpp',
    '/intl/icu/source/common/utf_impl.cpp',
    '/intl/icu/source/common/util.cpp',
-   '/intl/icu/source/common/util_props.cpp',
    '/intl/icu/source/common/utrace.cpp',
    '/intl/icu/source/common/utrie.cpp',
    '/intl/icu/source/common/utrie2.cpp',
    '/intl/icu/source/common/utrie2_builder.cpp',
    '/intl/icu/source/common/utrie_swap.cpp',
    '/intl/icu/source/common/uts46.cpp',
    '/intl/icu/source/common/utypes.cpp',
    '/intl/icu/source/common/uvector.cpp',
--- a/config/external/icu/i18n/sources.mozbuild
+++ b/config/external/icu/i18n/sources.mozbuild
@@ -1,19 +1,15 @@
 # THIS FILE IS GENERATED BY /intl/icu_sources_data.py DO NOT EDIT
 SOURCES += [
-   '/intl/icu/source/i18n/alphaindex.cpp',
-   '/intl/icu/source/i18n/anytrans.cpp',
    '/intl/icu/source/i18n/astro.cpp',
    '/intl/icu/source/i18n/basictz.cpp',
    '/intl/icu/source/i18n/bocsu.cpp',
-   '/intl/icu/source/i18n/brktrans.cpp',
    '/intl/icu/source/i18n/buddhcal.cpp',
    '/intl/icu/source/i18n/calendar.cpp',
-   '/intl/icu/source/i18n/casetrn.cpp',
    '/intl/icu/source/i18n/cecal.cpp',
    '/intl/icu/source/i18n/chnsecal.cpp',
    '/intl/icu/source/i18n/choicfmt.cpp',
    '/intl/icu/source/i18n/coleitr.cpp',
    '/intl/icu/source/i18n/coll.cpp',
    '/intl/icu/source/i18n/collation.cpp',
    '/intl/icu/source/i18n/collationbuilder.cpp',
    '/intl/icu/source/i18n/collationcompare.cpp',
@@ -30,25 +26,16 @@ SOURCES += [
    '/intl/icu/source/i18n/collationrootelements.cpp',
    '/intl/icu/source/i18n/collationruleparser.cpp',
    '/intl/icu/source/i18n/collationsets.cpp',
    '/intl/icu/source/i18n/collationsettings.cpp',
    '/intl/icu/source/i18n/collationtailoring.cpp',
    '/intl/icu/source/i18n/collationweights.cpp',
    '/intl/icu/source/i18n/compactdecimalformat.cpp',
    '/intl/icu/source/i18n/coptccal.cpp',
-   '/intl/icu/source/i18n/cpdtrans.cpp',
-   '/intl/icu/source/i18n/csdetect.cpp',
-   '/intl/icu/source/i18n/csmatch.cpp',
-   '/intl/icu/source/i18n/csr2022.cpp',
-   '/intl/icu/source/i18n/csrecog.cpp',
-   '/intl/icu/source/i18n/csrmbcs.cpp',
-   '/intl/icu/source/i18n/csrsbcs.cpp',
-   '/intl/icu/source/i18n/csrucode.cpp',
-   '/intl/icu/source/i18n/csrutf8.cpp',
    '/intl/icu/source/i18n/curramt.cpp',
    '/intl/icu/source/i18n/currfmt.cpp',
    '/intl/icu/source/i18n/currpinf.cpp',
    '/intl/icu/source/i18n/currunit.cpp',
    '/intl/icu/source/i18n/dangical.cpp',
    '/intl/icu/source/i18n/datefmt.cpp',
    '/intl/icu/source/i18n/dayperiodrules.cpp',
    '/intl/icu/source/i18n/dcfmtsym.cpp',
@@ -63,44 +50,36 @@ SOURCES += [
    '/intl/icu/source/i18n/double-conversion-strtod.cpp',
    '/intl/icu/source/i18n/double-conversion.cpp',
    '/intl/icu/source/i18n/dtfmtsym.cpp',
    '/intl/icu/source/i18n/dtitvfmt.cpp',
    '/intl/icu/source/i18n/dtitvinf.cpp',
    '/intl/icu/source/i18n/dtptngen.cpp',
    '/intl/icu/source/i18n/dtrule.cpp',
    '/intl/icu/source/i18n/erarules.cpp',
-   '/intl/icu/source/i18n/esctrn.cpp',
    '/intl/icu/source/i18n/ethpccal.cpp',
    '/intl/icu/source/i18n/fmtable.cpp',
-   '/intl/icu/source/i18n/fmtable_cnv.cpp',
    '/intl/icu/source/i18n/format.cpp',
    '/intl/icu/source/i18n/fphdlimp.cpp',
    '/intl/icu/source/i18n/fpositer.cpp',
-   '/intl/icu/source/i18n/funcrepl.cpp',
-   '/intl/icu/source/i18n/gender.cpp',
    '/intl/icu/source/i18n/gregocal.cpp',
    '/intl/icu/source/i18n/gregoimp.cpp',
    '/intl/icu/source/i18n/hebrwcal.cpp',
    '/intl/icu/source/i18n/indiancal.cpp',
-   '/intl/icu/source/i18n/inputext.cpp',
    '/intl/icu/source/i18n/islamcal.cpp',
    '/intl/icu/source/i18n/japancal.cpp',
    '/intl/icu/source/i18n/listformatter.cpp',
    '/intl/icu/source/i18n/measfmt.cpp',
    '/intl/icu/source/i18n/measunit.cpp',
    '/intl/icu/source/i18n/measure.cpp',
    '/intl/icu/source/i18n/msgfmt.cpp',
-   '/intl/icu/source/i18n/name2uni.cpp',
    '/intl/icu/source/i18n/nfrs.cpp',
    '/intl/icu/source/i18n/nfrule.cpp',
    '/intl/icu/source/i18n/nfsubs.cpp',
-   '/intl/icu/source/i18n/nortrans.cpp',
    '/intl/icu/source/i18n/nounit.cpp',
-   '/intl/icu/source/i18n/nultrans.cpp',
    '/intl/icu/source/i18n/number_affixutils.cpp',
    '/intl/icu/source/i18n/number_asformat.cpp',
    '/intl/icu/source/i18n/number_capi.cpp',
    '/intl/icu/source/i18n/number_compact.cpp',
    '/intl/icu/source/i18n/number_currencysymbols.cpp',
    '/intl/icu/source/i18n/number_decimalquantity.cpp',
    '/intl/icu/source/i18n/number_decimfmtprops.cpp',
    '/intl/icu/source/i18n/number_fluent.cpp',
@@ -133,106 +112,70 @@ SOURCES += [
    '/intl/icu/source/i18n/numparse_validators.cpp',
    '/intl/icu/source/i18n/numrange_fluent.cpp',
    '/intl/icu/source/i18n/numrange_impl.cpp',
    '/intl/icu/source/i18n/numsys.cpp',
    '/intl/icu/source/i18n/olsontz.cpp',
    '/intl/icu/source/i18n/persncal.cpp',
    '/intl/icu/source/i18n/plurfmt.cpp',
    '/intl/icu/source/i18n/plurrule.cpp',
-   '/intl/icu/source/i18n/quant.cpp',
    '/intl/icu/source/i18n/quantityformatter.cpp',
    '/intl/icu/source/i18n/rbnf.cpp',
-   '/intl/icu/source/i18n/rbt.cpp',
-   '/intl/icu/source/i18n/rbt_data.cpp',
-   '/intl/icu/source/i18n/rbt_pars.cpp',
-   '/intl/icu/source/i18n/rbt_rule.cpp',
-   '/intl/icu/source/i18n/rbt_set.cpp',
    '/intl/icu/source/i18n/rbtz.cpp',
-   '/intl/icu/source/i18n/regexcmp.cpp',
-   '/intl/icu/source/i18n/regeximp.cpp',
-   '/intl/icu/source/i18n/regexst.cpp',
-   '/intl/icu/source/i18n/regextxt.cpp',
    '/intl/icu/source/i18n/region.cpp',
    '/intl/icu/source/i18n/reldatefmt.cpp',
    '/intl/icu/source/i18n/reldtfmt.cpp',
-   '/intl/icu/source/i18n/rematch.cpp',
-   '/intl/icu/source/i18n/remtrans.cpp',
-   '/intl/icu/source/i18n/repattrn.cpp',
    '/intl/icu/source/i18n/rulebasedcollator.cpp',
-   '/intl/icu/source/i18n/scientificnumberformatter.cpp',
    '/intl/icu/source/i18n/scriptset.cpp',
    '/intl/icu/source/i18n/search.cpp',
    '/intl/icu/source/i18n/selfmt.cpp',
    '/intl/icu/source/i18n/sharedbreakiterator.cpp',
    '/intl/icu/source/i18n/simpletz.cpp',
    '/intl/icu/source/i18n/smpdtfmt.cpp',
    '/intl/icu/source/i18n/smpdtfst.cpp',
    '/intl/icu/source/i18n/sortkey.cpp',
    '/intl/icu/source/i18n/standardplural.cpp',
-   '/intl/icu/source/i18n/strmatch.cpp',
-   '/intl/icu/source/i18n/strrepl.cpp',
    '/intl/icu/source/i18n/stsearch.cpp',
    '/intl/icu/source/i18n/taiwncal.cpp',
    '/intl/icu/source/i18n/timezone.cpp',
-   '/intl/icu/source/i18n/titletrn.cpp',
    '/intl/icu/source/i18n/tmunit.cpp',
    '/intl/icu/source/i18n/tmutamt.cpp',
    '/intl/icu/source/i18n/tmutfmt.cpp',
-   '/intl/icu/source/i18n/tolowtrn.cpp',
-   '/intl/icu/source/i18n/toupptrn.cpp',
-   '/intl/icu/source/i18n/translit.cpp',
-   '/intl/icu/source/i18n/transreg.cpp',
-   '/intl/icu/source/i18n/tridpars.cpp',
    '/intl/icu/source/i18n/tzfmt.cpp',
    '/intl/icu/source/i18n/tzgnames.cpp',
    '/intl/icu/source/i18n/tznames.cpp',
    '/intl/icu/source/i18n/tznames_impl.cpp',
    '/intl/icu/source/i18n/tzrule.cpp',
    '/intl/icu/source/i18n/tztrans.cpp',
    '/intl/icu/source/i18n/ucal.cpp',
    '/intl/icu/source/i18n/ucln_in.cpp',
    '/intl/icu/source/i18n/ucol.cpp',
    '/intl/icu/source/i18n/ucol_res.cpp',
    '/intl/icu/source/i18n/ucol_sit.cpp',
    '/intl/icu/source/i18n/ucoleitr.cpp',
-   '/intl/icu/source/i18n/ucsdet.cpp',
    '/intl/icu/source/i18n/udat.cpp',
-   '/intl/icu/source/i18n/udateintervalformat.cpp',
    '/intl/icu/source/i18n/udatpg.cpp',
    '/intl/icu/source/i18n/ufieldpositer.cpp',
    '/intl/icu/source/i18n/uitercollationiterator.cpp',
    '/intl/icu/source/i18n/ulistformatter.cpp',
-   '/intl/icu/source/i18n/ulocdata.cpp',
    '/intl/icu/source/i18n/umsg.cpp',
-   '/intl/icu/source/i18n/unesctrn.cpp',
-   '/intl/icu/source/i18n/uni2name.cpp',
    '/intl/icu/source/i18n/unum.cpp',
    '/intl/icu/source/i18n/unumsys.cpp',
    '/intl/icu/source/i18n/upluralrules.cpp',
-   '/intl/icu/source/i18n/uregex.cpp',
-   '/intl/icu/source/i18n/uregexc.cpp',
-   '/intl/icu/source/i18n/uregion.cpp',
    '/intl/icu/source/i18n/usearch.cpp',
    '/intl/icu/source/i18n/uspoof.cpp',
-   '/intl/icu/source/i18n/uspoof_build.cpp',
-   '/intl/icu/source/i18n/uspoof_conf.cpp',
    '/intl/icu/source/i18n/uspoof_impl.cpp',
    '/intl/icu/source/i18n/utf16collationiterator.cpp',
    '/intl/icu/source/i18n/utf8collationiterator.cpp',
    '/intl/icu/source/i18n/utmscale.cpp',
-   '/intl/icu/source/i18n/utrans.cpp',
    '/intl/icu/source/i18n/vtzone.cpp',
-   '/intl/icu/source/i18n/vzone.cpp',
    '/intl/icu/source/i18n/windtfmt.cpp',
    '/intl/icu/source/i18n/winnmfmt.cpp',
    '/intl/icu/source/i18n/wintzimpl.cpp',
    '/intl/icu/source/i18n/zonemeta.cpp',
-   '/intl/icu/source/i18n/zrule.cpp',
-   '/intl/icu/source/i18n/ztrans.cpp',
 ]
 
 EXPORTS.unicode += [
    '/intl/icu/source/i18n/unicode/alphaindex.h',
    '/intl/icu/source/i18n/unicode/basictz.h',
    '/intl/icu/source/i18n/unicode/calendar.h',
    '/intl/icu/source/i18n/unicode/choicfmt.h',
    '/intl/icu/source/i18n/unicode/coleitr.h',
--- a/db/sqlite3/src/sqlite.symbols
+++ b/db/sqlite3/src/sqlite.symbols
@@ -31,16 +31,17 @@ sqlite3_column_int
 sqlite3_column_int64
 sqlite3_column_name
 sqlite3_column_name16
 sqlite3_column_text
 sqlite3_column_text16
 sqlite3_column_type
 sqlite3_column_value
 sqlite3_commit_hook
+sqlite3_compileoption_used
 sqlite3_complete
 sqlite3_complete16
 sqlite3_config
 sqlite3_create_collation
 sqlite3_create_collation16
 sqlite3_create_function
 sqlite3_create_function16
 sqlite3_create_module
--- 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: {
@@ -180,84 +182,80 @@ function getBreakpointByLocation(locatio
 function setXHRBreakpoint(path: string, method: string) {
   return threadClient.setXHRBreakpoint(path, method);
 }
 
 function removeXHRBreakpoint(path: string, method: string) {
   return threadClient.removeXHRBreakpoint(path, method);
 }
 
-// Source and breakpoint clients do not yet support an options structure, so
-// for now we transform options into condition strings when setting breakpoints.
-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),
+      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];
 
-  const sourceThreadClient = sourceThreads[bpClient.source.actor];
-  return bpClient
-    .setCondition(sourceThreadClient, transformOptionsToCondition(options))
-    .then(_bpClient => {
-      bpClients[breakpointId] = _bpClient;
-      return { id: breakpointId };
-    });
+  if (debuggerClient.mainRoot.traits.nativeLogpoints) {
+    bpClient.setOptions(options);
+  } else {
+    // Older server breakpoints destroy themselves when changing options.
+    delete bpClients[id];
+    bpClient
+      .setOptions(options)
+      .then(_bpClient => {
+        bpClients[id] = _bpClient;
+      });
+  }
 }
 
 async function evaluateInFrame(script: Script, options: EvaluateParam) {
   return evaluate(script, options);
 }
 
 async function evaluateExpressions(scripts: Script[], options: EvaluateParam) {
   return Promise.all(scripts.map(script => evaluate(script, options)));
@@ -323,65 +321,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 +387,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 +447,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 +469,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<*>,
@@ -385,17 +393,17 @@ export type BreakpointClient = {
   actor: ActorId,
   remove: () => void,
   location: {
     actor: string,
     url: string,
     line: number,
     column: ?number
   },
-  setCondition: (ThreadClient, ?string) => Promise<BreakpointClient>,
+  setOptions: (BreakpointOptions) => Promise<BreakpointClient>,
   // request: any,
   source: SourceClient,
   options: BreakpointOptions
 };
 
 export type BPClients = { [id: ActorId]: BreakpointClient };
 
 export type BreakpointResponse = [
@@ -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)
+    );
+  }