Merge inbound to mozilla-central. a=merge
authorCiure Andrei <aciure@mozilla.com>
Sat, 26 Jan 2019 11:37:07 +0200
changeset 455531 341040a5fb8064aef7dc5c520709019716ec222b
parent 455530 edfd8a13603c0d5ebb69362dff02bb138f616127 (current diff)
parent 455483 21211736651cc066fee6bfb0377ed94623c9f8fc (diff)
child 455532 c835d9df553faf2d95b7e8f30995dc7fdad25dd2
child 455534 7ea32bdfebf13efa1c11d817eb73f7a4d2654c36
child 455553 aee9ebe9b9b2432993e8a3cc1f1cf33ab4e2a240
push id111476
push useraciure@mozilla.com
push dateSat, 26 Jan 2019 09:41:06 +0000
treeherdermozilla-inbound@341040a5fb80 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone66.0a1
first release with
nightly linux32
341040a5fb80 / 66.0a1 / 20190126093742 / files
nightly linux64
341040a5fb80 / 66.0a1 / 20190126093742 / files
nightly mac
341040a5fb80 / 66.0a1 / 20190126093742 / files
nightly win32
341040a5fb80 / 66.0a1 / 20190126093742 / files
nightly win64
341040a5fb80 / 66.0a1 / 20190126093742 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central. a=merge
--- a/browser/base/content/test/trackingUI/browser_trackingUI_animation.js
+++ b/browser/base/content/test/trackingUI/browser_trackingUI_animation.js
@@ -2,39 +2,44 @@
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 const TRACKING_PAGE = "http://tracking.example.org/browser/browser/base/content/test/trackingUI/trackingPage.html";
 const BENIGN_PAGE = "http://tracking.example.org/browser/browser/base/content/test/trackingUI/benignPage.html";
 const TP_PREF = "privacy.trackingprotection.enabled";
 const ANIMATIONS_PREF = "toolkit.cosmeticAnimations.enabled";
+const DTSCBN_PREF = "dom.testing.sync-content-blocking-notifications";
 
 // Test that the shield icon animation can be controlled by the cosmetic
 // animations pref and that one of the icons is visible in each case.
 add_task(async function testShieldAnimation() {
   await UrlClassifierTestUtils.addTestTrackers();
   Services.prefs.setBoolPref(TP_PREF, true);
+  Services.prefs.setBoolPref(DTSCBN_PREF, true);
 
   let tab = gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser);
 
   let animationIcon = document.getElementById("tracking-protection-icon-animatable-image");
   let noAnimationIcon = document.getElementById("tracking-protection-icon");
 
   Services.prefs.setBoolPref(ANIMATIONS_PREF, true);
-  await promiseTabLoadEvent(tab, TRACKING_PAGE);
+  await Promise.all([promiseTabLoadEvent(tab, TRACKING_PAGE),
+                     waitForContentBlockingEvent(2, tab.linkedBrowser.ownerGlobal)]);
   ok(BrowserTestUtils.is_hidden(noAnimationIcon), "the default icon is hidden when animations are enabled");
   ok(BrowserTestUtils.is_visible(animationIcon), "the animated icon is shown when animations are enabled");
 
   await promiseTabLoadEvent(tab, BENIGN_PAGE);
   ok(BrowserTestUtils.is_hidden(animationIcon), "the animated icon is hidden");
   ok(BrowserTestUtils.is_hidden(noAnimationIcon), "the default icon is hidden");
 
   Services.prefs.setBoolPref(ANIMATIONS_PREF, false);
-  await promiseTabLoadEvent(tab, TRACKING_PAGE);
+  await Promise.all([promiseTabLoadEvent(tab, TRACKING_PAGE),
+                     waitForContentBlockingEvent(2, tab.linkedBrowser.ownerGlobal)]);
   ok(BrowserTestUtils.is_visible(noAnimationIcon), "the default icon is shown when animations are disabled");
   ok(BrowserTestUtils.is_hidden(animationIcon), "the animated icon is hidden when animations are disabled");
 
   gBrowser.removeCurrentTab();
   Services.prefs.clearUserPref(ANIMATIONS_PREF);
   Services.prefs.clearUserPref(TP_PREF);
+  Services.prefs.clearUserPref(DTSCBN_PREF);
   UrlClassifierTestUtils.cleanupTestTrackers();
 });
--- a/browser/base/content/test/trackingUI/browser_trackingUI_animation_2.js
+++ b/browser/base/content/test/trackingUI/browser_trackingUI_animation_2.js
@@ -6,28 +6,32 @@
  */
 
 const TP_PREF = "privacy.trackingprotection.enabled";
 const TP_PB_PREF = "privacy.trackingprotection.enabled";
 const NCB_PREF = "network.cookie.cookieBehavior";
 const BENIGN_PAGE = "http://tracking.example.org/browser/browser/base/content/test/trackingUI/benignPage.html";
 const TRACKING_PAGE = "http://tracking.example.org/browser/browser/base/content/test/trackingUI/trackingPage.html";
 const COOKIE_PAGE = "http://tracking.example.org/browser/browser/base/content/test/trackingUI/cookiePage.html";
+const DTSCBN_PREF = "dom.testing.sync-content-blocking-notifications";
 
 requestLongerTimeout(2);
 
 registerCleanupFunction(function() {
   UrlClassifierTestUtils.cleanupTestTrackers();
   Services.prefs.clearUserPref(TP_PREF);
   Services.prefs.clearUserPref(TP_PB_PREF);
   Services.prefs.clearUserPref(NCB_PREF);
+  Services.prefs.clearUserPref(DTSCBN_PREF);
   Services.prefs.clearUserPref(ContentBlocking.prefIntroCount);
 });
 
 async function testTrackingProtectionAnimation(tabbrowser) {
+  Services.prefs.setBoolPref(DTSCBN_PREF, true);
+
   info("Load a test page not containing tracking elements");
   let benignTab = await BrowserTestUtils.openNewForegroundTab(tabbrowser, BENIGN_PAGE);
   let ContentBlocking = tabbrowser.ownerGlobal.ContentBlocking;
 
   ok(!ContentBlocking.iconBox.hasAttribute("active"), "iconBox not active");
   ok(!ContentBlocking.iconBox.hasAttribute("animate"), "iconBox not animating");
 
   info("Load a test page containing tracking elements");
--- a/browser/base/content/test/trackingUI/browser_trackingUI_pbmode_exceptions.js
+++ b/browser/base/content/test/trackingUI/browser_trackingUI_pbmode_exceptions.js
@@ -1,22 +1,24 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Test that sites added to the Tracking Protection whitelist in private
 // browsing mode don't persist once the private browsing window closes.
 
 const TP_PB_PREF = "privacy.trackingprotection.enabled";
 const TRACKING_PAGE = "http://tracking.example.org/browser/browser/base/content/test/trackingUI/trackingPage.html";
+const DTSCBN_PREF = "dom.testing.sync-content-blocking-notifications";
 var TrackingProtection = null;
 var ContentBlocking = null;
 var browser = null;
 
 registerCleanupFunction(function() {
   Services.prefs.clearUserPref(TP_PB_PREF);
+  Services.prefs.clearUserPref(DTSCBN_PREF);
   ContentBlocking = TrackingProtection = browser = null;
   UrlClassifierTestUtils.cleanupTestTrackers();
 });
 
 function hidden(sel) {
   let win = browser.ownerGlobal;
   let el = win.document.querySelector(sel);
   let display = win.getComputedStyle(el).getPropertyValue("display", null);
@@ -74,30 +76,32 @@ function testTrackingPageUnblocked() {
   ok(hidden("#tracking-action-unblock"), "unblockButton is hidden");
 
   ok(hidden("#identity-popup-content-blocking-not-detected"), "blocking not detected label is hidden");
   ok(!hidden("#identity-popup-content-blocking-detected"), "blocking detected label is visible");
 }
 
 add_task(async function testExceptionAddition() {
   await UrlClassifierTestUtils.addTestTrackers();
+  Services.prefs.setBoolPref(DTSCBN_PREF, true);
   let privateWin = await BrowserTestUtils.openNewBrowserWindow({private: true});
   browser = privateWin.gBrowser;
   let tab = await BrowserTestUtils.openNewForegroundTab({ gBrowser: browser, waitForLoad: true, waitForStateStop: true });
 
   ContentBlocking = browser.ownerGlobal.ContentBlocking;
   ok(ContentBlocking, "CB is attached to the private window");
   TrackingProtection = browser.ownerGlobal.TrackingProtection;
   ok(TrackingProtection, "TP is attached to the private window");
 
   Services.prefs.setBoolPref(TP_PB_PREF, true);
   ok(TrackingProtection.enabled, "TP is enabled after setting the pref");
 
   info("Load a test page containing tracking elements");
-  await promiseTabLoadEvent(tab, TRACKING_PAGE);
+  await Promise.all([promiseTabLoadEvent(tab, TRACKING_PAGE),
+                     waitForContentBlockingEvent(2, tab.ownerGlobal)]);
 
   testTrackingPage(tab.ownerGlobal);
 
   info("Disable TP for the page (which reloads the page)");
   let tabReloadPromise = promiseTabLoadEvent(tab);
   clickButton("#tracking-action-unblock");
   is(identityPopupState(), "closed", "Identity popup is closed");
 
@@ -123,22 +127,24 @@ add_task(async function testExceptionPer
   ContentBlocking = browser.ownerGlobal.ContentBlocking;
   ok(ContentBlocking, "CB is attached to the private window");
   TrackingProtection = browser.ownerGlobal.TrackingProtection;
   ok(TrackingProtection, "TP is attached to the private window");
 
   ok(TrackingProtection.enabled, "TP is still enabled");
 
   info("Load a test page containing tracking elements");
-  await promiseTabLoadEvent(tab, TRACKING_PAGE);
+  await Promise.all([promiseTabLoadEvent(tab, TRACKING_PAGE),
+                     waitForContentBlockingEvent(2, tab.ownerGlobal)]);
 
   testTrackingPage(tab.ownerGlobal);
 
   info("Disable TP for the page (which reloads the page)");
   let tabReloadPromise = promiseTabLoadEvent(tab);
   clickButton("#tracking-action-unblock");
   is(identityPopupState(), "closed", "Identity popup is closed");
 
-  await tabReloadPromise;
+  await Promise.all([tabReloadPromise,
+                     waitForContentBlockingEvent(2, tab.ownerGlobal)]);
   testTrackingPageUnblocked();
 
   privateWin.close();
 });
--- a/browser/base/content/test/trackingUI/browser_trackingUI_state.js
+++ b/browser/base/content/test/trackingUI/browser_trackingUI_state.js
@@ -12,32 +12,34 @@
  *     2) A page with tracking elements is loaded.
  *
  * See also Bugs 1175327, 1043801, 1178985
  */
 
 const TP_PREF = "privacy.trackingprotection.enabled";
 const TP_PB_PREF = "privacy.trackingprotection.pbmode.enabled";
 const TPC_PREF = "network.cookie.cookieBehavior";
+const DTSCBN_PREF = "dom.testing.sync-content-blocking-notifications";
 const BENIGN_PAGE = "http://tracking.example.org/browser/browser/base/content/test/trackingUI/benignPage.html";
 const TRACKING_PAGE = "http://tracking.example.org/browser/browser/base/content/test/trackingUI/trackingPage.html";
 const COOKIE_PAGE = "http://not-tracking.example.com/browser/browser/base/content/test/trackingUI/cookiePage.html";
 var ContentBlocking = null;
 var TrackingProtection = null;
 var ThirdPartyCookies = null;
 var tabbrowser = null;
 var gTrackingPageURL = TRACKING_PAGE;
 
 registerCleanupFunction(function() {
   TrackingProtection = ContentBlocking =
     ThirdPartyCookies = tabbrowser = null;
   UrlClassifierTestUtils.cleanupTestTrackers();
   Services.prefs.clearUserPref(TP_PREF);
   Services.prefs.clearUserPref(TP_PB_PREF);
   Services.prefs.clearUserPref(TPC_PREF);
+  Services.prefs.clearUserPref(DTSCBN_PREF);
 });
 
 // This is a special version of "hidden" that doesn't check for item
 // visibility and just asserts the display and opacity attributes.
 // That way we can test elements even when their panel is hidden...
 function hidden(sel) {
   let win = tabbrowser.ownerGlobal;
   let el = win.document.querySelector(sel);
@@ -101,30 +103,29 @@ function areTrackersBlocked(isPrivateBro
   return blockedByTP || blockedByTPC;
 }
 
 function testTrackingPage(window) {
   info("Tracking content must be blocked");
   ok(ContentBlocking.content.hasAttribute("detected"), "trackers are detected");
   ok(!ContentBlocking.content.hasAttribute("hasException"), "content shows no exception");
 
-  let isPrivateBrowsing = PrivateBrowsingUtils.isWindowPrivate(window);
-  let blockedByTP = areTrackersBlocked(isPrivateBrowsing);
+  let isWindowPrivate = PrivateBrowsingUtils.isWindowPrivate(window);
+  let blockedByTP = areTrackersBlocked(isWindowPrivate);
   is(BrowserTestUtils.is_visible(ContentBlocking.iconBox), blockedByTP,
      "icon box is" + (blockedByTP ? "" : " not") + " visible");
   is(ContentBlocking.iconBox.hasAttribute("active"), blockedByTP,
       "shield is" + (blockedByTP ? "" : " not") + " active");
   ok(!ContentBlocking.iconBox.hasAttribute("hasException"), "icon box shows no exception");
   is(ContentBlocking.iconBox.getAttribute("tooltiptext"),
      blockedByTP ? gNavigatorBundle.getString("trackingProtection.icon.activeTooltip") : "",
      "correct tooltip");
 
   ok(hidden("#tracking-action-block"), "blockButton is hidden");
 
-  let isWindowPrivate = PrivateBrowsingUtils.isWindowPrivate(window);
   if (isWindowPrivate) {
     ok(hidden("#tracking-action-unblock"), "unblockButton is hidden");
     is(!hidden("#tracking-action-unblock-private"), blockedByTP,
        "unblockButtonPrivate is" + (blockedByTP ? "" : " not") + " visible");
   } else {
     ok(hidden("#tracking-action-unblock-private"), "unblockButtonPrivate is hidden");
     is(!hidden("#tracking-action-unblock"), blockedByTP,
        "unblockButton is" + (blockedByTP ? "" : " not") + " visible");
@@ -213,16 +214,18 @@ async function testContentBlocking(tab) 
   clickButton("#tracking-action-block");
   await tabReloadPromise;
   testTrackingPage(tab.ownerGlobal);
 }
 
 add_task(async function testNormalBrowsing() {
   await UrlClassifierTestUtils.addTestTrackers();
 
+  Services.prefs.setBoolPref(DTSCBN_PREF, true);
+
   tabbrowser = gBrowser;
   let tab = tabbrowser.selectedTab = BrowserTestUtils.addTab(tabbrowser);
 
   ContentBlocking = gBrowser.ownerGlobal.ContentBlocking;
   ok(ContentBlocking, "CB is attached to the browser window");
   TrackingProtection = gBrowser.ownerGlobal.TrackingProtection;
   ok(TrackingProtection, "TP is attached to the browser window");
   is(TrackingProtection.enabled, Services.prefs.getBoolPref(TP_PREF),
--- a/browser/base/content/test/trackingUI/browser_trackingUI_state_all_disabled.js
+++ b/browser/base/content/test/trackingUI/browser_trackingUI_state_all_disabled.js
@@ -3,50 +3,54 @@
 
 "use strict";
 
 const TRACKING_PAGE = "http://tracking.example.org/browser/browser/base/content/test/trackingUI/trackingPage.html";
 const BENIGN_PAGE = "http://tracking.example.org/browser/browser/base/content/test/trackingUI/benignPage.html";
 const TP_PREF = "privacy.trackingprotection.enabled";
 const COOKIE_PREF = "network.cookie.cookieBehavior";
 const ANIMATIONS_PREF = "toolkit.cosmeticAnimations.enabled";
+const DTSCBN_PREF = "dom.testing.sync-content-blocking-notifications";
 
 // Check that the shield icon is always hidden when all content blocking
 // categories are turned off, even when content blocking is on.
 add_task(async function testContentBlockingAllDisabled() {
   await SpecialPowers.pushPrefEnv({set: [
     [TP_PREF, false],
     [COOKIE_PREF, Ci.nsICookieService.BEHAVIOR_ACCEPT],
+    [DTSCBN_PREF, true],
   ]});
   await UrlClassifierTestUtils.addTestTrackers();
 
   let tab = gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser);
   Services.prefs.setBoolPref(ANIMATIONS_PREF, true);
 
   registerCleanupFunction(function() {
     gBrowser.removeCurrentTab();
     Services.prefs.clearUserPref(ANIMATIONS_PREF);
     UrlClassifierTestUtils.cleanupTestTrackers();
   });
 
   let animationIcon = document.getElementById("tracking-protection-icon-animatable-image");
   let noAnimationIcon = document.getElementById("tracking-protection-icon");
 
-  await promiseTabLoadEvent(tab, TRACKING_PAGE);
+  await Promise.all([promiseTabLoadEvent(tab, TRACKING_PAGE),
+                     waitForContentBlockingEvent(2, tab.ownerGlobal)]);
   ok(BrowserTestUtils.is_hidden(noAnimationIcon), "the default icon is hidden");
   ok(BrowserTestUtils.is_hidden(animationIcon), "the animated icon is hidden");
 
   await promiseTabLoadEvent(tab, BENIGN_PAGE);
   ok(BrowserTestUtils.is_hidden(animationIcon), "the animated icon is hidden");
   ok(BrowserTestUtils.is_hidden(noAnimationIcon), "the default icon is hidden");
 
   Services.prefs.setBoolPref(ANIMATIONS_PREF, false);
   await promiseTabLoadEvent(tab, TRACKING_PAGE);
   ok(BrowserTestUtils.is_hidden(animationIcon), "the animated icon is hidden");
   ok(BrowserTestUtils.is_hidden(noAnimationIcon), "the default icon is hidden");
 
   // Sanitity check that the shield is showing when at least one blocker is enabled.
   await SpecialPowers.pushPrefEnv({set: [
     [TP_PREF, true],
   ]});
-  await promiseTabLoadEvent(tab, TRACKING_PAGE);
+  await Promise.all([promiseTabLoadEvent(tab, TRACKING_PAGE),
+                     waitForContentBlockingEvent(2, tab.ownerGlobal)]);
   ok(BrowserTestUtils.is_visible(noAnimationIcon), "the default icon is shown");
 });
--- a/devtools/client/debugger/new/src/client/firefox/create.js
+++ b/devtools/client/debugger/new/src/client/firefox/create.js
@@ -21,18 +21,20 @@ export function createFrame(frame: Frame
   }
   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 location = {
-    sourceId: frame.where.source.actor,
+    sourceId: frame.where.source ? frame.where.source.actor : frame.where.actor,
     line: frame.where.line,
     column: frame.where.column
   };
 
   return {
     id: frame.actor,
     displayName: title,
     location,
--- a/devtools/server/actors/frame.js
+++ b/devtools/server/actors/frame.js
@@ -94,17 +94,17 @@ const FrameActor = ActorClassWithSpec(fr
       form.this = createValueGrip(this.frame.this, threadActor._pausePool,
         threadActor.objectGrip);
     }
 
     form.arguments = this._args();
     if (this.frame.script) {
       const generatedLocation = this.threadActor.sources.getFrameLocation(this.frame);
       form.where = {
-        source: generatedLocation.generatedSourceActor.form(),
+        actor: generatedLocation.generatedSourceActor.actorID,
         line: generatedLocation.generatedLine,
         column: generatedLocation.generatedColumn,
       };
     }
 
     if (!this.frame.older) {
       form.oldest = true;
     }
--- a/devtools/server/actors/thread.js
+++ b/devtools/server/actors/thread.js
@@ -478,17 +478,17 @@ const ThreadActor = ActorClassWithSpec(t
 
       if (!generatedSourceActor) {
         // If the frame location is in a source that not pass the 'allowSource'
         // check and thus has no actor, we do not bother pausing.
         return undefined;
       }
 
       packet.frame.where = {
-        source: generatedSourceActor.form(),
+        actor: generatedSourceActor.actorID,
         line: generatedLine,
         column: generatedColumn,
       };
       const pkt = onPacket(packet);
 
       this.conn.send(pkt);
     } catch (error) {
       reportError(error);
@@ -1186,29 +1186,27 @@ const ThreadActor = ActorClassWithSpec(t
     for (; frame && (!count || i < (start + count)); i++, frame = frame.older) {
       const form = this._createFrameActor(frame).form();
       form.depth = i;
 
       let frameItem = null;
 
       const frameSourceActor = this.sources.createSourceActor(frame.script.source);
       if (frameSourceActor) {
-        const sourceForm = frameSourceActor.form();
         form.where = {
-          source: sourceForm,
+          actor: frameSourceActor.actorID,
           line: form.where.line,
           column: form.where.column,
         };
-        form.source = sourceForm;
         frameItem = form;
       }
       frames.push(frameItem);
     }
 
-    // Filter null values because sourcemapping may have failed.
+    // Filter null values because createSourceActor can be falsey
     return { frames: frames.filter(x => !!x) };
   },
 
   onReleaseMany: function(request) {
     if (!request.actors) {
       return { error: "missingParameter",
                message: "no actors were specified" };
     }
--- a/devtools/server/tests/unit/head_dbg.js
+++ b/devtools/server/tests/unit/head_dbg.js
@@ -799,29 +799,36 @@ function getSourceContent(sourceClient) 
 
 /**
  * Get a source at the specified url.
  *
  * @param ThreadClient threadClient
  * @param string url
  * @returns Promise<SourceClient>
  */
-function getSource(threadClient, url) {
-  const deferred = defer();
-  threadClient.getSources((res) => {
-    const source = res.sources.filter(function(s) {
-      return s.url === url;
-    });
-    if (source.length) {
-      deferred.resolve(threadClient.source(source[0]));
-    } else {
-      deferred.reject(new Error("source not found"));
-    }
-  });
-  return deferred.promise;
+async function getSource(threadClient, url) {
+  const {sources} = await threadClient.getSources();
+  const source = sources.find((s) => s.url === url);
+
+  if (source) {
+    return threadClient.source(source);
+  }
+
+  throw new Error("source not found");
+}
+
+async function getSourceById(threadClient, id) {
+  const { sources } = await threadClient.getSources();
+  const form = sources.find(source => source.actor == id);
+  return threadClient.source(form);
+}
+
+async function getSourceFormById(threadClient, id) {
+  const { sources } = await threadClient.getSources();
+  return sources.find(source => source.actor == id);
 }
 
 /**
  * Do a reload which clears the thread debugger
  *
  * @param TabFront tabFront
  * @returns Promise<response>
  */
--- a/devtools/server/tests/unit/test_blackboxing-01.js
+++ b/devtools/server/tests/unit/test_blackboxing-01.js
@@ -25,74 +25,82 @@ function run_test() {
   do_test_pending();
 }
 
 const BLACK_BOXED_URL = "http://example.com/blackboxme.js";
 const SOURCE_URL = "http://example.com/source.js";
 
 const testBlackBox = async function() {
   const packet = await executeOnNextTickAndWaitForPause(evalCode, gClient);
-  const source = gThreadClient.source(packet.frame.where.source);
+  const bpSource = await getSourceById(
+    gThreadClient,
+    packet.frame.where.actor
+  );
 
-  await setBreakpoint(source, {
-    line: 2,
-  });
+  await setBreakpoint(bpSource, { line: 2 });
   await resume(gThreadClient);
 
-  const { sources } = await getSources(gThreadClient);
-  const sourceClient = gThreadClient.source(
-    sources.filter(s => s.url == BLACK_BOXED_URL)[0]
-  );
+  const sourceClient = await getSource(gThreadClient, BLACK_BOXED_URL);
 
   Assert.ok(!sourceClient.isBlackBoxed,
             "By default the source is not black boxed.");
 
   // Test that we can step into `doStuff` when we are not black boxed.
   await runTest(
-    function onSteppedLocation(location) {
-      Assert.equal(location.source.url, BLACK_BOXED_URL);
+    async function onSteppedLocation(location) {
+      const source = await getSourceFormById(gThreadClient, location.actor);
+      Assert.equal(source.url, BLACK_BOXED_URL);
       Assert.equal(location.line, 2);
     },
-    function onDebuggerStatementFrames(frames) {
-      Assert.ok(!frames.some(f => f.where.source.isBlackBoxed));
+    async function onDebuggerStatementFrames(frames) {
+      for (const frame of frames) {
+        const source = await getSourceFormById(gThreadClient, frame.where.actor);
+        Assert.ok(!source.isBlackBoxed);
+      }
     }
   );
 
   await blackBox(sourceClient);
   Assert.ok(sourceClient.isBlackBoxed);
 
   // Test that we step through `doStuff` when we are black boxed and its frame
   // doesn't show up.
   await runTest(
-    function onSteppedLocation(location) {
-      Assert.equal(location.source.url, SOURCE_URL);
+    async function onSteppedLocation(location) {
+      const source = await getSourceFormById(gThreadClient, location.actor);
+      Assert.equal(source.url, SOURCE_URL);
       Assert.equal(location.line, 4);
     },
-    function onDebuggerStatementFrames(frames) {
-      for (const f of frames) {
-        if (f.where.source.url == BLACK_BOXED_URL) {
-          Assert.ok(f.where.source.isBlackBoxed);
+    async function onDebuggerStatementFrames(frames) {
+      for (const frame of frames) {
+        const source = await getSourceFormById(gThreadClient, frame.where.actor);
+        if (source.url == BLACK_BOXED_URL) {
+          Assert.ok(source.isBlackBoxed);
         } else {
-          Assert.ok(!f.where.source.isBlackBoxed);
+          Assert.ok(!source.isBlackBoxed);
         }
       }
     }
   );
 
   await unBlackBox(sourceClient);
   Assert.ok(!sourceClient.isBlackBoxed);
 
   // Test that we can step into `doStuff` again.
   await runTest(
-    function onSteppedLocation(location) {
-      Assert.equal(location.source.url, BLACK_BOXED_URL);
+    async function onSteppedLocation(location) {
+      const source = await getSourceFormById(gThreadClient, location.actor);
+      Assert.equal(source.url, BLACK_BOXED_URL);
       Assert.equal(location.line, 2);
     },
-    function onDebuggerStatementFrames(frames) {
-      Assert.ok(!frames.some(f => f.where.source.isBlackBoxed));
+    async function onDebuggerStatementFrames(frames) {
+      for (const frame of frames) {
+        const source = await getSourceFormById(gThreadClient, frame.where.actor);
+        Assert.ok(!source.isBlackBoxed);
+      }
     }
   );
 
   finishClient(gClient);
 };
 
 function evalCode() {
   /* eslint-disable */
@@ -127,23 +135,23 @@ function evalCode() {
 const runTest = async function(onSteppedLocation, onDebuggerStatementFrames) {
   let packet = await executeOnNextTickAndWaitForPause(gDebuggee.runTest,
                                                       gClient);
   Assert.equal(packet.why.type, "breakpoint");
 
   await stepIn(gClient, gThreadClient);
 
   const location = await getCurrentLocation();
-  onSteppedLocation(location);
+  await onSteppedLocation(location);
 
   packet = await resumeAndWaitForPause(gClient, gThreadClient);
   Assert.equal(packet.why.type, "debuggerStatement");
 
   const { frames } = await getFrames(gThreadClient, 0, 100);
-  onDebuggerStatementFrames(frames);
+  await onDebuggerStatementFrames(frames);
 
   return resume(gThreadClient);
 };
 
 const getCurrentLocation = async function() {
   const response = await getFrames(gThreadClient, 0, 1);
   return response.frames[0].where;
 };
--- a/devtools/server/tests/unit/test_blackboxing-03.js
+++ b/devtools/server/tests/unit/test_blackboxing-03.js
@@ -26,24 +26,22 @@ function run_test() {
   });
   do_test_pending();
 }
 
 const BLACK_BOXED_URL = "http://example.com/blackboxme.js";
 const SOURCE_URL = "http://example.com/source.js";
 
 function test_black_box() {
-  gClient.addOneTimeListener("paused", function(event, packet) {
-    const source = gThreadClient.source(packet.frame.where.source);
-    source.setBreakpoint({
-      line: 4,
-    }).then(function([response, bpClient]) {
-      gBpClient = bpClient;
-      gThreadClient.resume(test_black_box_dbg_statement);
-    });
+  gClient.addOneTimeListener("paused", async function(event, packet) {
+    const source = await getSourceById(gThreadClient, packet.frame.where.actor);
+    const [, bpClient] = await source.setBreakpoint({ line: 4 });
+    gBpClient = bpClient;
+    await gThreadClient.resume();
+    test_black_box_dbg_statement();
   });
 
   /* eslint-disable no-multi-spaces, no-undef */
   Cu.evalInSandbox(
     "" + function doStuff(k) { // line 1
       debugger;                // line 2 - Break here
       k(100);                  // line 3
     },                         // line 4
@@ -68,29 +66,27 @@ function test_black_box() {
     1
   );
   /* eslint-enable no-multi-spaces, no-undef */
 }
 
 function test_black_box_dbg_statement() {
   gThreadClient.getSources(async function({error, sources}) {
     Assert.ok(!error, "Should not get an error: " + error);
-    const sourceClient = gThreadClient.source(
-      sources.filter(s => s.url == BLACK_BOXED_URL)[0]
-    );
+    const sourceClient = await getSource(gThreadClient, BLACK_BOXED_URL);
 
     await blackBox(sourceClient);
 
-    gClient.addOneTimeListener("paused", function(event, packet) {
+    gThreadClient.addOneTimeListener("paused", async function(event, packet) {
       Assert.equal(packet.why.type, "breakpoint",
                    "We should pass over the debugger statement.");
-      gBpClient.remove(function({error}) {
-        Assert.ok(!error, "Should not get an error: " + error);
-        gThreadClient.resume(test_unblack_box_dbg_statement.bind(null, sourceClient));
-      });
+
+      await gBpClient.remove();
+      await gThreadClient.resume();
+      await test_unblack_box_dbg_statement(sourceClient);
     });
     gDebuggee.runTest();
   });
 }
 
 async function test_unblack_box_dbg_statement(sourceClient) {
   await unBlackBox(sourceClient);
 
--- a/devtools/server/tests/unit/test_blackboxing-05.js
+++ b/devtools/server/tests/unit/test_blackboxing-05.js
@@ -66,24 +66,23 @@ function test_black_box() {
     1
   );
   /* eslint-enable no-multi-spaces, no-unreachable, no-undef */
 }
 
 function test_black_box_exception() {
   gThreadClient.getSources(async function({error, sources}) {
     Assert.ok(!error, "Should not get an error: " + error);
-    const sourceClient = gThreadClient.source(
-      sources.filter(s => s.url == BLACK_BOXED_URL)[0]
-    );
-
+    const sourceClient = await getSource(gThreadClient, BLACK_BOXED_URL);
     await blackBox(sourceClient);
     gThreadClient.pauseOnExceptions(true);
 
-    gClient.addOneTimeListener("paused", function(event, packet) {
-      Assert.equal(packet.frame.where.source.url, SOURCE_URL,
+    gClient.addOneTimeListener("paused", async function(event, packet) {
+      const source = await getSourceById(gThreadClient, packet.frame.where.actor);
+
+      Assert.equal(source.url, SOURCE_URL,
                    "We shouldn't pause while in the black boxed source.");
       finishClient(gClient);
     });
 
     gThreadClient.resume();
   });
 }
--- a/devtools/server/tests/unit/test_breakpoint-01.js
+++ b/devtools/server/tests/unit/test_breakpoint-01.js
@@ -3,50 +3,49 @@
 /* eslint-disable no-shadow, max-nested-callbacks */
 
 "use strict";
 
 /**
  * Check basic breakpoint functionality.
  */
 
-add_task(threadClientTest(({ threadClient, debuggee }) => {
-  return new Promise(resolve => {
-    threadClient.addOneTimeListener("paused", function(event, packet) {
-      const source = threadClient.source(packet.frame.where.source);
-      const location = {
-        line: debuggee.line0 + 3,
-      };
+add_task(threadClientTest(async ({ threadClient, debuggee }) => {
+  (async () => {
+    info("Wait for the debugger statement to be hit");
+    let packet = await waitForPause(threadClient);
+    const source = await getSourceById(
+      threadClient,
+      packet.frame.where.actor
+    );
 
-      source.setBreakpoint(location).then(function([response, bpClient]) {
-        threadClient.addOneTimeListener("paused", function(event, packet) {
-          // Check the return value.
-          Assert.equal(packet.type, "paused");
-          Assert.equal(packet.frame.where.source.actor, source.actor);
-          Assert.equal(packet.frame.where.line, location.line);
-          Assert.equal(packet.why.type, "breakpoint");
-          Assert.equal(packet.why.actors[0], bpClient.actor);
-          // Check that the breakpoint worked.
-          Assert.equal(debuggee.a, 1);
-          Assert.equal(debuggee.b, undefined);
+    const location = { line: debuggee.line0 + 3 };
+
+    const [, bpClient] = await source.setBreakpoint(location);
+
+    await threadClient.resume();
+    packet = await waitForPause(threadClient);
 
-          // Remove the breakpoint.
-          bpClient.remove(function(response) {
-            threadClient.resume(resolve);
-          });
-        });
+    info("Paused at the breakpoint");
+    Assert.equal(packet.type, "paused");
+    Assert.equal(packet.frame.where.actor, source.actor);
+    Assert.equal(packet.frame.where.line, location.line);
+    Assert.equal(packet.why.type, "breakpoint");
+    Assert.equal(packet.why.actors[0], bpClient.actor);
 
-        // Continue until the breakpoint is hit.
-        threadClient.resume();
-      });
-    });
+    info("Check that the breakpoint worked.");
+    Assert.equal(debuggee.a, 1);
+    Assert.equal(debuggee.b, undefined);
 
-    /* eslint-disable */
-    Cu.evalInSandbox(
-      "var line0 = Error().lineNumber;\n" +
-      "debugger;\n" +   // line0 + 1
-      "var a = 1;\n" +  // line0 + 2
-      "var b = 2;\n",   // line0 + 3
-      debuggee
-    );
-    /* eslint-enable */
-  });
+    await bpClient.remove();
+    await threadClient.resume();
+  })();
+
+  /* eslint-disable */
+  Cu.evalInSandbox(
+    "var line0 = Error().lineNumber;\n" +
+    "debugger;\n" +   // line0 + 1
+    "var a = 1;\n" +  // line0 + 2
+    "var b = 2;\n",   // line0 + 3
+     debuggee
+  );
+  /* eslint-enable */
 }));
--- a/devtools/server/tests/unit/test_breakpoint-02.js
+++ b/devtools/server/tests/unit/test_breakpoint-02.js
@@ -4,29 +4,32 @@
 
 "use strict";
 
 /**
  * Check that setting breakpoints when the debuggee is running works.
  */
 
 add_task(threadClientTest(({ threadClient, debuggee }) => {
-  return new Promise(resolve => {
-    threadClient.addOneTimeListener("paused", function(event, packet) {
+  return new Promise((resolve) => {
+    threadClient.addOneTimeListener("paused", async function(event, packet) {
       const location = { line: debuggee.line0 + 3 };
+      const source = await getSourceById(
+        threadClient,
+        packet.frame.where.actor
+      );
 
       threadClient.resume();
 
       // Setting the breakpoint later should interrupt the debuggee.
       threadClient.addOneTimeListener("paused", function(event, packet) {
         Assert.equal(packet.type, "paused");
         Assert.equal(packet.why.type, "interrupted");
       });
 
-      const source = threadClient.source(packet.frame.where.source);
       source.setBreakpoint(location).then(function() {
         executeSoon(resolve);
       }, function(response) {
         // Eval scripts don't stick around long enough for the breakpoint to be set,
         // so just make sure we got the expected response from the actor.
         Assert.notEqual(response.error, "noScript");
 
         executeSoon(resolve);
--- a/devtools/server/tests/unit/test_breakpoint-03.js
+++ b/devtools/server/tests/unit/test_breakpoint-03.js
@@ -19,32 +19,34 @@ var test_no_skip_breakpoint = async func
   Assert.equal(bpClient.location.line, debuggee.line0 + 3);
   await bpClient.remove();
 };
 
 add_task(threadClientTest(({ threadClient, debuggee }) => {
   return new Promise(resolve => {
     threadClient.addOneTimeListener("paused", async function(event, packet) {
       const location = { line: debuggee.line0 + 3 };
-      const source = threadClient.source(packet.frame.where.source);
-
+      const source = await getSourceById(
+        threadClient,
+        packet.frame.where.actor
+      );
       // First, make sure that we can disable sliding with the
       // `noSliding` option.
       await test_no_skip_breakpoint(source, location, debuggee);
 
       // Now make sure that the breakpoint properly slides forward one line.
       const [response, bpClient] = await source.setBreakpoint(location);
       Assert.ok(!!response.actualLocation);
       Assert.equal(response.actualLocation.source.actor, source.actor);
       Assert.equal(response.actualLocation.line, location.line + 1);
 
       threadClient.addOneTimeListener("paused", function(event, packet) {
         // Check the return value.
         Assert.equal(packet.type, "paused");
-        Assert.equal(packet.frame.where.source.actor, source.actor);
+        Assert.equal(packet.frame.where.actor, source.actor);
         Assert.equal(packet.frame.where.line, location.line + 1);
         Assert.equal(packet.why.type, "breakpoint");
         Assert.equal(packet.why.actors[0], bpClient.actor);
         // Check that the breakpoint worked.
         Assert.equal(debuggee.a, 1);
         Assert.equal(debuggee.b, undefined);
 
         // Remove the breakpoint.
--- a/devtools/server/tests/unit/test_breakpoint-04.js
+++ b/devtools/server/tests/unit/test_breakpoint-04.js
@@ -5,28 +5,31 @@
 "use strict";
 
 /**
  * Check that setting a breakpoint in a line in a child script works.
  */
 
 add_task(threadClientTest(({ threadClient, debuggee }) => {
   return new Promise(resolve => {
-    threadClient.addOneTimeListener("paused", function(event, packet) {
-      const source = threadClient.source(packet.frame.where.source);
+    threadClient.addOneTimeListener("paused", async function(event, packet) {
+      const source = await getSourceById(
+        threadClient,
+        packet.frame.where.actor
+      );
       const location = { line: debuggee.line0 + 3 };
 
       source.setBreakpoint(location).then(function([response, bpClient]) {
         // actualLocation is not returned when breakpoints don't skip forward.
         Assert.equal(response.actualLocation, undefined);
 
         threadClient.addOneTimeListener("paused", function(event, packet) {
           // Check the return value.
           Assert.equal(packet.type, "paused");
-          Assert.equal(packet.frame.where.source.actor, source.actor);
+          Assert.equal(packet.frame.where.actor, source.actor);
           Assert.equal(packet.frame.where.line, location.line);
           Assert.equal(packet.why.type, "breakpoint");
           Assert.equal(packet.why.actors[0], bpClient.actor);
           // Check that the breakpoint worked.
           Assert.equal(debuggee.a, 1);
           Assert.equal(debuggee.b, undefined);
 
           // Remove the breakpoint.
--- a/devtools/server/tests/unit/test_breakpoint-05.js
+++ b/devtools/server/tests/unit/test_breakpoint-05.js
@@ -6,29 +6,32 @@
 
 /**
  * Check that setting a breakpoint in a line without code in a child script
  * will skip forward.
  */
 
 add_task(threadClientTest(({ threadClient, debuggee }) => {
   return new Promise(resolve => {
-    threadClient.addOneTimeListener("paused", function(event, packet) {
-      const source = threadClient.source(packet.frame.where.source);
+    threadClient.addOneTimeListener("paused", async function(event, packet) {
+      const source = await getSourceById(
+        threadClient,
+        packet.frame.where.actor
+      );
       const location = { line: debuggee.line0 + 3 };
 
       source.setBreakpoint(location).then(function([response, bpClient]) {
         // Check that the breakpoint has properly skipped forward one line.
         Assert.equal(response.actualLocation.source.actor, source.actor);
         Assert.equal(response.actualLocation.line, location.line + 1);
 
         threadClient.addOneTimeListener("paused", function(event, packet) {
           // Check the return value.
           Assert.equal(packet.type, "paused");
-          Assert.equal(packet.frame.where.source.actor, source.actor);
+          Assert.equal(packet.frame.where.actor, source.actor);
           Assert.equal(packet.frame.where.line, location.line + 1);
           Assert.equal(packet.why.type, "breakpoint");
           Assert.equal(packet.why.actors[0], bpClient.actor);
           // Check that the breakpoint worked.
           Assert.equal(debuggee.a, 1);
           Assert.equal(debuggee.b, undefined);
 
           // Remove the breakpoint.
--- a/devtools/server/tests/unit/test_breakpoint-06.js
+++ b/devtools/server/tests/unit/test_breakpoint-06.js
@@ -6,29 +6,32 @@
 
 /**
  * Check that setting a breakpoint in a line without code in a deeply-nested
  * child script will skip forward.
  */
 
 add_task(threadClientTest(({ threadClient, debuggee }) => {
   return new Promise(resolve => {
-    threadClient.addOneTimeListener("paused", function(event, packet) {
-      const source = threadClient.source(packet.frame.where.source);
+    threadClient.addOneTimeListener("paused", async function(event, packet) {
+      const source = await getSourceById(
+        threadClient,
+        packet.frame.where.actor
+      );
       const location = { line: debuggee.line0 + 5 };
 
       source.setBreakpoint(location).then(function([response, bpClient]) {
         // Check that the breakpoint has properly skipped forward one line.
         Assert.equal(response.actualLocation.source.actor, source.actor);
         Assert.equal(response.actualLocation.line, location.line + 1);
 
         threadClient.addOneTimeListener("paused", function(event, packet) {
           // Check the return value.
           Assert.equal(packet.type, "paused");
-          Assert.equal(packet.frame.where.source.actor, source.actor);
+          Assert.equal(packet.frame.where.actor, source.actor);
           Assert.equal(packet.frame.where.line, location.line + 1);
           Assert.equal(packet.why.type, "breakpoint");
           Assert.equal(packet.why.actors[0], bpClient.actor);
           // Check that the breakpoint worked.
           Assert.equal(debuggee.a, 1);
           Assert.equal(debuggee.b, undefined);
 
           // Remove the breakpoint.
--- a/devtools/server/tests/unit/test_breakpoint-07.js
+++ b/devtools/server/tests/unit/test_breakpoint-07.js
@@ -6,29 +6,32 @@
 
 /**
  * Check that setting a breakpoint in a line without code in the second child
  * script will skip forward.
  */
 
 add_task(threadClientTest(({ threadClient, debuggee }) => {
   return new Promise(resolve => {
-    threadClient.addOneTimeListener("paused", function(event, packet) {
-      const source = threadClient.source(packet.frame.where.source);
+    threadClient.addOneTimeListener("paused", async function(event, packet) {
+      const source = await getSourceById(
+        threadClient,
+        packet.frame.where.actor
+      );
       const location = { line: debuggee.line0 + 6 };
 
       source.setBreakpoint(location).then(function([response, bpClient]) {
         // Check that the breakpoint has properly skipped forward one line.
         Assert.equal(response.actualLocation.source.actor, source.actor);
         Assert.equal(response.actualLocation.line, location.line + 1);
 
         threadClient.addOneTimeListener("paused", function(event, packet) {
           // Check the return value.
           Assert.equal(packet.type, "paused");
-          Assert.equal(packet.frame.where.source.actor, source.actor);
+          Assert.equal(packet.frame.where.actor, source.actor);
           Assert.equal(packet.frame.where.line, location.line + 1);
           Assert.equal(packet.why.type, "breakpoint");
           Assert.equal(packet.why.actors[0], bpClient.actor);
           // Check that the breakpoint worked.
           Assert.equal(debuggee.a, 1);
           Assert.equal(debuggee.b, undefined);
 
           // Remove the breakpoint.
--- a/devtools/server/tests/unit/test_breakpoint-08.js
+++ b/devtools/server/tests/unit/test_breakpoint-08.js
@@ -8,52 +8,53 @@
  * Check that setting a breakpoint in a line without code in a child script
  * will skip forward, in a file with two scripts.
  */
 
 add_task(threadClientTest(({ threadClient, debuggee }) => {
   return new Promise(resolve => {
     threadClient.addOneTimeListener("paused", function(event, packet) {
       threadClient.eval(packet.frame.actor, "foo", function(response) {
-        threadClient.addOneTimeListener("paused", function(event, packet) {
+        threadClient.addOneTimeListener("paused", async function(event, packet) {
           const obj = threadClient.pauseGrip(packet.why.frameFinished.return);
-          obj.getDefinitionSite(runWithBreakpoint);
+          const site = await obj.getDefinitionSite();
+
+          const location = { line: debuggee.line0 + 3 };
+          const source = await getSourceById(
+            threadClient,
+            site.source.actor
+          );
+
+          source.setBreakpoint(location).then(function([response, bpClient]) {
+            // Check that the breakpoint has properly skipped forward one line.
+            Assert.equal(response.actualLocation.source.actor, source.actor);
+            Assert.equal(response.actualLocation.line, location.line + 1);
+
+            threadClient.addOneTimeListener("paused", function(event, packet) {
+              // Check the return value.
+              Assert.equal(packet.type, "paused");
+              Assert.equal(packet.frame.where.actor, source.actor);
+              Assert.equal(packet.frame.where.line, location.line + 1);
+              Assert.equal(packet.why.type, "breakpoint");
+              Assert.equal(packet.why.actors[0], bpClient.actor);
+              // Check that the breakpoint worked.
+              Assert.equal(debuggee.a, 1);
+              Assert.equal(debuggee.b, undefined);
+
+              // Remove the breakpoint.
+              bpClient.remove(function(response) {
+                threadClient.resume(resolve);
+              });
+            });
+
+            // Continue until the breakpoint is hit.
+            threadClient.resume();
+          });
         });
       });
-
-      function runWithBreakpoint(packet) {
-        const source = threadClient.source(packet.source);
-        const location = { line: debuggee.line0 + 3 };
-
-        source.setBreakpoint(location).then(function([response, bpClient]) {
-          // Check that the breakpoint has properly skipped forward one line.
-          Assert.equal(response.actualLocation.source.actor, source.actor);
-          Assert.equal(response.actualLocation.line, location.line + 1);
-
-          threadClient.addOneTimeListener("paused", function(event, packet) {
-            // Check the return value.
-            Assert.equal(packet.type, "paused");
-            Assert.equal(packet.frame.where.source.actor, source.actor);
-            Assert.equal(packet.frame.where.line, location.line + 1);
-            Assert.equal(packet.why.type, "breakpoint");
-            Assert.equal(packet.why.actors[0], bpClient.actor);
-            // Check that the breakpoint worked.
-            Assert.equal(debuggee.a, 1);
-            Assert.equal(debuggee.b, undefined);
-
-            // Remove the breakpoint.
-            bpClient.remove(function(response) {
-              threadClient.resume(resolve);
-            });
-          });
-
-          // Continue until the breakpoint is hit.
-          threadClient.resume();
-        });
-      }
     });
 
     /* eslint-disable */
     Cu.evalInSandbox("var line0 = Error().lineNumber;\n" +
                      "function foo() {\n" + // line0 + 1
                      "  this.a = 1;\n" +    // line0 + 2
                      "  // A comment.\n" +  // line0 + 3
                      "  this.b = 2;\n" +    // line0 + 4
--- a/devtools/server/tests/unit/test_breakpoint-09.js
+++ b/devtools/server/tests/unit/test_breakpoint-09.js
@@ -6,25 +6,28 @@
 
 /**
  * Check that removing a breakpoint works.
  */
 
 add_task(threadClientTest(({ threadClient, debuggee }) => {
   return new Promise(resolve => {
     let done = false;
-    threadClient.addOneTimeListener("paused", function(event, packet) {
-      const source = threadClient.source(packet.frame.where.source);
+    threadClient.addOneTimeListener("paused", async function(event, packet) {
+      const source = await getSourceById(
+        threadClient,
+        packet.frame.where.actor
+      );
       const location = { line: debuggee.line0 + 2 };
 
       source.setBreakpoint(location).then(function([response, bpClient]) {
         threadClient.addOneTimeListener("paused", function(event, packet) {
           // Check the return value.
           Assert.equal(packet.type, "paused");
-          Assert.equal(packet.frame.where.source.actor, source.actor);
+          Assert.equal(packet.frame.where.actor, source.actor);
           Assert.equal(packet.frame.where.line, location.line);
           Assert.equal(packet.why.type, "breakpoint");
           Assert.equal(packet.why.actors[0], bpClient.actor);
           // Check that the breakpoint worked.
           Assert.equal(debuggee.a, undefined);
 
           // Remove the breakpoint.
           bpClient.remove(function(response) {
--- a/devtools/server/tests/unit/test_breakpoint-10.js
+++ b/devtools/server/tests/unit/test_breakpoint-10.js
@@ -6,18 +6,21 @@
 
 /**
  * Check that setting a breakpoint in a line with multiple entry points
  * triggers no matter which entry point we reach.
  */
 
 add_task(threadClientTest(({ threadClient, debuggee }) => {
   return new Promise(resolve => {
-    threadClient.addOneTimeListener("paused", function(event, packet) {
-      const source = threadClient.source(packet.frame.where.source);
+    threadClient.addOneTimeListener("paused", async function(event, packet) {
+      const source = await getSourceById(
+        threadClient,
+        packet.frame.where.actor
+      );
       const location = { line: debuggee.line0 + 3 };
 
       source.setBreakpoint(location).then(function([response, bpClient]) {
         // actualLocation is not returned when breakpoints don't skip forward.
         Assert.equal(response.actualLocation, undefined);
 
         threadClient.addOneTimeListener("paused", function(event, packet) {
           // Check the return value.
--- a/devtools/server/tests/unit/test_breakpoint-11.js
+++ b/devtools/server/tests/unit/test_breakpoint-11.js
@@ -6,18 +6,21 @@
 
 /**
  * Make sure that setting a breakpoint in a line with bytecodes in multiple
  * scripts, sets the breakpoint in all of them (bug 793214).
  */
 
 add_task(threadClientTest(({ threadClient, debuggee }) => {
   return new Promise(resolve => {
-    threadClient.addOneTimeListener("paused", function(event, packet) {
-      const source = threadClient.source(packet.frame.where.source);
+    threadClient.addOneTimeListener("paused", async function(event, packet) {
+      const source = await getSourceById(
+        threadClient,
+        packet.frame.where.actor
+      );
       const location = { line: debuggee.line0 + 2 };
 
       source.setBreakpoint(location).then(function([response, bpClient]) {
         // actualLocation is not returned when breakpoints don't skip forward.
         Assert.equal(response.actualLocation, undefined);
 
         threadClient.addOneTimeListener("paused", function(event, packet) {
           // Check the return value.
--- a/devtools/server/tests/unit/test_breakpoint-12.js
+++ b/devtools/server/tests/unit/test_breakpoint-12.js
@@ -10,18 +10,21 @@
  */
 
 const NUM_BREAKPOINTS = 10;
 var gBpActor;
 var gCount;
 
 add_task(threadClientTest(({ threadClient, debuggee }) => {
   return new Promise(resolve => {
-    threadClient.addOneTimeListener("paused", function(event, packet) {
-      const source = threadClient.source(packet.frame.where.source);
+    threadClient.addOneTimeListener("paused", async function(event, packet) {
+      const source = await getSourceById(
+        threadClient,
+        packet.frame.where.actor
+      );
       const location = { line: debuggee.line0 + 3};
 
       source.setBreakpoint(location).then(function([response, bpClient]) {
         // Check that the breakpoint has properly skipped forward one line.
         Assert.equal(response.actualLocation.source.actor, source.actor);
         Assert.equal(response.actualLocation.line, location.line + 1);
         gBpActor = response.actor;
 
@@ -57,17 +60,17 @@ add_task(threadClientTest(({ threadClien
           return;
         }
 
         // After setting all the breakpoints, check that only one has effectively
         // remained.
         threadClient.addOneTimeListener("paused", function(event, packet) {
           // Check the return value.
           Assert.equal(packet.type, "paused");
-          Assert.equal(packet.frame.where.source.actor, source.actor);
+          Assert.equal(packet.frame.where.actor, source.actor);
           Assert.equal(packet.frame.where.line, location.line + 1);
           Assert.equal(packet.why.type, "breakpoint");
           Assert.equal(packet.why.actors[0], bpClient.actor);
           // Check that the breakpoint worked.
           Assert.equal(debuggee.a, 1);
           Assert.equal(debuggee.b, undefined);
 
           threadClient.addOneTimeListener("paused", function(event, packet) {
--- a/devtools/server/tests/unit/test_breakpoint-13.js
+++ b/devtools/server/tests/unit/test_breakpoint-13.js
@@ -6,18 +6,21 @@
 
 /**
  * Check that execution doesn't pause twice while stepping, when encountering
  * either a breakpoint or a debugger statement.
  */
 
 add_task(threadClientTest(({ threadClient, debuggee }) => {
   return new Promise(resolve => {
-    threadClient.addOneTimeListener("paused", function(event, packet) {
-      const source = threadClient.source(packet.frame.where.source);
+    threadClient.addOneTimeListener("paused", async function(event, packet) {
+      const source = await getSourceById(
+        threadClient,
+        packet.frame.where.actor
+      );
       const location = { line: debuggee.line0 + 2 };
 
       source.setBreakpoint(location).then(async function([response, bpClient]) {
         const testCallbacks = [
           function(packet) {
             // Check that the stepping worked.
             Assert.equal(packet.frame.where.line, debuggee.line0 + 5);
             Assert.equal(packet.why.type, "resumeLimit");
--- a/devtools/server/tests/unit/test_breakpoint-14.js
+++ b/devtools/server/tests/unit/test_breakpoint-14.js
@@ -6,18 +6,21 @@
 
 /**
  * Check that a breakpoint or a debugger statement cause execution to pause even
  * in a stepped-over function.
  */
 
 add_task(threadClientTest(({ threadClient, debuggee }) => {
   return new Promise(resolve => {
-    threadClient.addOneTimeListener("paused", function(event, packet) {
-      const source = threadClient.source(packet.frame.where.source);
+    threadClient.addOneTimeListener("paused", async function(event, packet) {
+      const source = await getSourceById(
+        threadClient,
+        packet.frame.where.actor
+      );
       const location = { line: debuggee.line0 + 2 };
 
       source.setBreakpoint(location).then(async function([response, bpClient]) {
         const testCallbacks = [
           function(packet) {
             // Check that the stepping worked.
             Assert.equal(packet.frame.where.line, debuggee.line0 + 5);
             Assert.equal(packet.why.type, "resumeLimit");
--- a/devtools/server/tests/unit/test_breakpoint-15.js
+++ b/devtools/server/tests/unit/test_breakpoint-15.js
@@ -24,17 +24,20 @@ function run_test() {
   });
   do_test_pending();
 }
 
 const SOURCE_URL = "http://example.com/source.js";
 
 const testSameBreakpoint = async function() {
   const packet = await executeOnNextTickAndWaitForPause(evalCode, gClient);
-  const source = gThreadClient.source(packet.frame.where.source);
+  const source = await getSourceById(
+    gThreadClient,
+    packet.frame.where.actor
+  );
 
   // Whole line
   const wholeLineLocation = {
     line: 2,
   };
 
   let [, firstBpClient] = await setBreakpoint(source, wholeLineLocation);
   let [, secondBpClient] = await setBreakpoint(source, wholeLineLocation);
--- a/devtools/server/tests/unit/test_breakpoint-16.js
+++ b/devtools/server/tests/unit/test_breakpoint-16.js
@@ -6,30 +6,33 @@
 
 /**
  * Check that we can set breakpoints in columns, not just lines.
  */
 
 add_task(threadClientTest(({ threadClient, debuggee, client }) => {
   return new Promise(resolve => {
     // Debugger statement
-    client.addOneTimeListener("paused", function(event, packet) {
-      const source = threadClient.source(packet.frame.where.source);
+    client.addOneTimeListener("paused", async function(event, packet) {
+      const source = await getSourceById(
+        threadClient,
+        packet.frame.where.actor
+      );
       const location = {
         line: debuggee.line0 + 1,
         column: 55,
       };
       let timesBreakpointHit = 0;
 
       source.setBreakpoint(location).then(function([response, bpClient]) {
         threadClient.addListener("paused", function onPaused(event, packet) {
           Assert.equal(packet.type, "paused");
           Assert.equal(packet.why.type, "breakpoint");
           Assert.equal(packet.why.actors[0], bpClient.actor);
-          Assert.equal(packet.frame.where.source.actor, source.actor);
+          Assert.equal(packet.frame.where.actor, source.actor);
           Assert.equal(packet.frame.where.line, location.line);
           Assert.equal(packet.frame.where.column, location.column);
 
           Assert.equal(debuggee.acc, timesBreakpointHit);
           Assert.equal(packet.frame.environment.bindings.variables.i.value,
                        timesBreakpointHit);
 
           if (++timesBreakpointHit === 3) {
--- a/devtools/server/tests/unit/test_breakpoint-17.js
+++ b/devtools/server/tests/unit/test_breakpoint-17.js
@@ -37,19 +37,22 @@ add_task(threadClientTest(({ threadClien
       resolve();
     });
 
     Cu.evalInSandbox(code, debuggee, "1.8", "http://example.com/", 1);
   });
 }));
 
 function set_breakpoints(packet, threadClient) {
-  return new Promise(resolve => {
+  return new Promise(async resolve => {
     let first, second;
-    const source = threadClient.source(packet.frame.where.source);
+    const source = await getSourceById(
+      threadClient,
+      packet.frame.where.actor
+    );
 
     source.setBreakpoint(firstLocation).then(function([{ actualLocation },
                                                        breakpointClient]) {
       Assert.ok(!actualLocation, "Should not get an actualLocation");
       first = breakpointClient;
 
       source.setBreakpoint(secondLocation).then(function([{ actualLocation },
                                                           breakpointClient]) {
--- a/devtools/server/tests/unit/test_breakpoint-18.js
+++ b/devtools/server/tests/unit/test_breakpoint-18.js
@@ -39,18 +39,21 @@ add_task(threadClientTest(({ threadClien
       "1.8",
       "http://example.com/",
       1
     );
   });
 }));
 
 function setBreakpoint(packet, threadClient, client) {
-  return new Promise(resolve => {
-    const source = threadClient.source(packet.frame.where.source);
+  return new Promise(async resolve => {
+    const source = await getSourceById(
+      threadClient,
+      packet.frame.where.actor
+    );
     client.addOneTimeListener("resumed", resolve);
 
     source.setBreakpoint({ line: 2 }).then(() => {
       threadClient.resume();
     });
   });
 }
 
--- a/devtools/server/tests/unit/test_breakpoint-21.js
+++ b/devtools/server/tests/unit/test_breakpoint-21.js
@@ -12,30 +12,33 @@
 add_task(threadClientTest(async ({ threadClient, debuggee, client }) => {
   // Populate the `ScriptStore` so that we only test that the script
   // is added through `onNewScript`
   await getSources(threadClient);
 
   let packet = await executeOnNextTickAndWaitForPause(() => {
     evalCode(debuggee);
   }, client);
-  const source = threadClient.source(packet.frame.where.source);
+  const source = await getSourceById(
+      threadClient,
+      packet.frame.where.actor
+    );
   const location = {
     line: debuggee.line0 + 8,
   };
 
   const [res, bpClient] = await setBreakpoint(source, location);
   ok(!res.error);
 
   await resume(threadClient);
   packet = await waitForPause(client);
   Assert.equal(packet.type, "paused");
   Assert.equal(packet.why.type, "breakpoint");
   Assert.equal(packet.why.actors[0], bpClient.actor);
-  Assert.equal(packet.frame.where.source.actor, source.actor);
+  Assert.equal(packet.frame.where.actor, source.actor);
   Assert.equal(packet.frame.where.line, location.line);
 
   await resume(threadClient);
 }));
 
 /* eslint-disable */
 function evalCode(debuggee) {
   // Start a new script
--- a/devtools/server/tests/unit/test_breakpoint-22.js
+++ b/devtools/server/tests/unit/test_breakpoint-22.js
@@ -11,17 +11,21 @@
 add_task(threadClientTest(async ({ threadClient, debuggee, client }) => {
   // Populate the `ScriptStore` so that we only test that the script
   // is added through `onNewScript`
   await getSources(threadClient);
 
   const packet = await executeOnNextTickAndWaitForPause(() => {
     evalCode(debuggee);
   }, client);
-  const source = threadClient.source(packet.frame.where.source);
+  const source = await getSourceById(
+    threadClient,
+    packet.frame.where.actor
+  );
+
   const location = {
     line: debuggee.line0 + 2,
   };
 
   const [res ] = await setBreakpoint(source, location);
   ok(!res.error);
 
   const location2 = {
--- a/devtools/server/tests/unit/test_conditional_breakpoint-01.js
+++ b/devtools/server/tests/unit/test_conditional_breakpoint-01.js
@@ -22,18 +22,21 @@ function run_test() {
                              gThreadClient = threadClient;
                              test_simple_breakpoint();
                            });
   });
   do_test_pending();
 }
 
 function test_simple_breakpoint() {
-  gThreadClient.addOneTimeListener("paused", function(event, packet) {
-    const source = gThreadClient.source(packet.frame.where.source);
+  gThreadClient.addOneTimeListener("paused", async function(event, packet) {
+    const source = await getSourceById(
+      gThreadClient,
+      packet.frame.where.actor
+    );
     source.setBreakpoint({
       line: 3,
       condition: "a === 1",
     }).then(function([response, bpClient]) {
       gThreadClient.addOneTimeListener("paused", function(event, packet) {
         // Check the return value.
         Assert.equal(packet.why.type, "breakpoint");
         Assert.equal(packet.frame.where.line, 3);
--- a/devtools/server/tests/unit/test_conditional_breakpoint-02.js
+++ b/devtools/server/tests/unit/test_conditional_breakpoint-02.js
@@ -22,18 +22,21 @@ function run_test() {
                              gThreadClient = threadClient;
                              test_simple_breakpoint();
                            });
   });
   do_test_pending();
 }
 
 function test_simple_breakpoint() {
-  gThreadClient.addOneTimeListener("paused", function(event, packet) {
-    const source = gThreadClient.source(packet.frame.where.source);
+  gThreadClient.addOneTimeListener("paused", async function(event, packet) {
+    const source = await getSourceById(
+      gThreadClient,
+      packet.frame.where.actor
+    );
     source.setBreakpoint({
       line: 3,
       condition: "a === 2",
     }).then(function([response, bpClient]) {
       gThreadClient.addOneTimeListener("paused", function(event, packet) {
         // Check the return value.
         Assert.equal(packet.why.type, "debuggerStatement");
         Assert.equal(packet.frame.where.line, 4);
--- a/devtools/server/tests/unit/test_conditional_breakpoint-03.js
+++ b/devtools/server/tests/unit/test_conditional_breakpoint-03.js
@@ -22,18 +22,21 @@ function run_test() {
                              gThreadClient = threadClient;
                              test_simple_breakpoint();
                            });
   });
   do_test_pending();
 }
 
 function test_simple_breakpoint() {
-  gThreadClient.addOneTimeListener("paused", function(event, packet) {
-    const source = gThreadClient.source(packet.frame.where.source);
+  gThreadClient.addOneTimeListener("paused", async function(event, packet) {
+    const source = await getSourceById(
+      gThreadClient,
+      packet.frame.where.actor
+    );
     source.setBreakpoint({
       line: 3,
       condition: "throw new Error()",
     }).then(function([response, bpClient]) {
       gThreadClient.addOneTimeListener("paused", function(event, packet) {
         // Check the return value.
         Assert.equal(packet.why.type, "breakpointConditionThrown");
         Assert.equal(packet.frame.where.line, 3);
--- a/devtools/server/tests/unit/test_frameactor_wasm-01.js
+++ b/devtools/server/tests/unit/test_frameactor_wasm-01.js
@@ -39,27 +39,28 @@ function run_test() {
         });
       });
   });
   do_test_pending();
 }
 
 function test_pause_frame() {
   gThreadClient.addOneTimeListener("paused", function(event, packet) {
-    gThreadClient.getFrames(0, null, function(frameResponse) {
+    gThreadClient.getFrames(0, null, async function(frameResponse) {
       Assert.equal(frameResponse.frames.length, 4);
 
       const wasmFrame = frameResponse.frames[1];
       Assert.equal(wasmFrame.type, "wasmcall");
       Assert.equal(wasmFrame.this, undefined);
 
       const location = wasmFrame.where;
+      const source = await getSourceById(gThreadClient, location.actor);
       Assert.equal(location.line > 0, true);
       Assert.equal(location.column > 0, true);
-      Assert.equal(/^wasm:(?:[^:]*:)*?[0-9a-f]{16}$/.test(location.source.url), true);
+      Assert.equal(/^wasm:(?:[^:]*:)*?[0-9a-f]{16}$/.test(source.url), true);
 
       finishClient(gClient);
     });
   });
 
   /* eslint-disable comma-spacing, max-len */
   gDebuggee.eval("(" + function() {
     // WebAssembly bytecode was generated by running:
--- a/devtools/server/tests/unit/test_setBreakpoint-at-the-beginning-of-a-line.js
+++ b/devtools/server/tests/unit/test_setBreakpoint-at-the-beginning-of-a-line.js
@@ -23,20 +23,19 @@ add_task(threadClientTest(async ({ threa
 
   Assert.equal(packet.type, "paused");
   const why = packet.why;
   Assert.equal(why.type, "breakpoint");
   Assert.equal(why.actors.length, 1);
   Assert.equal(why.actors[0], breakpointClient.actor);
 
   const frame = packet.frame;
-  const where = frame.where;
-  Assert.equal(where.source.actor, source.actor);
-  Assert.equal(where.line, location.line);
-  Assert.equal(where.column, 6);
+  Assert.equal(frame.where.actor, source.actor);
+  Assert.equal(frame.where.line, location.line);
+  Assert.equal(frame.where.column, 6);
 
   const variables = frame.environment.bindings.variables;
   Assert.equal(variables.a.value.type, "undefined");
   Assert.equal(variables.b.value.type, "undefined");
   Assert.equal(variables.c.value.type, "undefined");
 
   await resume(threadClient);
 }, { doNotRunWorker: true }));
--- a/devtools/server/tests/unit/test_setBreakpoint-at-the-beginning-of-a-minified-fn.js
+++ b/devtools/server/tests/unit/test_setBreakpoint-at-the-beginning-of-a-minified-fn.js
@@ -26,17 +26,17 @@ add_task(threadClientTest(async ({ threa
   Assert.equal(packet.type, "paused");
   const why = packet.why;
   Assert.equal(why.type, "breakpoint");
   Assert.equal(why.actors.length, 1);
   Assert.equal(why.actors[0], breakpointClient.actor);
 
   const frame = packet.frame;
   const where = frame.where;
-  Assert.equal(where.source.actor, source.actor);
+  Assert.equal(where.actor, source.actor);
   Assert.equal(where.line, location.line);
   Assert.equal(where.column, 52);
 
   const variables = frame.environment.bindings.variables;
   Assert.equal(variables.a.value.type, "undefined");
   Assert.equal(variables.b.value.type, "undefined");
   Assert.equal(variables.c.value.type, "undefined");
 
--- a/devtools/server/tests/unit/test_setBreakpoint-at-the-end-of-a-line.js
+++ b/devtools/server/tests/unit/test_setBreakpoint-at-the-end-of-a-line.js
@@ -24,17 +24,17 @@ add_task(threadClientTest(async ({ threa
   Assert.equal(packet.type, "paused");
   const why = packet.why;
   Assert.equal(why.type, "breakpoint");
   Assert.equal(why.actors.length, 1);
   Assert.equal(why.actors[0], breakpointClient.actor);
 
   const frame = packet.frame;
   const where = frame.where;
-  Assert.equal(where.source.actor, source.actor);
+  Assert.equal(where.actor, source.actor);
   Assert.equal(where.line, location.line);
   Assert.equal(where.column, 28);
 
   const variables = frame.environment.bindings.variables;
   Assert.equal(variables.a.value, 1);
   Assert.equal(variables.b.value, 2);
   Assert.equal(variables.c.value.type, "undefined");
 
--- a/devtools/server/tests/unit/test_setBreakpoint-at-the-end-of-a-minified-fn.js
+++ b/devtools/server/tests/unit/test_setBreakpoint-at-the-end-of-a-minified-fn.js
@@ -26,17 +26,17 @@ add_task(threadClientTest(async ({ threa
   Assert.equal(packet.type, "paused");
   const why = packet.why;
   Assert.equal(why.type, "breakpoint");
   Assert.equal(why.actors.length, 1);
   Assert.equal(why.actors[0], breakpointClient.actor);
 
   const frame = packet.frame;
   const where = frame.where;
-  Assert.equal(where.source.actor, source.actor);
+  Assert.equal(where.actor, source.actor);
   Assert.equal(where.line, location.line);
   Assert.equal(where.column, 81);
 
   const variables = frame.environment.bindings.variables;
   Assert.equal(variables.a.value, 1);
   Assert.equal(variables.b.value, 2);
   Assert.equal(variables.c.value, 3);
 
--- a/devtools/server/tests/unit/test_setBreakpoint-on-column-in-gcd-script.js
+++ b/devtools/server/tests/unit/test_setBreakpoint-on-column-in-gcd-script.js
@@ -22,17 +22,17 @@ add_task(threadClientTest(async ({ threa
   }, client);
   Assert.equal(packet.type, "paused");
   const why = packet.why;
   Assert.equal(why.type, "breakpoint");
   Assert.equal(why.actors.length, 1);
   Assert.equal(why.actors[0], breakpointClient.actor);
   const frame = packet.frame;
   const where = frame.where;
-  Assert.equal(where.source.actor, source.actor);
+  Assert.equal(where.actor, source.actor);
   Assert.equal(where.line, location.line);
   Assert.equal(where.column, location.column);
   const variables = frame.environment.bindings.variables;
   Assert.equal(variables.a.value, 1);
   Assert.equal(variables.b.value.type, "undefined");
   Assert.equal(variables.c.value.type, "undefined");
   await resume(threadClient);
 }, { doNotRunWorker: true }));
--- a/devtools/server/tests/unit/test_setBreakpoint-on-column.js
+++ b/devtools/server/tests/unit/test_setBreakpoint-on-column.js
@@ -18,17 +18,17 @@ add_task(threadClientTest(async ({ threa
   }, client);
   Assert.equal(packet.type, "paused");
   const why = packet.why;
   Assert.equal(why.type, "breakpoint");
   Assert.equal(why.actors.length, 1);
   Assert.equal(why.actors[0], breakpointClient.actor);
   const frame = packet.frame;
   const where = frame.where;
-  Assert.equal(where.source.actor, source.actor);
+  Assert.equal(where.actor, source.actor);
   Assert.equal(where.line, location.line);
   Assert.equal(where.column, location.column);
   const variables = frame.environment.bindings.variables;
   Assert.equal(variables.a.value, 1);
   Assert.equal(variables.b.value.type, "undefined");
   Assert.equal(variables.c.value.type, "undefined");
 
   await resume(threadClient);
--- a/devtools/server/tests/unit/test_setBreakpoint-on-line-in-gcd-script.js
+++ b/devtools/server/tests/unit/test_setBreakpoint-on-line-in-gcd-script.js
@@ -22,16 +22,16 @@ add_task(threadClientTest(async ({ threa
   }, client);
   Assert.equal(packet.type, "paused");
   const why = packet.why;
   Assert.equal(why.type, "breakpoint");
   Assert.equal(why.actors.length, 1);
   Assert.equal(why.actors[0], breakpointClient.actor);
   const frame = packet.frame;
   const where = frame.where;
-  Assert.equal(where.source.actor, source.actor);
+  Assert.equal(where.actor, source.actor);
   Assert.equal(where.line, location.line);
   const variables = frame.environment.bindings.variables;
   Assert.equal(variables.a.value, 1);
   Assert.equal(variables.b.value.type, "undefined");
   Assert.equal(variables.c.value.type, "undefined");
   await resume(threadClient);
 }, { doNotRunWorker: true }));
--- a/devtools/server/tests/unit/test_setBreakpoint-on-line-with-multiple-offsets.js
+++ b/devtools/server/tests/unit/test_setBreakpoint-on-line-with-multiple-offsets.js
@@ -18,30 +18,31 @@ add_task(threadClientTest(async ({ threa
   }, client);
   Assert.equal(packet.type, "paused");
   let why = packet.why;
   Assert.equal(why.type, "breakpoint");
   Assert.equal(why.actors.length, 1);
   Assert.equal(why.actors[0], breakpointClient.actor);
   let frame = packet.frame;
   let where = frame.where;
-  Assert.equal(where.source.actor, source.actor);
+  Assert.equal(where.actor, source.actor);
   Assert.equal(where.line, location.line);
   let variables = frame.environment.bindings.variables;
   Assert.equal(variables.i.value.type, "undefined");
 
-  packet = await executeOnNextTickAndWaitForPause(function() {
-    resume(threadClient);
-  }, client);
+  packet = await executeOnNextTickAndWaitForPause(
+    () => resume(threadClient),
+    client
+  );
   Assert.equal(packet.type, "paused");
   why = packet.why;
   Assert.equal(why.type, "breakpoint");
   Assert.equal(why.actors.length, 1);
   Assert.equal(why.actors[0], breakpointClient.actor);
   frame = packet.frame;
   where = frame.where;
-  Assert.equal(where.source.actor, source.actor);
+  Assert.equal(where.actor, source.actor);
   Assert.equal(where.line, location.line);
   variables = frame.environment.bindings.variables;
   Assert.equal(variables.i.value, 0);
 
   await resume(threadClient);
 }, { doNotRunWorker: true }));
--- a/devtools/server/tests/unit/test_setBreakpoint-on-line-with-multiple-statements.js
+++ b/devtools/server/tests/unit/test_setBreakpoint-on-line-with-multiple-statements.js
@@ -18,17 +18,17 @@ add_task(threadClientTest(async ({ threa
   }, client);
   Assert.equal(packet.type, "paused");
   const why = packet.why;
   Assert.equal(why.type, "breakpoint");
   Assert.equal(why.actors.length, 1);
   Assert.equal(why.actors[0], breakpointClient.actor);
   const frame = packet.frame;
   const where = frame.where;
-  Assert.equal(where.source.actor, source.actor);
+  Assert.equal(where.actor, source.actor);
   Assert.equal(where.line, location.line);
   const variables = frame.environment.bindings.variables;
   Assert.equal(variables.a.value.type, "undefined");
   Assert.equal(variables.b.value.type, "undefined");
   Assert.equal(variables.c.value.type, "undefined");
 
   await resume(threadClient);
 }, { doNotRunWorker: true }));
--- a/devtools/server/tests/unit/test_setBreakpoint-on-line-with-no-offsets-in-gcd-script.js
+++ b/devtools/server/tests/unit/test_setBreakpoint-on-line-with-no-offsets-in-gcd-script.js
@@ -22,16 +22,16 @@ add_task(threadClientTest(async ({ threa
   }, client);
   Assert.equal(packet.type, "paused");
   const why = packet.why;
   Assert.equal(why.type, "breakpoint");
   Assert.equal(why.actors.length, 1);
   Assert.equal(why.actors[0], breakpointClient.actor);
   const frame = packet.frame;
   const where = frame.where;
-  Assert.equal(where.source.actor, source.actor);
+  Assert.equal(where.actor, source.actor);
   Assert.equal(where.line, 8);
   const variables = frame.environment.bindings.variables;
   Assert.equal(variables.a.value, 1);
   Assert.equal(variables.c.value.type, "undefined");
 
   await resume(threadClient);
 }, { doNotRunWorker: true }));
--- a/devtools/server/tests/unit/test_setBreakpoint-on-line-with-no-offsets.js
+++ b/devtools/server/tests/unit/test_setBreakpoint-on-line-with-no-offsets.js
@@ -20,16 +20,16 @@ add_task(threadClientTest(async ({ threa
   }, client);
   Assert.equal(packet.type, "paused");
   const why = packet.why;
   Assert.equal(why.type, "breakpoint");
   Assert.equal(why.actors.length, 1);
   Assert.equal(why.actors[0], breakpointClient.actor);
   const frame = packet.frame;
   const where = frame.where;
-  Assert.equal(where.source.actor, source.actor);
+  Assert.equal(where.actor, source.actor);
   Assert.equal(where.line, actualLocation.line);
   const variables = frame.environment.bindings.variables;
   Assert.equal(variables.a.value, 1);
   Assert.equal(variables.c.value.type, "undefined");
 
   await resume(threadClient);
 }, { doNotRunWorker: true }));
--- a/devtools/server/tests/unit/test_setBreakpoint-on-line.js
+++ b/devtools/server/tests/unit/test_setBreakpoint-on-line.js
@@ -18,17 +18,17 @@ add_task(threadClientTest(async ({ threa
   }, client);
   Assert.equal(packet.type, "paused");
   const why = packet.why;
   Assert.equal(why.type, "breakpoint");
   Assert.equal(why.actors.length, 1);
   Assert.equal(why.actors[0], breakpointClient.actor);
   const frame = packet.frame;
   const where = frame.where;
-  Assert.equal(where.source.actor, source.actor);
+  Assert.equal(where.actor, source.actor);
   Assert.equal(where.line, location.line);
   const variables = frame.environment.bindings.variables;
   Assert.equal(variables.a.value, 1);
   Assert.equal(variables.b.value.type, "undefined");
   Assert.equal(variables.c.value.type, "undefined");
 
   await resume(threadClient);
 }, { doNotRunWorker: true }));
--- a/devtools/server/tests/unit/test_stepping-08.js
+++ b/devtools/server/tests/unit/test_stepping-08.js
@@ -9,17 +9,20 @@
 
 add_task(threadClientTest(async ({ threadClient, debuggee, client }) => {
   dumpn("Evaluating test code and waiting for first debugger statement");
   const dbgStmt = await executeOnNextTickAndWaitForPause(
     () => evaluateTestCode(debuggee), client);
   equal(dbgStmt.frame.where.line, 3, "Should be at debugger statement on line 3");
 
   dumpn("Setting breakpoint in innerFunction");
-  const source = threadClient.source(dbgStmt.frame.where.source);
+  const source = await getSourceById(
+    threadClient,
+    dbgStmt.frame.where.actor
+  );
   await source.setBreakpoint({ line: 7 });
 
   dumpn("Step in to innerFunction");
   const step1 = await stepIn(client, threadClient);
   equal(step1.frame.where.line, 7);
 
   dumpn("Step out of innerFunction");
   const step2 = await stepOut(client, threadClient);
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
@@ -314,16 +314,18 @@ using mozilla::TimeStamp;
   }                                                     \
   return GetCurrentInnerWindowInternal()->method args;  \
   PR_END_MACRO
 
 static LazyLogModule gDOMLeakPRLogOuter("DOMLeakOuter");
 
 static int32_t gOpenPopupSpamCount = 0;
 
+static bool gSyncContentBlockingNotifications = false;
+
 nsGlobalWindowOuter::OuterWindowByIdTable*
     nsGlobalWindowOuter::sOuterWindowsById = nullptr;
 
 /* static */
 nsPIDOMWindowOuter* nsPIDOMWindowOuter::GetFromCurrentInner(
     nsPIDOMWindowInner* aInner) {
   if (!aInner) {
     return nullptr;
@@ -5400,110 +5402,140 @@ void nsGlobalWindowOuter::NotifyContentB
 
   nsCOMPtr<nsIDocShell> docShell = GetDocShell();
   if (!docShell) {
     return;
   }
   nsCOMPtr<Document> doc = docShell->GetDocument();
   NS_ENSURE_TRUE_VOID(doc);
 
-  // This event might come after the user has navigated to another page.
-  // To prevent showing the TrackingProtection UI on the wrong page, we need to
-  // check that the loading URI for the channel is the same as the URI currently
-  // loaded in the document.
-  if (!SameLoadingURI(doc, aChannel) &&
-      aEvent == nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT) {
-    return;
-  }
-
-  // Notify nsIWebProgressListeners of this content blocking event.
-  // Can be used to change the UI state.
-  nsresult rv = NS_OK;
-  nsCOMPtr<nsISecurityEventSink> eventSink = do_QueryInterface(docShell, &rv);
-  NS_ENSURE_SUCCESS_VOID(rv);
-  uint32_t event = 0;
-  nsCOMPtr<nsISecureBrowserUI> securityUI;
-  docShell->GetSecurityUI(getter_AddRefs(securityUI));
-  if (!securityUI) {
+  nsCOMPtr<nsIURI> uri(aURIHint);
+  nsCOMPtr<nsIChannel> channel(aChannel);
+
+  static bool prefInitialized = false;
+  if (!prefInitialized) {
+    Preferences::AddBoolVarCache(
+        &gSyncContentBlockingNotifications,
+        "dom.testing.sync-content-blocking-notifications", false);
+    prefInitialized = true;
+  }
+
+  nsCOMPtr<nsIRunnable> func = NS_NewRunnableFunction(
+      "NotifyContentBlockingEventDelayed",
+      [doc, docShell, uri, channel, aEvent, aBlocked]() {
+        // This event might come after the user has navigated to another
+        // page. To prevent showing the TrackingProtection UI on the wrong
+        // page, we need to check that the loading URI for the channel is
+        // the same as the URI currently loaded in the document.
+        if (!SameLoadingURI(doc, channel) &&
+            aEvent == nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT) {
+          return;
+        }
+
+        // Notify nsIWebProgressListeners of this content blocking event.
+        // Can be used to change the UI state.
+        nsresult rv = NS_OK;
+        nsCOMPtr<nsISecurityEventSink> eventSink =
+            do_QueryInterface(docShell, &rv);
+        NS_ENSURE_SUCCESS_VOID(rv);
+        uint32_t event = 0;
+        nsCOMPtr<nsISecureBrowserUI> securityUI;
+        docShell->GetSecurityUI(getter_AddRefs(securityUI));
+        if (!securityUI) {
+          return;
+        }
+        securityUI->GetContentBlockingEvent(&event);
+        nsAutoCString origin;
+        nsContentUtils::GetASCIIOrigin(uri, origin);
+
+        bool blockedValue = aBlocked;
+        bool unblocked = false;
+        if (aEvent == nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT) {
+          doc->SetHasTrackingContentBlocked(aBlocked, origin);
+          if (!aBlocked) {
+            unblocked = !doc->GetHasTrackingContentBlocked();
+          }
+        } else if (aEvent ==
+                   nsIWebProgressListener::STATE_LOADED_TRACKING_CONTENT) {
+          doc->SetHasTrackingContentLoaded(aBlocked, origin);
+          if (!aBlocked) {
+            unblocked = !doc->GetHasTrackingContentLoaded();
+          }
+        } else if (aEvent == nsIWebProgressListener::
+                                 STATE_COOKIES_BLOCKED_BY_PERMISSION) {
+          doc->SetHasCookiesBlockedByPermission(aBlocked, origin);
+          if (!aBlocked) {
+            unblocked = !doc->GetHasCookiesBlockedByPermission();
+          }
+        } else if (aEvent ==
+                   nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER) {
+          doc->SetHasTrackingCookiesBlocked(aBlocked, origin);
+          if (!aBlocked) {
+            unblocked = !doc->GetHasTrackingCookiesBlocked();
+          }
+        } else if (aEvent ==
+                   nsIWebProgressListener::STATE_COOKIES_BLOCKED_ALL) {
+          doc->SetHasAllCookiesBlocked(aBlocked, origin);
+          if (!aBlocked) {
+            unblocked = !doc->GetHasAllCookiesBlocked();
+          }
+        } else if (aEvent ==
+                   nsIWebProgressListener::STATE_COOKIES_BLOCKED_FOREIGN) {
+          doc->SetHasForeignCookiesBlocked(aBlocked, origin);
+          if (!aBlocked) {
+            unblocked = !doc->GetHasForeignCookiesBlocked();
+          }
+        } else if (aEvent == nsIWebProgressListener::STATE_COOKIES_LOADED) {
+          MOZ_ASSERT(!aBlocked,
+                     "We don't expected to see blocked STATE_COOKIES_LOADED");
+          // Note that the logic in this branch is the logical negation of
+          // the logic in other branches, since the Document API we have is
+          // phrased in "loaded" terms as opposed to "blocked" terms.
+          blockedValue = !aBlocked;
+          doc->SetHasCookiesLoaded(blockedValue, origin);
+          if (!aBlocked) {
+            unblocked = !doc->GetHasCookiesLoaded();
+          }
+        } else {
+          // Ignore nsIWebProgressListener::STATE_BLOCKED_UNSAFE_CONTENT;
+        }
+        const uint32_t oldEvent = event;
+        if (blockedValue) {
+          event |= aEvent;
+        } else if (unblocked) {
+          event &= ~aEvent;
+        }
+
+        if (event == oldEvent
+#ifdef ANDROID
+            // GeckoView always needs to notify about blocked trackers,
+            // since the GeckoView API always needs to report the URI and
+            // type of any blocked tracker. We use a platform-dependent code
+            // path here because reporting this notification on desktop
+            // platforms isn't necessary and doing so can have a big
+            // performance cost.
+            && aEvent != nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT
+#endif
+        ) {
+          // Avoid dispatching repeated notifications when nothing has
+          // changed
+          return;
+        }
+
+        eventSink->OnContentBlockingEvent(channel, event);
+      });
+  nsresult rv;
+  if (gSyncContentBlockingNotifications) {
+    rv = func->Run();
+  } else {
+    rv = NS_IdleDispatchToCurrentThread(func.forget(), 100);
+  }
+  if (NS_WARN_IF(NS_FAILED(rv))) {
     return;
   }
-  securityUI->GetContentBlockingEvent(&event);
-  nsAutoCString origin;
-  nsContentUtils::GetASCIIOrigin(aURIHint, origin);
-
-  bool blockedValue = aBlocked;
-  bool unblocked = false;
-  if (aEvent == nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT) {
-    doc->SetHasTrackingContentBlocked(aBlocked, origin);
-    if (!aBlocked) {
-      unblocked = !doc->GetHasTrackingContentBlocked();
-    }
-  } else if (aEvent == nsIWebProgressListener::STATE_LOADED_TRACKING_CONTENT) {
-    doc->SetHasTrackingContentLoaded(aBlocked, origin);
-    if (!aBlocked) {
-      unblocked = !doc->GetHasTrackingContentLoaded();
-    }
-  } else if (aEvent ==
-             nsIWebProgressListener::STATE_COOKIES_BLOCKED_BY_PERMISSION) {
-    doc->SetHasCookiesBlockedByPermission(aBlocked, origin);
-    if (!aBlocked) {
-      unblocked = !doc->GetHasCookiesBlockedByPermission();
-    }
-  } else if (aEvent == nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER) {
-    doc->SetHasTrackingCookiesBlocked(aBlocked, origin);
-    if (!aBlocked) {
-      unblocked = !doc->GetHasTrackingCookiesBlocked();
-    }
-  } else if (aEvent == nsIWebProgressListener::STATE_COOKIES_BLOCKED_ALL) {
-    doc->SetHasAllCookiesBlocked(aBlocked, origin);
-    if (!aBlocked) {
-      unblocked = !doc->GetHasAllCookiesBlocked();
-    }
-  } else if (aEvent == nsIWebProgressListener::STATE_COOKIES_BLOCKED_FOREIGN) {
-    doc->SetHasForeignCookiesBlocked(aBlocked, origin);
-    if (!aBlocked) {
-      unblocked = !doc->GetHasForeignCookiesBlocked();
-    }
-  } else if (aEvent == nsIWebProgressListener::STATE_COOKIES_LOADED) {
-    MOZ_ASSERT(!aBlocked,
-               "We don't expected to see blocked STATE_COOKIES_LOADED");
-    // Note that the logic in this branch is the logical negation of the logic
-    // in other branches, since the Document API we have is phrased in
-    // "loaded" terms as opposed to "blocked" terms.
-    blockedValue = !aBlocked;
-    doc->SetHasCookiesLoaded(blockedValue, origin);
-    if (!aBlocked) {
-      unblocked = !doc->GetHasCookiesLoaded();
-    }
-  } else {
-    // Ignore nsIWebProgressListener::STATE_BLOCKED_UNSAFE_CONTENT;
-  }
-  const uint32_t oldEvent = event;
-  if (blockedValue) {
-    event |= aEvent;
-  } else if (unblocked) {
-    event &= ~aEvent;
-  }
-
-  if (event == oldEvent
-#ifdef ANDROID
-      // GeckoView always needs to notify about blocked trackers, since the
-      // GeckoView API always needs to report the URI and type of any blocked
-      // tracker.
-      // We use a platform-dependent code path here because reporting this
-      // notification on desktop platforms isn't necessary and doing so can have
-      // a big performance cost.
-      && aEvent != nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT
-#endif
-  ) {
-    // Avoid dispatching repeated notifications when nothing has changed
-    return;
-  }
-
-  eventSink->OnContentBlockingEvent(aChannel, event);
 }
 
 // static
 bool nsGlobalWindowOuter::SameLoadingURI(Document* aDoc, nsIChannel* aChannel) {
   nsCOMPtr<nsIURI> docURI = aDoc->GetDocumentURI();
   nsCOMPtr<nsILoadInfo> channelLoadInfo = aChannel->GetLoadInfo();
   if (!channelLoadInfo || !docURI) {
     return false;
--- a/js/src/wasm/AsmJS.cpp
+++ b/js/src/wasm/AsmJS.cpp
@@ -18,16 +18,17 @@
 
 #include "wasm/AsmJS.h"
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Compression.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/ScopeExit.h"
+#include "mozilla/Sprintf.h"  // SprintfLiteral
 #include "mozilla/Unused.h"
 #include "mozilla/Utf8.h"  // mozilla::Utf8Unit
 #include "mozilla/Variant.h"
 
 #include <new>
 
 #include "jsmath.h"
 #include "jsutil.h"
--- a/netwerk/base/nsIURIMutator.idl
+++ b/netwerk/base/nsIURIMutator.idl
@@ -329,17 +329,17 @@ NS_MutatorMethod(Method aMethod, Args ..
 {
   // Capture arguments by value, otherwise we crash.
   return [=](nsIURIMutator* aMutator) {
     typedef typename nsMethodTypeTraits<Method>::class_type Interface;
     nsresult rv;
     nsCOMPtr<Interface> target = do_QueryInterface(aMutator, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
     rv = (target->*aMethod)(aArgs...);
-    NS_ENSURE_SUCCESS(rv, rv);
+    if (NS_FAILED(rv)) return rv;
     return NS_OK;
   };
 }
 
 // This class provides a useful helper that allows chaining of setter operations
 class MOZ_STACK_CLASS NS_MutateURI
 {
 public:
@@ -488,17 +488,16 @@ public:
    * types, or the number of arguments, then there will be a compile error.
    */
   NS_MutateURI& Apply(const std::function<nsresult(nsIURIMutator*)>& aFunction)
   {
     if (NS_FAILED(mStatus)) {
       return *this;
     }
     mStatus = aFunction(mMutator);
-    NS_ENSURE_SUCCESS(mStatus, *this);
     return *this;
   }
 
   template <class C>
   MOZ_MUST_USE nsresult Finalize(nsCOMPtr<C>& aURI)
   {
     NS_ENSURE_SUCCESS(mStatus, mStatus);
 
@@ -538,17 +537,17 @@ public:
 
     result.forget(aURI);
     mStatus = NS_ERROR_NOT_AVAILABLE; // Second call to Finalize should fail.
     return NS_OK;
   }
 
   MOZ_MUST_USE nsresult Finalize(nsIURI** aURI)
   {
-    NS_ENSURE_SUCCESS(mStatus, mStatus);
+    if (NS_FAILED(mStatus)) return mStatus;
     mStatus = mMutator->Finalize(aURI);
     NS_ENSURE_SUCCESS(mStatus, mStatus);
 
     mStatus = NS_ERROR_NOT_AVAILABLE; // Second call to Finalize should fail.
     return NS_OK;
   }
 
   nsresult GetStatus() { return mStatus; }
new file mode 100644
--- /dev/null
+++ b/security/nss/.arcconfig
@@ -0,0 +1,5 @@
+{
+  "phabricator.uri" : "https://phabricator.services.mozilla.com/",
+  "repository.callsign": "NSS",
+  "history.immutable": false
+}
--- a/security/nss/TAG-INFO
+++ b/security/nss/TAG-INFO
@@ -1,1 +1,1 @@
-NSS_3_42_BETA1
+NSS_3_42_BETA2
--- a/security/nss/build.sh
+++ b/security/nss/build.sh
@@ -45,16 +45,17 @@ clean=0
 rebuild_gyp=0
 rebuild_nspr=0
 target=Debug
 verbose=0
 fuzz=0
 fuzz_tls=0
 fuzz_oss=0
 no_local_nspr=0
+sslkeylogfile=1
 
 gyp_params=(--depth="$cwd" --generator-output=".")
 ninja_params=()
 
 # Assume that the target architecture is the same as the host by default.
 host_arch=$(python "$cwd"/coreconf/detect_host_arch.py)
 target_arch=$host_arch
 
@@ -98,29 +99,32 @@ while [ $# -gt 0 ]; do
         --ct-verif) gyp_params+=(-Dct_verif=1) ;;
         --nspr) nspr_clean; rebuild_nspr=1 ;;
         --with-nspr=?*) set_nspr_path "${1#*=}"; no_local_nspr=1 ;;
         --system-nspr) set_nspr_path "/usr/include/nspr/:"; no_local_nspr=1 ;;
         --system-sqlite) gyp_params+=(-Duse_system_sqlite=1) ;;
         --enable-fips) gyp_params+=(-Ddisable_fips=0) ;;
         --enable-libpkix) gyp_params+=(-Ddisable_libpkix=0) ;;
         --mozpkix-only) gyp_params+=(-Dmozpkix_only=1 -Ddisable_tests=1 -Dsign_libs=0) ;;
+        --disable-keylog) sslkeylogfile=0 ;;
         *) show_help; exit 2 ;;
     esac
     shift
 done
 
 # Set the target architecture and build type.
 gyp_params+=(-Dtarget_arch="$target_arch")
 if [ "$opt_build" = 1 ]; then
     target=Release
 else
     target=Debug
 fi
 
+gyp_params+=(-Denable_sslkeylogfile="$sslkeylogfile")
+
 # Do special setup.
 if [ "$fuzz" = 1 ]; then
     source "$cwd"/coreconf/fuzz.sh
 fi
 nspr_set_flags $sanitizer_flags
 if [ ! -z "$sanitizer_flags" ]; then
     gyp_params+=(-Dsanitizer_flags="$sanitizer_flags")
 fi
--- a/security/nss/coreconf/coreconf.dep
+++ b/security/nss/coreconf/coreconf.dep
@@ -5,8 +5,9 @@
 
 /*
  * A dummy header file that is a dependency for all the object files.
  * Used to force a full recompilation of NSS in Mozilla's Tinderbox
  * depend builds.  See comments in rules.mk.
  */
 
 #error "Do not include this header file."
+
--- a/security/nss/gtests/ssl_gtest/Makefile
+++ b/security/nss/gtests/ssl_gtest/Makefile
@@ -31,16 +31,22 @@ CFLAGS += -I$(CORE_DEPTH)/lib/ssl
 
 ifdef NSS_DISABLE_TLS_1_3
 NSS_DISABLE_TLS_1_3=1
 # Run parameterized tests only, for which we can easily exclude TLS 1.3
 CPPSRCS := $(filter-out $(shell grep -l '^TEST_F' $(CPPSRCS)), $(CPPSRCS))
 CFLAGS += -DNSS_DISABLE_TLS_1_3
 endif
 
+ifdef NSS_ALLOW_SSLKEYLOGFILE
+SSLKEYLOGFILE_FILES = ssl_keylog_unittest.cc
+else
+SSLKEYLOGFILE_FILES = $(NULL)
+endif
+
 #######################################################################
 # (5) Execute "global" rules. (OPTIONAL)                              #
 #######################################################################
 
 include $(CORE_DEPTH)/coreconf/rules.mk
 
 #######################################################################
 # (6) Execute "component" rules. (OPTIONAL)                           #
--- a/security/nss/gtests/ssl_gtest/manifest.mn
+++ b/security/nss/gtests/ssl_gtest/manifest.mn
@@ -15,28 +15,28 @@ CPPSRCS = \
       bloomfilter_unittest.cc \
       ssl_0rtt_unittest.cc \
       ssl_agent_unittest.cc \
       ssl_auth_unittest.cc \
       ssl_cert_ext_unittest.cc \
       ssl_ciphersuite_unittest.cc \
       ssl_custext_unittest.cc \
       ssl_damage_unittest.cc \
+      ssl_debug_env_unittest.cc \
       ssl_dhe_unittest.cc \
       ssl_drop_unittest.cc \
       ssl_ecdh_unittest.cc \
       ssl_ems_unittest.cc \
       ssl_exporter_unittest.cc \
       ssl_extension_unittest.cc \
       ssl_fragment_unittest.cc \
       ssl_fuzz_unittest.cc \
       ssl_gather_unittest.cc \
       ssl_gtest.cc \
       ssl_hrr_unittest.cc \
-      ssl_keylog_unittest.cc \
       ssl_keyupdate_unittest.cc \
       ssl_loopback_unittest.cc \
       ssl_misc_unittest.cc \
       ssl_record_unittest.cc \
       ssl_recordsize_unittest.cc \
       ssl_resumption_unittest.cc \
       ssl_renegotiation_unittest.cc \
       ssl_skip_unittest.cc \
@@ -48,16 +48,17 @@ CPPSRCS = \
       selfencrypt_unittest.cc \
       test_io.cc \
       tls_agent.cc \
       tls_connect.cc \
       tls_hkdf_unittest.cc \
       tls_filter.cc \
       tls_protect.cc \
       tls_esni_unittest.cc \
+      $(SSLKEYLOGFILE_FILES) \
       $(NULL)
 
 INCLUDES += -I$(CORE_DEPTH)/gtests/google_test/gtest/include \
             -I$(CORE_DEPTH)/gtests/common \
             -I$(CORE_DEPTH)/cpputil
 
 REQUIRES = nspr nss libdbm gtest cpputil
 
new file mode 100644
--- /dev/null
+++ b/security/nss/gtests/ssl_gtest/ssl_debug_env_unittest.cc
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#include <cstdlib>
+#include <fstream>
+#include <sstream>
+
+#include "gtest_utils.h"
+#include "tls_connect.h"
+
+namespace nss_test {
+
+extern "C" {
+extern FILE* ssl_trace_iob;
+
+#ifdef NSS_ALLOW_SSLKEYLOGFILE
+extern FILE* ssl_keylog_iob;
+#endif
+}
+
+// These tests ensure that when the associated environment variables are unset
+// that the lazily-initialized defaults are what they are supposed to be.
+
+#ifdef DEBUG
+TEST_P(TlsConnectGeneric, DebugEnvTraceFileNotSet) {
+  char* ev = PR_GetEnvSecure("SSLDEBUGFILE");
+  if (ev && ev[0]) {
+    // note: should use GTEST_SKIP when GTest gets updated to support it
+    return;
+  }
+
+  Connect();
+  EXPECT_EQ(stderr, ssl_trace_iob);
+}
+#endif
+
+#ifdef NSS_ALLOW_SSLKEYLOGFILE
+TEST_P(TlsConnectGeneric, DebugEnvKeylogFileNotSet) {
+  char* ev = PR_GetEnvSecure("SSLKEYLOGFILE");
+  if (ev && ev[0]) {
+    // note: should use GTEST_SKIP when GTest gets updated to support it
+    return;
+  }
+
+  Connect();
+  EXPECT_EQ(nullptr, ssl_keylog_iob);
+}
+#endif
+
+}  // namespace nss_test
--- a/security/nss/gtests/ssl_gtest/ssl_gtest.gyp
+++ b/security/nss/gtests/ssl_gtest/ssl_gtest.gyp
@@ -16,28 +16,28 @@
         'selfencrypt_unittest.cc',
         'ssl_0rtt_unittest.cc',
         'ssl_agent_unittest.cc',
         'ssl_auth_unittest.cc',
         'ssl_cert_ext_unittest.cc',
         'ssl_ciphersuite_unittest.cc',
         'ssl_custext_unittest.cc',
         'ssl_damage_unittest.cc',
+        'ssl_debug_env_unittest.cc',
         'ssl_dhe_unittest.cc',
         'ssl_drop_unittest.cc',
         'ssl_ecdh_unittest.cc',
         'ssl_ems_unittest.cc',
         'ssl_exporter_unittest.cc',
         'ssl_extension_unittest.cc',
         'ssl_fuzz_unittest.cc',
         'ssl_fragment_unittest.cc',
         'ssl_gather_unittest.cc',
         'ssl_gtest.cc',
         'ssl_hrr_unittest.cc',
-        'ssl_keylog_unittest.cc',
         'ssl_keyupdate_unittest.cc',
         'ssl_loopback_unittest.cc',
         'ssl_misc_unittest.cc',
         'ssl_record_unittest.cc',
         'ssl_recordsize_unittest.cc',
         'ssl_resumption_unittest.cc',
         'ssl_renegotiation_unittest.cc',
         'ssl_skip_unittest.cc',
@@ -86,23 +86,31 @@
             '<(DEPTH)/lib/freebl/freebl.gyp:freebl',
           ],
         }],
         [ 'disable_dbm==0', {
           'dependencies': [
             '<(DEPTH)/lib/dbm/src/src.gyp:dbm',
           ],
         }],
+        [ 'enable_sslkeylogfile==1', {
+          'sources': [
+            'ssl_keylog_unittest.cc',
+          ],
+          'defines': [
+            'NSS_ALLOW_SSLKEYLOGFILE',
+          ],
+        }],
       ],
     }
   ],
   'target_defaults': {
     'include_dirs': [
       '../../lib/ssl'
     ],
     'defines': [
-      'NSS_USE_STATIC_LIBS'
+      'NSS_USE_STATIC_LIBS',
     ],
   },
   'variables': {
     'module': 'nss',
   }
 }
--- a/security/nss/gtests/ssl_gtest/ssl_keylog_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_keylog_unittest.cc
@@ -1,39 +1,76 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* 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/. */
 
-#ifdef NSS_ALLOW_SSLKEYLOGFILE
-
 #include <cstdlib>
 #include <fstream>
 #include <sstream>
 
 #include "gtest_utils.h"
 #include "tls_connect.h"
 
 namespace nss_test {
 
-static const std::string keylog_file_path = "keylog.txt";
-static const std::string keylog_env = "SSLKEYLOGFILE=" + keylog_file_path;
+static const std::string kKeylogFilePath = "keylog.txt";
+static const std::string kKeylogBlankEnv = "SSLKEYLOGFILE=";
+static const std::string kKeylogSetEnv = kKeylogBlankEnv + kKeylogFilePath;
+
+extern "C" {
+extern FILE* ssl_keylog_iob;
+}
 
-class KeyLogFileTest : public TlsConnectGeneric {
+class KeyLogFileTestBase : public TlsConnectGeneric {
+ private:
+  std::string env_to_set_;
+
  public:
+  virtual void CheckKeyLog() = 0;
+
+  KeyLogFileTestBase(std::string env) : env_to_set_(env) {}
+
   void SetUp() override {
     TlsConnectGeneric::SetUp();
     // Remove previous results (if any).
-    (void)remove(keylog_file_path.c_str());
-    PR_SetEnv(keylog_env.c_str());
+    (void)remove(kKeylogFilePath.c_str());
+    PR_SetEnv(env_to_set_.c_str());
   }
 
-  void CheckKeyLog() {
-    std::ifstream f(keylog_file_path);
+  void ConnectAndCheck() {
+    // This is a child process, ensure that error messages immediately
+    // propagate or else it will not be visible.
+    ::testing::GTEST_FLAG(throw_on_failure) = true;
+
+    if (version_ == SSL_LIBRARY_VERSION_TLS_1_3) {
+      SetupForZeroRtt();
+      client_->Set0RttEnabled(true);
+      server_->Set0RttEnabled(true);
+      ExpectResumption(RESUME_TICKET);
+      ZeroRttSendReceive(true, true);
+      Handshake();
+      ExpectEarlyDataAccepted(true);
+      CheckConnected();
+      SendReceive();
+    } else {
+      Connect();
+    }
+    CheckKeyLog();
+    _exit(0);
+  }
+};
+
+class KeyLogFileTest : public KeyLogFileTestBase {
+ public:
+  KeyLogFileTest() : KeyLogFileTestBase(kKeylogSetEnv) {}
+
+  void CheckKeyLog() override {
+    std::ifstream f(kKeylogFilePath);
     std::map<std::string, size_t> labels;
     std::set<std::string> client_randoms;
     for (std::string line; std::getline(f, line);) {
       if (line[0] == '#') {
         continue;
       }
 
       std::istringstream iss(line);
@@ -60,38 +97,16 @@ class KeyLogFileTest : public TlsConnect
       ASSERT_EQ(2U, labels["EARLY_EXPORTER_SECRET"]);
       ASSERT_EQ(4U, labels["CLIENT_HANDSHAKE_TRAFFIC_SECRET"]);
       ASSERT_EQ(4U, labels["SERVER_HANDSHAKE_TRAFFIC_SECRET"]);
       ASSERT_EQ(4U, labels["CLIENT_TRAFFIC_SECRET_0"]);
       ASSERT_EQ(4U, labels["SERVER_TRAFFIC_SECRET_0"]);
       ASSERT_EQ(4U, labels["EXPORTER_SECRET"]);
     }
   }
-
-  void ConnectAndCheck() {
-    // This is a child process, ensure that error messages immediately
-    // propagate or else it will not be visible.
-    ::testing::GTEST_FLAG(throw_on_failure) = true;
-
-    if (version_ == SSL_LIBRARY_VERSION_TLS_1_3) {
-      SetupForZeroRtt();
-      client_->Set0RttEnabled(true);
-      server_->Set0RttEnabled(true);
-      ExpectResumption(RESUME_TICKET);
-      ZeroRttSendReceive(true, true);
-      Handshake();
-      ExpectEarlyDataAccepted(true);
-      CheckConnected();
-      SendReceive();
-    } else {
-      Connect();
-    }
-    CheckKeyLog();
-    _exit(0);
-  }
 };
 
 // Tests are run in a separate process to ensure that NSS is not initialized yet
 // and can process the SSLKEYLOGFILE environment variable.
 
 TEST_P(KeyLogFileTest, KeyLogFile) {
   testing::GTEST_FLAG(death_test_style) = "threadsafe";
 
@@ -108,11 +123,42 @@ INSTANTIATE_TEST_CASE_P(
                        TlsConnectTestBase::kTlsV10ToV12));
 #ifndef NSS_DISABLE_TLS_1_3
 INSTANTIATE_TEST_CASE_P(
     KeyLogFileTLS13, KeyLogFileTest,
     ::testing::Combine(TlsConnectTestBase::kTlsVariantsStream,
                        TlsConnectTestBase::kTlsV13));
 #endif
 
-}  // namespace nss_test
+class KeyLogFileUnsetTest : public KeyLogFileTestBase {
+ public:
+  KeyLogFileUnsetTest() : KeyLogFileTestBase(kKeylogBlankEnv) {}
+
+  void CheckKeyLog() override {
+    std::ifstream f(kKeylogFilePath);
+    EXPECT_FALSE(f.good());
+
+    EXPECT_EQ(nullptr, ssl_keylog_iob);
+  }
+};
+
+TEST_P(KeyLogFileUnsetTest, KeyLogFile) {
+  testing::GTEST_FLAG(death_test_style) = "threadsafe";
 
-#endif  // NSS_ALLOW_SSLKEYLOGFILE
+  ASSERT_EXIT(ConnectAndCheck(), ::testing::ExitedWithCode(0), "");
+}
+
+INSTANTIATE_TEST_CASE_P(
+    KeyLogFileDTLS12, KeyLogFileUnsetTest,
+    ::testing::Combine(TlsConnectTestBase::kTlsVariantsDatagram,
+                       TlsConnectTestBase::kTlsV11V12));
+INSTANTIATE_TEST_CASE_P(
+    KeyLogFileTLS12, KeyLogFileUnsetTest,
+    ::testing::Combine(TlsConnectTestBase::kTlsVariantsStream,
+                       TlsConnectTestBase::kTlsV10ToV12));
+#ifndef NSS_DISABLE_TLS_1_3
+INSTANTIATE_TEST_CASE_P(
+    KeyLogFileTLS13, KeyLogFileUnsetTest,
+    ::testing::Combine(TlsConnectTestBase::kTlsVariantsStream,
+                       TlsConnectTestBase::kTlsV13));
+#endif
+
+}  // namespace nss_test
--- a/security/nss/help.txt
+++ b/security/nss/help.txt
@@ -46,8 +46,10 @@ NSS build tool options:
                      --with-nspr=<include>:<lib> sets include and lib paths
     --system-nspr    attempt to use system nspr
                      shorthand for --with-nspr=/usr/include/nspr:
     --system-sqlite  use system sqlite
     --enable-fips    enable FIPS checks
     --enable-libpkix make libpkix part of the build
     --mozpkix-only   build only static mozpkix and mozpkix-test libraries
                      support for this build option is limited
+    --disable-keylog enable support for logging key data to a file specified
+                     by the SSLKEYLOGFILE environment variable
--- a/security/nss/lib/ssl/ssl.gyp
+++ b/security/nss/lib/ssl/ssl.gyp
@@ -68,16 +68,21 @@
             'UNSAFE_FUZZER_MODE',
           ],
         }],
         [ 'OS=="dragonfly" or OS=="freebsd" or OS=="netbsd" or OS=="openbsd" or OS=="linux"', {
           'cflags': [
             '-std=gnu99',
           ],
         }],
+        [ 'enable_sslkeylogfile==1', {
+          'defines': [
+            'NSS_ALLOW_SSLKEYLOGFILE',
+          ],
+        }],
       ],
       'dependencies': [
         '<(DEPTH)/exports.gyp:nss_exports',
       ],
     },
     {
       'target_name': 'ssl3',
       'type': 'shared_library',
@@ -87,17 +92,12 @@
         '<(DEPTH)/lib/util/util.gyp:nssutil3',
         '<(DEPTH)/lib/freebl/freebl.gyp:freebl',
       ],
       'variables': {
         'mapfile': 'ssl.def'
       }
     }
   ],
-  'target_defaults': {
-    'defines': [
-      'NSS_ALLOW_SSLKEYLOGFILE=1'
-    ]
-  },
   'variables': {
     'module': 'nss'
   }
 }
--- a/security/nss/lib/ssl/sslsock.c
+++ b/security/nss/lib/ssl/sslsock.c
@@ -3639,16 +3639,17 @@ ssl_SetDefaultsFromEnvironment(void)
 {
 #if defined(NSS_HAVE_GETENV)
     static int firsttime = 1;
 
     if (firsttime) {
         char *ev;
         firsttime = 0;
 #ifdef DEBUG
+        ssl_trace_iob = NULL;
         ev = PR_GetEnvSecure("SSLDEBUGFILE");
         if (ev && ev[0]) {
             ssl_trace_iob = fopen(ev, "w");
         }
         if (!ssl_trace_iob) {
             ssl_trace_iob = stderr;
         }
 #ifdef TRACE
@@ -3660,16 +3661,17 @@ ssl_SetDefaultsFromEnvironment(void)
 #endif /* TRACE */
         ev = PR_GetEnvSecure("SSLDEBUG");
         if (ev && ev[0]) {
             ssl_debug = atoi(ev);
             SSL_TRACE(("SSL: debugging set to %d", ssl_debug));
         }
 #endif /* DEBUG */
 #ifdef NSS_ALLOW_SSLKEYLOGFILE
+        ssl_keylog_iob = NULL;
         ev = PR_GetEnvSecure("SSLKEYLOGFILE");
         if (ev && ev[0]) {
             ssl_keylog_iob = fopen(ev, "a");
             if (!ssl_keylog_iob) {
                 SSL_TRACE(("SSL: failed to open key log file"));
             } else {
                 if (ftell(ssl_keylog_iob) == 0) {
                     fputs("# SSL/TLS secrets log file, generated by NSS\n",
--- a/toolkit/components/antitracking/AntiTrackingCommon.cpp
+++ b/toolkit/components/antitracking/AntiTrackingCommon.cpp
@@ -325,22 +325,16 @@ void ReportBlockingToConsole(nsPIDOMWind
             nsCOMPtr<nsIURIFixup> urifixup = services::GetURIFixup();
             NS_ENSURE_TRUE_VOID(urifixup);
             nsCOMPtr<nsIURI> exposableURI;
             nsresult rv =
                 urifixup->CreateExposableURI(uri, getter_AddRefs(exposableURI));
             NS_ENSURE_SUCCESS_VOID(rv);
 
             NS_ConvertUTF8toUTF16 spec(exposableURI->GetSpecOrDefault());
-            bool overflows = spec.Length() > sMaxSpecLength;
-            spec.Truncate(std::min(spec.Length(), sMaxSpecLength));
-            if (overflows) {
-              NS_NAMED_LITERAL_STRING(kEllipsis, u"\x2026");
-              spec.Append(kEllipsis);
-            }
             const char16_t* params[] = {spec.get()};
 
             nsContentUtils::ReportToConsole(
                 nsIScriptError::warningFlag, category, doc,
                 nsContentUtils::eNECKO_PROPERTIES, message, params,
                 ArrayLength(params), nullptr, sourceLine, lineNumber,
                 columnNumber);
           }),
--- a/toolkit/components/url-classifier/tests/mochitest/test_threathit_report.html
+++ b/toolkit/components/url-classifier/tests/mochitest/test_threathit_report.html
@@ -205,17 +205,20 @@ function testOnWindow(aTestData) {
       await BrowserTestUtils.waitForContentEvent(browser, "DOMContentLoaded");
       checkResults(aTestData, expected);
       win.close();
       resolve();
     })();
   });
 }
 SpecialPowers.pushPrefEnv(
-  {"set": [["browser.safebrowsing.phishing.enabled", true]]},
+  {"set": [
+    ["browser.safebrowsing.phishing.enabled", true],
+    ["dom.testing.sync-content-blocking-notifications", true],
+  ]},
   test);
 
 function test() {
   (async function() {
     await classifierHelper.waitForInit();
 
     for (let testData of testDatas) {
       await setupTestData(testData);
--- a/tools/profiler/core/platform.cpp
+++ b/tools/profiler/core/platform.cpp
@@ -440,16 +440,17 @@ class ActivePS {
 #endif
   {
     // Deep copy aFilters.
     MOZ_ALWAYS_TRUE(mFilters.resize(aFilterCount));
     for (uint32_t i = 0; i < aFilterCount; ++i) {
       mFilters[i] = aFilters[i];
     }
 
+#if !defined(RELEASE_OR_BETA)
     if (mInterposeObserver) {
       // We need to register the observer on the main thread, because we want
       // to observe IO that happens on the main thread.
       // IOInterposer needs to be initialized before calling
       // IOInterposer::Register or our observer will be silently dropped.
       if (NS_IsMainThread()) {
         IOInterposer::Init();
         IOInterposer::Register(IOInterposeObserver::OpAll, mInterposeObserver);
@@ -457,33 +458,36 @@ class ActivePS {
         RefPtr<ProfilerIOInterposeObserver> observer = mInterposeObserver;
         NS_DispatchToMainThread(
             NS_NewRunnableFunction("ActivePS::ActivePS", [=]() {
               IOInterposer::Init();
               IOInterposer::Register(IOInterposeObserver::OpAll, observer);
             }));
       }
     }
+#endif
   }
 
   ~ActivePS() {
+#if !defined(RELEASE_OR_BETA)
     if (mInterposeObserver) {
       // We need to unregister the observer on the main thread, because that's
       // where we've registered it.
       if (NS_IsMainThread()) {
         IOInterposer::Unregister(IOInterposeObserver::OpAll,
                                  mInterposeObserver);
       } else {
         RefPtr<ProfilerIOInterposeObserver> observer = mInterposeObserver;
         NS_DispatchToMainThread(
             NS_NewRunnableFunction("ActivePS::~ActivePS", [=]() {
               IOInterposer::Unregister(IOInterposeObserver::OpAll, observer);
             }));
       }
     }
+#endif
   }
 
   bool ThreadSelected(const char* aThreadName) {
     MOZ_RELEASE_ASSERT(sInstance);
 
     if (mFilters.empty()) {
       return true;
     }