Merge mozilla-central to mozilla-beta. a=same-version-merge l10n=same-version-merge on a CLOSED TREE
authorNarcis Beleuzu <nbeleuzu@mozilla.com>
Fri, 15 Mar 2019 21:58:48 +0200
changeset 522017 c594aee5b7a43dda53aff9e014a28304a3fc7190
parent 521844 4085cc6c53dd704ee8206a6837c44231573a0fe2 (current diff)
parent 522016 4d62ab0e31fd6918ca95763914a8bdf41afe757a (diff)
child 522624 2f6d5b78fa2596a59c869bb6198b6870092b26da
push id10870
push usernbeleuzu@mozilla.com
push dateFri, 15 Mar 2019 20:00:07 +0000
treeherdermozilla-beta@c594aee5b7a4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssame-version-merge
milestone67.0
Merge mozilla-central to mozilla-beta. a=same-version-merge l10n=same-version-merge on a CLOSED TREE
build/build-clang/clang-tidy-win32.json
devtools/server/tests/unit/test_eval-01.js
devtools/server/tests/unit/test_eval-02.js
devtools/server/tests/unit/test_eval-03.js
devtools/server/tests/unit/test_eval-04.js
devtools/server/tests/unit/test_eval-05.js
dom/push/test/xpcshell/test_startup_error.js
taskcluster/scripts/misc/build-clang-tidy32-windows.sh
taskcluster/scripts/misc/build-clang-windows-helper32.sh
--- a/.eslintignore
+++ b/.eslintignore
@@ -22,18 +22,16 @@ extensions/spellcheck/**
 extensions/universalchardet/**
 image/**
 layout/**
 netwerk/cookie/test/browser/**
 netwerk/test/browser/**
 netwerk/test/mochitests/**
 netwerk/test/unit*/**
 tools/update-packaging/**
-uriloader/exthandler/**
-uriloader/exthandler/tests/mochitest/**
 xpfe/**
 
 # We currently have no js files in these directories, so we ignore them by
 # default to aid ESLint's performance.
 build/**
 config/**
 db/**
 embedding/**
--- a/.taskcluster.yml
+++ b/.taskcluster.yml
@@ -236,16 +236,17 @@ tasks:
                   cd /builds/worker/checkouts/gecko &&
                   ln -s /builds/worker/artifacts artifacts &&
                   ./mach --log-no-times taskgraph decision
                   --pushlog-id='${push.pushlog_id}'
                   --pushdate='${push.pushdate}'
                   --project='${repository.project}'
                   --owner='${ownerEmail}'
                   --level='${repository.level}'
+                  --tasks-for='${tasks_for}'
                   --base-repository="$GECKO_BASE_REPOSITORY"
                   --head-repository="$GECKO_HEAD_REPOSITORY"
                   --head-ref="$GECKO_HEAD_REF"
                   --head-rev="$GECKO_HEAD_REV"
                   ${extraArgs}
 
           artifacts:
             'public':
--- a/browser/actors/ClickHandlerChild.jsm
+++ b/browser/actors/ClickHandlerChild.jsm
@@ -51,39 +51,48 @@ class ClickHandlerChild extends ActorChi
     }
 
     // Bug 965637, query the CSP from the doc instead of the Principal
     let csp = ownerDoc.nodePrincipal.csp;
     if (csp) {
       csp = E10SUtils.serializeCSP(csp);
     }
 
+    let ReferrerInfo = Components.Constructor("@mozilla.org/referrer-info;1",
+                                              "nsIReferrerInfo",
+                                              "init");
+    let referrerInfo = new ReferrerInfo(
+      referrerPolicy,
+      !BrowserUtils.linkHasNoReferrer(node),
+      ownerDoc.documentURIObject);
+    referrerInfo = E10SUtils.serializeReferrerInfo(referrerInfo);
     let frameOuterWindowID = WebNavigationFrames.getFrameId(ownerDoc.defaultView);
 
     let json = { button: event.button, shiftKey: event.shiftKey,
                  ctrlKey: event.ctrlKey, metaKey: event.metaKey,
                  altKey: event.altKey, href: null, title: null,
-                 frameOuterWindowID, referrerPolicy,
+                 frameOuterWindowID,
                  triggeringPrincipal: principal,
                  csp,
+                 referrerInfo,
                  originAttributes: principal ? principal.originAttributes : {},
                  isContentWindowPrivate: PrivateBrowsingUtils.isContentWindowPrivate(ownerDoc.defaultView)};
 
     if (href) {
       try {
         BrowserUtils.urlSecurityCheck(href, principal);
       } catch (e) {
         return;
       }
 
       json.href = href;
       if (node) {
         json.title = node.getAttribute("title");
       }
-      json.noReferrer = BrowserUtils.linkHasNoReferrer(node);
+
 
       // Check if the link needs to be opened with mixed content allowed.
       // Only when the owner doc has |mixedContentChannel| and the same origin
       // should we allow mixed content.
       json.allowMixedContent = false;
       let docshell = ownerDoc.defaultView.docShell;
       if (this.mm.docShell.mixedContentChannel) {
         const sm = Services.scriptSecurityManager;
--- a/browser/actors/NetErrorChild.jsm
+++ b/browser/actors/NetErrorChild.jsm
@@ -179,16 +179,20 @@ class NetErrorChild extends ActorChild {
       }
     }
 
     technicalInfo.appendChild(doc.createTextNode(msg1));
 
     if (input.data.isDomainMismatch) {
       let subjectAltNames = input.data.certSubjectAltNames.split(",");
       let numSubjectAltNames = subjectAltNames.length;
+
+      subjectAltNames = subjectAltNames.filter(name => name.length > 0);
+      numSubjectAltNames = subjectAltNames.length;
+
       let msgPrefix = "";
       if (numSubjectAltNames != 0) {
         if (numSubjectAltNames == 1) {
           if (newErrorPagesEnabled) {
             technicalInfo.textContent = "";
             let brandName = gBrandBundle.GetStringFromName("brandShortName");
             msgPrefix = gPipNSSBundle.formatStringFromName("certErrorMismatchSinglePrefix3", [brandName, hostString], 2) + " ";
             msgPrefix += gPipNSSBundle.GetStringFromName("certErrorMismatchSinglePrefix");
--- a/browser/app/blocklist.xml
+++ b/browser/app/blocklist.xml
@@ -1,10 +1,10 @@
 <?xml version='1.0' encoding='UTF-8'?>
-<blocklist lastupdate="1551794995188" xmlns="http://www.mozilla.org/2006/addons-blocklist">
+<blocklist lastupdate="1552492951595" xmlns="http://www.mozilla.org/2006/addons-blocklist">
   <emItems>
     <emItem blockID="i334" id="{0F827075-B026-42F3-885D-98981EE7B1AE}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i1211" id="flvto@hotger.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
@@ -2551,16 +2551,24 @@
     <emItem blockID="7718be46-8e84-4bc7-a5a9-4c5de18378ee" id="/^((\{5084f455-bc8f-483c-b145-91245bcbfd64\})|(\{bd69d5d0-4b2f-48cb-bab5-dcf1e0f9c63b\}))$/">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="aca80fb4-760e-4cd4-9fec-649fb38b2947" id="/^((\{ee2d725e-9726-43ac-8040-60ce9ff2831b\})|(\{55417a80-e6f7-4d77-8d73-f59045e5e890\}))$/">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
+    <emItem blockID="2c739daa-ffee-48d9-a825-e53c8fd2bb3c" id="{c04d9d7d-1c8c-4eab-a51a-828c47e1b8b7}">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="3"/>
+    </emItem>
+    <emItem blockID="b57d9505-21bf-4a24-accb-05ceac50dadc" id="/^((\{86c18738-11ed-4c16-af92-786aa036c83c\})|(\{d0fee16a-f4eb-4dc1-9961-82b913e5943d\})|(\{1c4937a1-c678-4607-8665-a16384ee302e\})|(\{22caeb02-38a3-415d-b168-64fadccbb4a4\})|(\{1c9372e7-5f0e-4541-99cf-dfbf2ab00b01\})|(\{9fe66994-8ed1-4317-a20a-1d0544ca062f\})|(\{6df222d8-97c7-42bf-9683-1cf8119c1e9e\})|(\{4c2dda03-bad0-4160-a8a1-6d089200420e\})|(\{7aae7d4f-55b9-42eb-b683-932591265e17\})|(\{e6f8ab99-3c96-410c-95d1-267ad48ed3e2\})|(\{6d8c5068-d0cb-47a5-af5e-3f23064f4608\})|(\{90481f38-d06a-465e-a54c-206bbb1ee9ae\})|(\{4b75aeb8-f14a-4ef3-b1ad-09733b40dac3\})|(\{3a8ca495-f5ab-4320-b070-4f44266fe3d1\})|(\{84f8914f-0dec-48ed-a0fd-4a7712c06793\})|(\{aa613fce-603c-41df-bf49-9b09614cebe6\})|(\{30314350-199a-4951-9c05-c3537a946492\})|(\{a2edce1d-10ab-483d-8c01-5e5fe0c82902\})|(\{ec91a3d4-8311-4700-aa15-b3941f21a052\})|(\{e9049687-164a-4cf3-be1f-1291cfb0f44a\})|(\{2be73925-ebaf-43ca-8b26-bd820887f591\})|(\{840eadea-1c68-411f-b4e9-08d9f236385d\})|(\{0a89d040-5fb1-46d7-bf81-43b55e83695d\})|(\{6a1e76ed-4ac2-4a0c-8beb-43ae63558b36\})|(\{1b90c930-e7d7-486a-9085-8b57129489c7\})|(\{eab649ca-af76-4de9-95b0-8036e35a66cc\})|(\{0628e652-98f4-4e58-9ecb-ad996b061aef\})|(elfr@geckoaddon\.org)|(else@geckoaddon\.org)|(fr_b@iext\.pro)|(it_b@iext\.pro)|(sv_b@iext\.pro)|(no_b1@iext\.pro)|(fi_b@iext\.pro)|(au_b@iext\.pro)|(elfr12@geckoaddon\.org)|(test@informations\.to)|(se_pop@informations\.to)|(it@spongebog\.funny-ok\.com)|(it@tsunami\.funny-ok\.com)|(fi@spongebog\.funny-ok\.com)|(fi@tsunami\.funny-ok\.com)|(no@spongebog\.funny-ok\.com)|(no@tsunami\.funny-ok\.com)|(fr@tsunami\.funny-ok\.com)|(fr@spongebog\.funny-ok\.com)|(se@tsunami\.funny-ok\.com)|(se@spongebog\.funny-ok\.com)|(au@spongebog\.funny-ok\.com)|(au@tsunami\.funny-ok\.com)|(nz@spongebog\.funny-ok\.com)|(nz@tsunami\.funny-ok\.com)|(gr@spongebog\.funny-ok\.com)|(gr@tsunami\.funny-ok\.com)|(nz_fnew@tsunami\.funny-ok\.com))$/">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="3"/>
+    </emItem>
   </emItems>
   <pluginItems>
     <pluginItem blockID="p332">
       <match exp="libflashplayer\.so" name="filename"/>
       <match exp="^Shockwave Flash 11.(0|1) r[0-9]{1,3}$" name="description"/>
       <infoURL>https://get.adobe.com/flashplayer/</infoURL>
       <versionRange severity="0" vulnerabilitystatus="1">
         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1242,17 +1242,19 @@ pref("services.sync.prefs.sync.privacy.c
 pref("services.sync.prefs.sync.privacy.clearOnShutdown.sessions", true);
 pref("services.sync.prefs.sync.privacy.clearOnShutdown.siteSettings", true);
 pref("services.sync.prefs.sync.privacy.donottrackheader.enabled", true);
 pref("services.sync.prefs.sync.privacy.fuzzyfox.enabled", false);
 pref("services.sync.prefs.sync.privacy.fuzzyfox.clockgrainus", false);
 pref("services.sync.prefs.sync.privacy.sanitize.sanitizeOnShutdown", true);
 pref("services.sync.prefs.sync.privacy.trackingprotection.enabled", true);
 pref("services.sync.prefs.sync.privacy.trackingprotection.cryptomining.enabled", true);
+pref("services.sync.prefs.sync.privacy.trackingprotection.cryptomining.annotate.enabled", true);
 pref("services.sync.prefs.sync.privacy.trackingprotection.fingerprinting.enabled", true);
+pref("services.sync.prefs.sync.privacy.trackingprotection.fingerprinting.annotate.enabled", true);
 pref("services.sync.prefs.sync.privacy.trackingprotection.pbmode.enabled", true);
 pref("services.sync.prefs.sync.privacy.resistFingerprinting", true);
 pref("services.sync.prefs.sync.privacy.reduceTimerPrecision", true);
 pref("services.sync.prefs.sync.privacy.resistFingerprinting.reduceTimerPrecision.microseconds", true);
 pref("services.sync.prefs.sync.privacy.resistFingerprinting.reduceTimerPrecision.jitter", true);
 pref("services.sync.prefs.sync.security.OCSP.enabled", true);
 pref("services.sync.prefs.sync.security.OCSP.require", true);
 pref("services.sync.prefs.sync.security.default_personal_cert", true);
--- a/browser/app/winlauncher/ErrorHandler.cpp
+++ b/browser/app/winlauncher/ErrorHandler.cpp
@@ -171,27 +171,27 @@ using SigMap = mozilla::Vector<std::wstr
 static const size_t kMaxStrLen = 80;
 
 static mozilla::UniquePtr<char[]> WideToUTF8(const wchar_t* aStr,
                                              const size_t aStrLenExclNul) {
   // Yes, this might not handle surrogate pairs correctly. Let's just let
   // WideCharToMultiByte fail in that unlikely case.
   size_t cvtLen = std::min(aStrLenExclNul, kMaxStrLen);
 
-  int numConv = ::WideCharToMultiByte(CP_UTF8, 0, aStr, cvtLen, nullptr,
-                                      0, nullptr, nullptr);
+  int numConv = ::WideCharToMultiByte(CP_UTF8, 0, aStr, cvtLen, nullptr, 0,
+                                      nullptr, nullptr);
   if (!numConv) {
     return nullptr;
   }
 
   // Include room for the null terminator by adding one
   auto buf = mozilla::MakeUnique<char[]>(numConv + 1);
 
-  numConv = ::WideCharToMultiByte(CP_UTF8, 0, aStr, cvtLen, buf.get(),
-                                  numConv, nullptr, nullptr);
+  numConv = ::WideCharToMultiByte(CP_UTF8, 0, aStr, cvtLen, buf.get(), numConv,
+                                  nullptr, nullptr);
   if (!numConv) {
     return nullptr;
   }
 
   // Add null termination. numConv does not include the terminator, so we don't
   // subtract 1 when indexing into buf.
   buf[numConv] = 0;
 
@@ -202,17 +202,17 @@ static mozilla::UniquePtr<char[]> WideTo
   return WideToUTF8(aStr, wcslen(aStr));
 }
 
 static mozilla::UniquePtr<char[]> WideToUTF8(const std::wstring& aStr) {
   return WideToUTF8(aStr.c_str(), aStr.length());
 }
 
 // MinGW does not support the Windows Security Center APIs.
-#if !defined(__MINGW32__)
+#  if !defined(__MINGW32__)
 
 static mozilla::UniquePtr<char[]> WideToUTF8(const _bstr_t& aStr) {
   return WideToUTF8(static_cast<const wchar_t*>(aStr), aStr.length());
 }
 
 namespace {
 
 struct ProviderKey {
@@ -322,17 +322,17 @@ static bool AddWscInfo(mozilla::JSONWrit
 
     aJson.EndArray();
   }
 
   aJson.EndObject();
 
   return true;
 }
-#endif  // !defined(__MINGW32__)
+#  endif  // !defined(__MINGW32__)
 
 // Max array length for telemetry intake.
 static const size_t kMaxArrayLen = 1000;
 
 static bool AddModuleInfo(const nsAutoHandle& aSnapshot,
                           mozilla::JSONWriter& aJson) {
   if (aSnapshot.get() == INVALID_HANDLE_VALUE) {
     // We haven't written anything yet, so we can return true here and continue
@@ -447,26 +447,25 @@ struct PingThreadContext {
       : mLauncherError(aError),
         mModulesSnapshot(::CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 0)) {}
   mozilla::LauncherError mLauncherError;
   nsAutoHandle mModulesSnapshot;
 };
 
 }  // anonymous namespace
 
-static bool PrepPing(const PingThreadContext& aContext,
-                     const std::wstring& aId,
+static bool PrepPing(const PingThreadContext& aContext, const std::wstring& aId,
                      mozilla::JSONWriter& aJson) {
-#if defined(DEBUG)
+#  if defined(DEBUG)
   const mozilla::JSONWriter::CollectionStyle style =
       mozilla::JSONWriter::MultiLineStyle;
-#else
+#  else
   const mozilla::JSONWriter::CollectionStyle style =
       mozilla::JSONWriter::SingleLineStyle;
-#endif  // defined(DEBUG)
+#  endif  // defined(DEBUG)
 
   aJson.Start(style);
 
   aJson.StringProperty("type", "launcher-process-failure");
   aJson.IntProperty("version", 1);
 
   auto idUtf8 = WideToUTF8(aId);
   if (idUtf8) {
@@ -558,21 +557,21 @@ static bool PrepPing(const PingThreadCon
   }
 
   aJson.StringProperty("source_file", srcFileLeaf.c_str());
 
   aJson.IntProperty("source_line", aContext.mLauncherError.mLine);
   aJson.IntProperty("hresult", aContext.mLauncherError.mError.AsHResult());
   aJson.EndObject();
 
-#if !defined(__MINGW32__)
+#  if !defined(__MINGW32__)
   if (!AddWscInfo(aJson)) {
     return false;
   }
-#endif  // !defined(__MINGW32__)
+#  endif  // !defined(__MINGW32__)
 
   if (!AddModuleInfo(aContext.mModulesSnapshot, aJson)) {
     return false;
   }
 
   aJson.End();
 
   return true;
@@ -677,28 +676,28 @@ static unsigned __stdcall SendPingThread
 
   return 0;
 }
 
 #endif  // defined(MOZ_TELEMETRY_REPORTING)
 
 static bool SendPing(const mozilla::LauncherError& aError) {
 #if defined(MOZ_TELEMETRY_REPORTING)
-#if defined(MOZ_LAUNCHER_PROCESS)
+#  if defined(MOZ_LAUNCHER_PROCESS)
   mozilla::LauncherRegistryInfo regInfo;
   mozilla::LauncherResult<mozilla::LauncherRegistryInfo::EnabledState>
       launcherEnabled = regInfo.IsEnabled();
   if (launcherEnabled.isErr() ||
       launcherEnabled.unwrap() ==
           mozilla::LauncherRegistryInfo::EnabledState::ForceDisabled) {
     // If the launcher is force disabled, we do not send any pings
     // (since studies and thus telemetry have been opted out)
     return false;
   }
-#endif  // defined(MOZ_LAUNCHER_PROCESS)
+#  endif  // defined(MOZ_LAUNCHER_PROCESS)
 
   // We send this ping when the launcher process fails. After we start the
   // SendPingThread, this thread falls back from running as the launcher process
   // to running as the browser main thread. Once this happens, it will be unsafe
   // to set up PoisonIOInterposer (since we have already spun up a background
   // thread).
   mozilla::SaveToEnv("MOZ_DISABLE_POISON_IO_INTERPOSER=1");
 
@@ -740,13 +739,11 @@ void HandleLauncherError(const LauncherE
 
   PostErrorToLog(aError);
 }
 
 void SetLauncherErrorAppData(const StaticXREAppData& aAppData) {
   gAppData = &aAppData;
 }
 
-void SetLauncherErrorForceEventLog() {
-  gForceEventLog = true;
-}
+void SetLauncherErrorForceEventLog() { gForceEventLog = true; }
 
 }  // namespace mozilla
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -713,20 +713,22 @@ html|input.urlbar-input {
 #urlbar:not([actiontype="switchtab"]):not([actiontype="extension"]) > #urlbar-display-box,
 #urlbar:not([actiontype="switchtab"]) > #urlbar-display-box > #switchtab,
 #urlbar:not([actiontype="extension"]) > #urlbar-display-box > #extension {
   display: none;
 }
 
 #PopupAutoComplete > richlistbox > richlistitem[originaltype="loginsFooter"] {
   color: var(--btn-text-color);
-  padding: 0 20px;
   min-height: 40px;
   border-top: 1px solid rgba(38,38,38,.15);
   background-color: #EDEDED;
+  display: flex;
+  justify-content: center;
+  align-items: center;
 }
 
 #PopupAutoComplete > richlistbox > richlistitem[originaltype="loginsFooter"]:hover,
 #PopupAutoComplete > richlistbox > richlistitem[originaltype="loginsFooter"][selected] {
   background-color: #DCDCDE;
 }
 
 #PopupAutoComplete[firstresultstyle="insecureWarning"] {
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -260,16 +260,21 @@ var gURLBarHandler = {
     }
   },
 };
 
 XPCOMUtils.defineLazyPreferenceGetter(gURLBarHandler, "quantumbar",
                                       "browser.urlbar.quantumbar", false,
                                       gURLBarHandler.handlePrefChange.bind(gURLBarHandler));
 
+XPCOMUtils.defineLazyGetter(this, "ReferrerInfo", () =>
+  Components.Constructor("@mozilla.org/referrer-info;1",
+                         "nsIReferrerInfo",
+                         "init"));
+
 // High priority notification bars shown at the top of the window.
 XPCOMUtils.defineLazyGetter(this, "gHighPriorityNotificationBox", () => {
   return new MozElements.NotificationBox(element => {
     element.classList.add("global-notificationbox");
     element.setAttribute("notificationside", "top");
     document.getElementById("appcontent").prepend(element);
   });
 });
@@ -1735,69 +1740,58 @@ var gBrowserInit = {
           gURLBar.removeAttribute("focused");
       });
     }
   },
 
   _handleURIToLoad() {
     this._callWithURIToLoad(uriToLoad => {
       if (!uriToLoad) {
-        // We don't check whether window.arguments[6] (userContextId) is set
+        // We don't check whether window.arguments[5] (userContextId) is set
         // because tabbrowser.js takes care of that for the initial tab.
         return;
       }
 
       // We don't check if uriToLoad is a XULElement because this case has
       // already been handled before first paint, and the argument cleared.
       if (Array.isArray(uriToLoad)) {
         // This function throws for certain malformed URIs, so use exception handling
         // so that we don't disrupt startup
         try {
           gBrowser.loadTabs(uriToLoad, {
             inBackground: false,
             replace: true,
             // See below for the semantics of window.arguments. Only the minimum is supported.
-            userContextId: window.arguments[6],
-            triggeringPrincipal: window.arguments[8] || Services.scriptSecurityManager.getSystemPrincipal(),
-            allowInheritPrincipal: window.arguments[9],
-            csp: window.arguments[10],
+            userContextId: window.arguments[5],
+            triggeringPrincipal: window.arguments[7] || Services.scriptSecurityManager.getSystemPrincipal(),
+            allowInheritPrincipal: window.arguments[8],
+            csp: window.arguments[9],
             fromExternal: true,
           });
         } catch (e) {}
       } else if (window.arguments.length >= 3) {
         // window.arguments[1]: unused (bug 871161)
-        //                 [2]: referrer (nsIURI | string)
+        //                 [2]: referrerInfo (nsIReferrerInfo)
         //                 [3]: postData (nsIInputStream)
         //                 [4]: allowThirdPartyFixup (bool)
-        //                 [5]: referrerPolicy (int)
-        //                 [6]: userContextId (int)
-        //                 [7]: originPrincipal (nsIPrincipal)
-        //                 [8]: triggeringPrincipal (nsIPrincipal)
-        //                 [9]: allowInheritPrincipal (bool)
-        //                [10]: csp (nsIContentSecurityPolicy)
-        let referrerURI = window.arguments[2];
-        if (typeof(referrerURI) == "string") {
-          try {
-            referrerURI = makeURI(referrerURI);
-          } catch (e) {
-            referrerURI = null;
-          }
-        }
-        let referrerPolicy = (window.arguments[5] != undefined ?
-            window.arguments[5] : Ci.nsIHttpChannel.REFERRER_POLICY_UNSET);
-        let userContextId = (window.arguments[6] != undefined ?
-            window.arguments[6] : Ci.nsIScriptSecurityManager.DEFAULT_USER_CONTEXT_ID);
-        loadURI(uriToLoad, referrerURI, window.arguments[3] || null,
-                window.arguments[4] || false, referrerPolicy, userContextId,
+        //                 [5]: userContextId (int)
+        //                 [6]: originPrincipal (nsIPrincipal)
+        //                 [7]: triggeringPrincipal (nsIPrincipal)
+        //                 [8]: allowInheritPrincipal (bool)
+        //                 [9]: csp (nsIContentSecurityPolicy)
+        let userContextId = (window.arguments[5] != undefined ?
+            window.arguments[5] : Ci.nsIScriptSecurityManager.DEFAULT_USER_CONTEXT_ID);
+        loadURI(uriToLoad, window.arguments[2] || null, window.arguments[3] || null,
+                window.arguments[4] || false, userContextId,
                 // pass the origin principal (if any) and force its use to create
                 // an initial about:blank viewer if present:
-                window.arguments[7], !!window.arguments[7], window.arguments[8],
+                window.arguments[6], !!window.arguments[6], window.arguments[7],
                 // TODO fix allowInheritPrincipal to default to false.
                 // Default to true unless explicitly set to false because of bug 1475201.
-                window.arguments[9] !== false, window.arguments[10]);
+                window.arguments[8] !== false, window.arguments[9]);
         window.focus();
       } else {
         // Note: loadOneOrMoreURIs *must not* be called if window.arguments.length >= 3.
         // Such callers expect that window.arguments[0] is handled as a single URI.
         loadOneOrMoreURIs(uriToLoad, Services.scriptSecurityManager.getSystemPrincipal());
       }
     });
   },
@@ -2504,27 +2498,26 @@ function BrowserCloseTabOrWindow(event) 
   gBrowser.removeCurrentTab({animate: true});
 }
 
 function BrowserTryToCloseWindow() {
   if (WindowIsClosing())
     window.close(); // WindowIsClosing does all the necessary checks
 }
 
-function loadURI(uri, referrer, postData, allowThirdPartyFixup, referrerPolicy,
+function loadURI(uri, referrerInfo, postData, allowThirdPartyFixup,
                  userContextId, originPrincipal, forceAboutBlankViewerInCurrent,
                  triggeringPrincipal, allowInheritPrincipal = false, csp = null) {
   if (!triggeringPrincipal) {
     throw new Error("Must load with a triggering Principal");
   }
 
   try {
     openLinkIn(uri, "current",
-               { referrerURI: referrer,
-                 referrerPolicy,
+               { referrerInfo,
                  postData,
                  allowThirdPartyFixup,
                  userContextId,
                  originPrincipal,
                  triggeringPrincipal,
                  csp,
                  forceAboutBlankViewerInCurrent,
                  allowInheritPrincipal,
@@ -5538,17 +5531,17 @@ var TabsProgressListener = {
   },
 };
 
 function nsBrowserAccess() { }
 
 nsBrowserAccess.prototype = {
   QueryInterface: ChromeUtils.generateQI([Ci.nsIBrowserDOMWindow]),
 
-  _openURIInNewTab(aURI, aReferrer, aReferrerPolicy, aIsPrivate,
+  _openURIInNewTab(aURI, aReferrerInfo, aIsPrivate,
                    aIsExternal, aForceNotRemote = false,
                    aUserContextId = Ci.nsIScriptSecurityManager.DEFAULT_USER_CONTEXT_ID,
                    aOpenerWindow = null, aOpenerBrowser = null,
                    aTriggeringPrincipal = null, aNextTabParentId = 0, aName = "", aCsp = null) {
     let win, needToFocusWin;
 
     // try the current window.  if we're in a popup, fall back on the most recent browser window
     if (window.toolbar.visible) {
@@ -5568,18 +5561,17 @@ nsBrowserAccess.prototype = {
       win.focus();
       return win.gBrowser.selectedBrowser;
     }
 
     let loadInBackground = Services.prefs.getBoolPref("browser.tabs.loadDivertedInBackground");
 
     let tab = win.gBrowser.loadOneTab(aURI ? aURI.spec : "about:blank", {
                                       triggeringPrincipal: aTriggeringPrincipal,
-                                      referrerURI: aReferrer,
-                                      referrerPolicy: aReferrerPolicy,
+                                      referrerInfo: aReferrerInfo,
                                       userContextId: aUserContextId,
                                       fromExternal: aIsExternal,
                                       inBackground: loadInBackground,
                                       forceNotRemote: aForceNotRemote,
                                       opener: aOpenerWindow,
                                       openerBrowser: aOpenerBrowser,
                                       nextTabParentId: aNextTabParentId,
                                       name: aName,
@@ -5633,20 +5625,20 @@ nsBrowserAccess.prototype = {
     if (aWhere == Ci.nsIBrowserDOMWindow.OPEN_DEFAULTWINDOW) {
       if (isExternal &&
           Services.prefs.prefHasUserValue("browser.link.open_newwindow.override.external"))
         aWhere = Services.prefs.getIntPref("browser.link.open_newwindow.override.external");
       else
         aWhere = Services.prefs.getIntPref("browser.link.open_newwindow");
     }
 
-    let referrer = aOpener ? makeURI(aOpener.location.href) : null;
-    let referrerPolicy = Ci.nsIHttpChannel.REFERRER_POLICY_UNSET;
+    let referrerInfo = new ReferrerInfo(Ci.nsIHttpChannel.REFERRER_POLICY_UNSET, true,
+      aOpener ? makeURI(aOpener.location.href) : null);
     if (aOpener && aOpener.document) {
-      referrerPolicy = aOpener.document.referrerPolicy;
+      referrerInfo.referrerPolicy = aOpener.document.referrerPolicy;
     }
     // Bug 965637, query the CSP from the doc instead of the Principal
     let csp = aTriggeringPrincipal.csp;
     let isPrivate = aOpener
                   ? PrivateBrowsingUtils.isContentWindowPrivate(aOpener)
                   : PrivateBrowsingUtils.isWindowPrivate(window);
 
     switch (aWhere) {
@@ -5658,34 +5650,34 @@ nsBrowserAccess.prototype = {
         if (isPrivate) {
           features += ",private";
         }
         // Pass all params to openDialog to ensure that "url" isn't passed through
         // loadOneOrMoreURIs, which splits based on "|"
         try {
           newWindow = openDialog(AppConstants.BROWSER_CHROME_URL, "_blank", features,
                       // window.arguments
-                      url, null, null, null, null, null, null, null, aTriggeringPrincipal);
+                      url, null, null, null, null, null, null, aTriggeringPrincipal);
         } catch (ex) {
           Cu.reportError(ex);
         }
         break;
       case Ci.nsIBrowserDOMWindow.OPEN_NEWTAB :
         // If we have an opener, that means that the caller is expecting access
         // to the nsIDOMWindow of the opened tab right away. For e10s windows,
         // this means forcing the newly opened browser to be non-remote so that
         // we can hand back the nsIDOMWindow. The XULBrowserWindow.shouldLoadURI
         // will do the job of shuttling off the newly opened browser to run in
         // the right process once it starts loading a URI.
         let forceNotRemote = !!aOpener;
         let userContextId = aOpener && aOpener.document
                               ? aOpener.document.nodePrincipal.originAttributes.userContextId
                               : Ci.nsIScriptSecurityManager.DEFAULT_USER_CONTEXT_ID;
         let openerWindow = (aFlags & Ci.nsIBrowserDOMWindow.OPEN_NO_OPENER) ? null : aOpener;
-        let browser = this._openURIInNewTab(aURI, referrer, referrerPolicy,
+        let browser = this._openURIInNewTab(aURI, referrerInfo,
                                             isPrivate, isExternal,
                                             forceNotRemote, userContextId,
                                             openerWindow, null, aTriggeringPrincipal,
                                             0, "", csp);
         if (browser)
           newWindow = browser.contentWindow;
         break;
       default : // OPEN_CURRENTWINDOW or an illegal value
@@ -5693,18 +5685,17 @@ nsBrowserAccess.prototype = {
         if (aURI) {
           let loadflags = isExternal ?
                             Ci.nsIWebNavigation.LOAD_FLAGS_FROM_EXTERNAL :
                             Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
           gBrowser.loadURI(aURI.spec, {
             triggeringPrincipal: aTriggeringPrincipal,
             csp,
             flags: loadflags,
-            referrerURI: referrer,
-            referrerPolicy,
+            referrerInfo,
           });
         }
         if (!Services.prefs.getBoolPref("browser.tabs.loadDivertedInBackground"))
           window.focus();
     }
     return newWindow;
   },
 
@@ -5732,19 +5723,18 @@ nsBrowserAccess.prototype = {
 
     var isExternal = !!(aFlags & Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL);
 
     var userContextId = aParams.openerOriginAttributes &&
                         ("userContextId" in aParams.openerOriginAttributes)
                           ? aParams.openerOriginAttributes.userContextId
                           : Ci.nsIScriptSecurityManager.DEFAULT_USER_CONTEXT_ID;
 
-    let referrer = aParams.referrer ? makeURI(aParams.referrer) : null;
-    return this._openURIInNewTab(aURI, referrer,
-                                 aParams.referrerPolicy,
+    return this._openURIInNewTab(aURI,
+                                 aParams.referrerInfo,
                                  aParams.isPrivate,
                                  isExternal, false,
                                  userContextId, null, aParams.openerBrowser,
                                  aParams.triggeringPrincipal,
                                  aNextTabParentId, aName, aParams.csp);
   },
 
   isTabContentWindow(aWindow) {
@@ -6290,27 +6280,29 @@ function handleLinkClick(event, href, li
     let referrerAttrValue = Services.netUtils.parseAttributePolicyString(linkNode.
                             getAttribute("referrerpolicy"));
     if (referrerAttrValue != Ci.nsIHttpChannel.REFERRER_POLICY_UNSET) {
       referrerPolicy = referrerAttrValue;
     }
   }
 
   let frameOuterWindowID = WebNavigationFrames.getFrameId(doc.defaultView);
+  let referrerInfo = new ReferrerInfo(
+    referrerPolicy,
+    !BrowserUtils.linkHasNoReferrer(linkNode),
+    referrerURI);
 
   // Bug 965637, query the CSP from the doc instead of the Principal
   let csp = doc.nodePrincipal.csp;
 
   urlSecurityCheck(href, doc.nodePrincipal);
   let params = {
     charset: doc.characterSet,
     allowMixedContent: persistAllowMixedContentInChildTab,
-    referrerURI,
-    referrerPolicy,
-    noReferrer: BrowserUtils.linkHasNoReferrer(linkNode),
+    referrerInfo,
     originPrincipal: doc.nodePrincipal,
     triggeringPrincipal: doc.nodePrincipal,
     csp,
     frameOuterWindowID,
   };
 
   // The new tab/window must use the same userContextId
   if (doc.nodePrincipal.originAttributes.userContextId) {
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -40,29 +40,40 @@ function openContextMenu(aMessage) {
 
   if (spellInfo) {
     spellInfo.target = aMessage.target.messageManager;
   }
 
   let documentURIObject = makeURI(data.docLocation,
                                   data.charSet,
                                   makeURI(data.baseURI));
+  let ReferrerInfo = Components.Constructor("@mozilla.org/referrer-info;1",
+                                        "nsIReferrerInfo",
+                                        "init");
+  let referrerInfo = new ReferrerInfo(
+    data.referrerPolicy,
+    !data.context.linkHasNoReferrer,
+    documentURIObject);
+  let frameReferrerInfo = new ReferrerInfo(
+    data.referrerPolicy,
+    !data.context.linkHasNoReferrer,
+    data.referrer ? makeURI(data.referrer) : null);
   gContextMenuContentData = { context: data.context,
                               isRemote: data.isRemote,
                               popupNodeSelectors: data.popupNodeSelectors,
                               browser,
                               editFlags: data.editFlags,
                               spellInfo,
                               principal: data.principal,
                               customMenuItems: data.customMenuItems,
                               documentURIObject,
                               docLocation: data.docLocation,
                               charSet: data.charSet,
-                              referrer: data.referrer,
-                              referrerPolicy: data.referrerPolicy,
+                              referrerInfo,
+                              frameReferrerInfo,
                               contentType: data.contentType,
                               contentDisposition: data.contentDisposition,
                               frameOuterWindowID: data.frameOuterWindowID,
                               selectionInfo: data.selectionInfo,
                               disableSetDesktopBackground: data.disableSetDesktopBg,
                               loginFillInfo: data.loginFillInfo,
                               parentAllowsMixedContent: data.parentAllowsMixedContent,
                               userContextId: data.userContextId,
@@ -189,17 +200,16 @@ nsContextMenu.prototype = {
     this.inFrame             = context.inFrame;
     this.inSrcdocFrame       = context.inSrcdocFrame;
     this.inSyntheticDoc      = context.inSyntheticDoc;
     this.inTabBrowser        = context.inTabBrowser;
     this.inWebExtBrowser     = context.inWebExtBrowser;
 
     this.link                = context.link;
     this.linkDownload        = context.linkDownload;
-    this.linkHasNoReferrer   = context.linkHasNoReferrer;
     this.linkProtocol        = context.linkProtocol;
     this.linkTextStr         = context.linkTextStr;
     this.linkURL             = context.linkURL;
     this.linkURI             = this.getLinkURI(); // can't send; regenerate
 
     this.onAudio             = context.onAudio;
     this.onCanvas            = context.onCanvas;
     this.onCompletedImage    = context.onCompletedImage;
@@ -776,37 +786,37 @@ nsContextMenu.prototype = {
     return DevToolsShim.inspectA11Y(gBrowser.selectedTab, this.targetSelectors);
   },
 
   _openLinkInParameters(extra) {
     let params = { charset: gContextMenuContentData.charSet,
                    originPrincipal: this.principal,
                    triggeringPrincipal: this.principal,
                    csp: this.csp,
-                   referrerURI: gContextMenuContentData.documentURIObject,
-                   referrerPolicy: gContextMenuContentData.referrerPolicy,
-                   frameOuterWindowID: gContextMenuContentData.frameOuterWindowID,
-                   noReferrer: this.linkHasNoReferrer || this.onPlainTextLink };
+                   frameOuterWindowID: gContextMenuContentData.frameOuterWindowID};
     for (let p in extra) {
       params[p] = extra[p];
     }
 
     if (!this.isRemote) {
       // Propagate the frameOuterWindowID value saved when
       // the context menu has been opened.
       params.frameOuterWindowID = this.frameOuterWindowID;
     }
 
+    let referrerInfo = gContextMenuContentData.referrerInfo;
     // If we want to change userContextId, we must be sure that we don't
     // propagate the referrer.
-    if ("userContextId" in params &&
-        params.userContextId != gContextMenuContentData.userContextId) {
-      params.noReferrer = true;
+    if (("userContextId" in params &&
+        params.userContextId != gContextMenuContentData.userContextId) ||
+      this.onPlainTextLink) {
+      referrerInfo.sendReferrer = false;
     }
 
+    params.referrerInfo = referrerInfo;
     return params;
   },
 
   // Open linked-to URL in a new window.
   openLink() {
     openLinkIn(this.linkURL, "window", this._openLinkInParameters());
   },
 
@@ -845,47 +855,44 @@ nsContextMenu.prototype = {
 
   // open URL in current tab
   openLinkInCurrent() {
     openLinkIn(this.linkURL, "current", this._openLinkInParameters());
   },
 
   // Open frame in a new tab.
   openFrameInTab() {
-    let referrer = gContextMenuContentData.referrer;
     openLinkIn(gContextMenuContentData.docLocation, "tab",
                { charset: gContextMenuContentData.charSet,
                  triggeringPrincipal: this.browser.contentPrincipal,
-                 referrerURI: referrer ? makeURI(referrer) : null });
+                 referrerInfo: gContextMenuContentData.frameReferrerInfo });
   },
 
   // Reload clicked-in frame.
   reloadFrame(aEvent) {
     let forceReload = aEvent.shiftKey;
     this.browser.messageManager.sendAsyncMessage("ContextMenu:ReloadFrame",
                                                  null, { target: this.target, forceReload });
   },
 
   // Open clicked-in frame in its own window.
   openFrame() {
-    let referrer = gContextMenuContentData.referrer;
     openLinkIn(gContextMenuContentData.docLocation, "window",
                { charset: gContextMenuContentData.charSet,
                  triggeringPrincipal: this.browser.contentPrincipal,
-                 referrerURI: referrer ? makeURI(referrer) : null });
+                 referrerInfo: gContextMenuContentData.frameReferrerInfo });
   },
 
   // Open clicked-in frame in the same window.
   showOnlyThisFrame() {
     urlSecurityCheck(gContextMenuContentData.docLocation,
                      this.browser.contentPrincipal,
                      Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT);
-    let referrer = gContextMenuContentData.referrer;
     openWebLinkIn(gContextMenuContentData.docLocation, "current", {
-      referrerURI: referrer ? makeURI(referrer) : null,
+      referrerInfo: gContextMenuContentData.frameReferrerInfo,
       triggeringPrincipal: this.browser.contentPrincipal,
     });
   },
 
   // View Partial Source
   viewPartialSource() {
     let {browser} = this;
     let openSelectionFn = function() {
@@ -928,17 +935,17 @@ nsContextMenu.prototype = {
     BrowserPageInfo(gContextMenuContentData.docLocation, "mediaTab",
                     this.imageInfo, null, this.browser);
   },
 
   viewImageDesc(e) {
     urlSecurityCheck(this.imageDescURL,
                      this.principal,
                      Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT);
-    openUILink(this.imageDescURL, e, { referrerURI: gContextMenuContentData.documentURIObject,
+    openUILink(this.imageDescURL, e, { referrerInfo: gContextMenuContentData.referrerInfo,
                                        triggeringPrincipal: this.principal,
     });
   },
 
   viewFrameInfo() {
     BrowserPageInfo(gContextMenuContentData.docLocation, null, null,
                     this.frameOuterWindowID, this.browser);
   },
@@ -962,28 +969,28 @@ nsContextMenu.prototype = {
         resolve(message.data.blobURL);
       };
       mm.addMessageListener("ContextMenu:Canvas:ToBlobURL:Result", onMessage);
     });
   },
 
   // Change current window to the URL of the image, video, or audio.
   viewMedia(e) {
-    let referrerURI = gContextMenuContentData.documentURIObject;
+    let referrerInfo = gContextMenuContentData.referrerInfo;
     let systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
     if (this.onCanvas) {
       this._canvasToBlobURL(this.target).then(function(blobURL) {
-        openUILink(blobURL, e, { referrerURI,
+        openUILink(blobURL, e, { referrerInfo,
                                  triggeringPrincipal: systemPrincipal});
       }, Cu.reportError);
     } else {
       urlSecurityCheck(this.mediaURL,
                        this.principal,
                        Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT);
-      openUILink(this.mediaURL, e, { referrerURI,
+      openUILink(this.mediaURL, e, { referrerInfo,
                                      forceAllowDataURI: true,
                                      triggeringPrincipal: this.principal,
       });
     }
   },
 
   saveVideoFrameAsImage() {
     let mm = this.browser.messageManager;
@@ -1030,17 +1037,17 @@ nsContextMenu.prototype = {
   },
 
   // Change current window to the URL of the background image.
   viewBGImage(e) {
     urlSecurityCheck(this.bgImageURL,
                      this.principal,
                      Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT);
 
-    openUILink(this.bgImageURL, e, { referrerURI: gContextMenuContentData.documentURIObject,
+    openUILink(this.bgImageURL, e, { referrerInfo: gContextMenuContentData.referrerInfo,
                                      triggeringPrincipal: this.principal,
     });
   },
 
   setDesktopBackground() {
     let mm = this.browser.messageManager;
 
     mm.sendAsyncMessage("ContextMenu:SetAsDesktopBackground", null,
--- a/browser/base/content/tabbrowser.js
+++ b/browser/base/content/tabbrowser.js
@@ -277,17 +277,17 @@ window._gBrowser = {
 
   get selectedBrowser() {
     return this._selectedBrowser;
   },
 
   _setupInitialBrowserAndTab() {
     // See browser.js for the meaning of window.arguments.
     // Bug 1485961 covers making this more sane.
-    let userContextId = window.arguments && window.arguments[6];
+    let userContextId = window.arguments && window.arguments[5];
 
     let tabArgument = gBrowserInit.getTabToAdopt();
 
     // We only need sameProcessAsFrameLoader in the case where we're passed a tab
     let sameProcessAsFrameLoader;
     // If we have a tab argument with browser, we use its remoteType. Otherwise,
     // if e10s is disabled or there's a parent process opener (e.g. parent
     // process about: page) for the content tab, we use a parent
@@ -1381,56 +1381,53 @@ window._gBrowser = {
 
     if (aTab.selected) {
       this.updateTitlebar();
     }
 
     return true;
   },
 
-  loadOneTab(aURI, aReferrerURI, aCharset, aPostData, aLoadInBackground, aAllowThirdPartyFixup) {
+  loadOneTab(aURI, aReferrerInfoOrParams, aCharset, aPostData, aLoadInBackground, aAllowThirdPartyFixup) {
     var aTriggeringPrincipal;
-    var aReferrerPolicy;
+    var aReferrerInfo;
     var aFromExternal;
     var aRelatedToCurrent;
     var aAllowInheritPrincipal;
     var aAllowMixedContent;
     var aSkipAnimation;
     var aForceNotRemote;
     var aPreferredRemoteType;
-    var aNoReferrer;
     var aUserContextId;
     var aSameProcessAsFrameLoader;
     var aOriginPrincipal;
     var aOpener;
     var aOpenerBrowser;
     var aCreateLazyBrowser;
     var aNextTabParentId;
     var aFocusUrlBar;
     var aName;
     var aCsp;
     if (arguments.length == 2 &&
         typeof arguments[1] == "object" &&
         !(arguments[1] instanceof Ci.nsIURI)) {
       let params = arguments[1];
       aTriggeringPrincipal = params.triggeringPrincipal;
-      aReferrerURI = params.referrerURI;
-      aReferrerPolicy = params.referrerPolicy;
+      aReferrerInfo = params.referrerInfo;
       aCharset = params.charset;
       aPostData = params.postData;
       aLoadInBackground = params.inBackground;
       aAllowThirdPartyFixup = params.allowThirdPartyFixup;
       aFromExternal = params.fromExternal;
       aRelatedToCurrent = params.relatedToCurrent;
       aAllowInheritPrincipal = !!params.allowInheritPrincipal;
       aAllowMixedContent = params.allowMixedContent;
       aSkipAnimation = params.skipAnimation;
       aForceNotRemote = params.forceNotRemote;
       aPreferredRemoteType = params.preferredRemoteType;
-      aNoReferrer = params.noReferrer;
       aUserContextId = params.userContextId;
       aSameProcessAsFrameLoader = params.sameProcessAsFrameLoader;
       aOriginPrincipal = params.originPrincipal;
       aOpener = params.opener;
       aOpenerBrowser = params.openerBrowser;
       aCreateLazyBrowser = params.createLazyBrowser;
       aNextTabParentId = params.nextTabParentId;
       aFocusUrlBar = params.focusUrlBar;
@@ -1444,31 +1441,29 @@ window._gBrowser = {
     }
 
     var bgLoad = (aLoadInBackground != null) ? aLoadInBackground :
       Services.prefs.getBoolPref("browser.tabs.loadInBackground");
     var owner = bgLoad ? null : this.selectedTab;
 
     var tab = this.addTab(aURI, {
       triggeringPrincipal: aTriggeringPrincipal,
-      referrerURI: aReferrerURI,
-      referrerPolicy: aReferrerPolicy,
+      referrerInfo: aReferrerInfo,
       charset: aCharset,
       postData: aPostData,
       ownerTab: owner,
       allowInheritPrincipal: aAllowInheritPrincipal,
       allowThirdPartyFixup: aAllowThirdPartyFixup,
       fromExternal: aFromExternal,
       relatedToCurrent: aRelatedToCurrent,
       skipAnimation: aSkipAnimation,
       allowMixedContent: aAllowMixedContent,
       forceNotRemote: aForceNotRemote,
       createLazyBrowser: aCreateLazyBrowser,
       preferredRemoteType: aPreferredRemoteType,
-      noReferrer: aNoReferrer,
       userContextId: aUserContextId,
       originPrincipal: aOriginPrincipal,
       sameProcessAsFrameLoader: aSameProcessAsFrameLoader,
       opener: aOpener,
       openerBrowser: aOpenerBrowser,
       nextTabParentId: aNextTabParentId,
       focusUrlBar: aFocusUrlBar,
       name: aName,
@@ -2339,26 +2334,24 @@ window._gBrowser = {
     focusUrlBar,
     forceNotRemote,
     fromExternal,
     index,
     lazyTabTitle,
     name,
     nextTabParentId,
     noInitialLabel,
-    noReferrer,
     opener,
     openerBrowser,
     originPrincipal,
     ownerTab,
     pinned,
     postData,
     preferredRemoteType,
-    referrerPolicy,
-    referrerURI,
+    referrerInfo,
     relatedToCurrent,
     sameProcessAsFrameLoader,
     skipAnimation,
     skipBackgroundNotify,
     triggeringPrincipal,
     userContextId,
     recordExecution,
     replayExecution,
@@ -2380,23 +2373,23 @@ window._gBrowser = {
     // determining positioning, and inherited attributes such as the
     // user context ID.
     //
     // If we have a browser opener (which is usually the browser
     // element from a remote window.open() call), use that.
     //
     // Otherwise, if the tab is related to the current tab (e.g.,
     // because it was opened by a link click), use the selected tab as
-    // the owner. If referrerURI is set, and we don't have an
+    // the owner. If referrerInfo is set, and we don't have an
     // explicit relatedToCurrent arg, we assume that the tab is
     // related to the current tab, since referrerURI is null or
     // undefined if the tab is opened from an external application or
     // bookmark (i.e. somewhere other than an existing tab).
     if (relatedToCurrent == null) {
-      relatedToCurrent = !!referrerURI;
+      relatedToCurrent = !!(referrerInfo && referrerInfo.originalReferrer);
     }
     let openerTab = ((openerBrowser && this.getTabForBrowser(openerBrowser)) ||
       (relatedToCurrent && this.selectedTab));
 
     var t = document.createXULElement("tab");
 
     t.openerTab = openerTab;
 
@@ -2532,19 +2525,21 @@ window._gBrowser = {
       // opener, use the opener's remote type.
       if (!preferredRemoteType && openerBrowser) {
         preferredRemoteType = openerBrowser.remoteType;
       }
 
       // If URI is about:blank and we don't have a preferred remote type,
       // then we need to use the referrer, if we have one, to get the
       // correct remote type for the new tab.
-      if (uriIsAboutBlank && !preferredRemoteType && referrerURI) {
+      if (uriIsAboutBlank && !preferredRemoteType && referrerInfo &&
+          referrerInfo.originalReferrer) {
         preferredRemoteType =
-          E10SUtils.getRemoteTypeForURI(referrerURI.spec, gMultiProcessBrowser);
+          E10SUtils.getRemoteTypeForURI(referrerInfo.originalReferrer.spec,
+                                        gMultiProcessBrowser);
       }
 
       let remoteType =
         forceNotRemote ? E10SUtils.NOT_REMOTE :
         E10SUtils.getRemoteTypeForURI(aURI, gMultiProcessBrowser,
           preferredRemoteType);
 
       // If we open a new tab with the newtab URL in the default
@@ -2662,26 +2657,21 @@ window._gBrowser = {
         flags |= Ci.nsIWebNavigation.LOAD_FLAGS_FROM_EXTERNAL;
       }
       if (allowMixedContent) {
         flags |= Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_MIXED_CONTENT;
       }
       if (!allowInheritPrincipal) {
         flags |= Ci.nsIWebNavigation.LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL;
       }
-
-      let ReferrerInfo = Components.Constructor("@mozilla.org/referrer-info;1",
-                                                "nsIReferrerInfo",
-                                                "init");
       try {
         b.loadURI(aURI, {
           flags,
           triggeringPrincipal,
-          referrerInfo: new ReferrerInfo(
-            referrerPolicy, !noReferrer, referrerURI),
+          referrerInfo,
           charset,
           postData,
           csp,
         });
       } catch (ex) {
         Cu.reportError(ex);
       }
     }
--- a/browser/base/content/test/about/browser.ini
+++ b/browser/base/content/test/about/browser.ini
@@ -8,16 +8,17 @@ support-files =
   dummy_page.html
 prefs =
   browser.newtabpage.activity-stream.improvesearch.handoffToAwesomebar=false
 
 [browser_aboutCertError.js]
 [browser_aboutCertError_clockSkew.js]
 [browser_aboutCertError_exception.js]
 [browser_aboutCertError_mitm.js]
+[browser_aboutCertError_noSubjectAltName.js]
 [browser_aboutCertError_telemetry.js]
 [browser_aboutHome_search_POST.js]
 [browser_aboutHome_search_composing.js]
 [browser_aboutHome_search_searchbar.js]
 [browser_aboutHome_search_suggestion.js]
 skip-if = os == "mac" || (os == "linux" && (!debug || bits == 64)) || (os == 'win' && os_version == '10.0' && bits == 64 && !debug) # Bug 1399648, bug 1402502
 [browser_aboutHome_search_telemetry.js]
 [browser_aboutNetError.js]
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/about/browser_aboutCertError_noSubjectAltName.js
@@ -0,0 +1,51 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const UNKNOWN_ISSUER = "https://no-subject-alt-name.example.com:443";
+
+const checkAdvancedAndGetTechnicalInfoText = async () => {
+  let doc = content.document;
+
+  let advancedButton = doc.getElementById("advancedButton");
+  ok(advancedButton, "advancedButton found");
+  is(advancedButton.hasAttribute("disabled"), false, "advancedButton should be clickable");
+  advancedButton.click();
+
+  let badCertAdvancedPanel = doc.getElementById("badCertAdvancedPanel");
+  ok(badCertAdvancedPanel, "badCertAdvancedPanel found");
+
+  let badCertTechnicalInfo = doc.getElementById("badCertTechnicalInfo");
+  ok(badCertTechnicalInfo, "badCertTechnicalInfo found");
+
+  let errorCode = doc.getElementById("errorCode").innerHTML;
+  is(errorCode, "SSL_ERROR_BAD_CERT_DOMAIN");
+
+  let viewCertificate = doc.getElementById("viewCertificate");
+  ok(viewCertificate, "viewCertificate found");
+
+  return badCertTechnicalInfo.innerHTML;
+};
+
+const checkCorrectMessages = (message) => {
+  let isCorrectMessage =
+    message.includes("Websites prove their identity via certificates."
+      + " Nightly does not trust this site because it uses a certificate that is"
+      + " not valid for no-subject-alt-name.example.com");
+  is(isCorrectMessage, true, "That message should appear");
+  let isWrongMessage =
+    message.includes("The certificate is only valid for ");
+  is(isWrongMessage, false, "That message shouldn't appear");
+};
+
+add_task(async function checkUntrustedCertError() {
+  info(`Loading ${UNKNOWN_ISSUER} which does not have a subject specified in the certificate`);
+  let tab = await openErrorPage(UNKNOWN_ISSUER);
+  let browser = tab.linkedBrowser;
+  info("Clicking the exceptionDialogButton in advanced panel");
+  let badCertTechnicalInfoText =
+    await ContentTask.spawn(browser, null, checkAdvancedAndGetTechnicalInfoText);
+  checkCorrectMessages(badCertTechnicalInfoText, browser);
+  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+});
--- a/browser/base/content/test/general/browser_relatedTabs.js
+++ b/browser/base/content/test/general/browser_relatedTabs.js
@@ -5,18 +5,24 @@
 add_task(async function() {
   is(gBrowser.tabs.length, 1, "one tab is open initially");
 
   // Add several new tabs in sequence, interrupted by selecting a
   // different tab, moving a tab around and closing a tab,
   // returning a list of opened tabs for verifying the expected order.
   // The new tab behaviour is documented in bug 465673
   let tabs = [];
+  let ReferrerInfo = Components.Constructor("@mozilla.org/referrer-info;1",
+                                            "nsIReferrerInfo",
+                                            "init");
+
   function addTab(aURL, aReferrer) {
-    let tab = BrowserTestUtils.addTab(gBrowser, aURL, {referrerURI: aReferrer});
+    let referrerInfo = new ReferrerInfo(
+      Ci.nsIHttpChannel.REFERRER_POLICY_UNSET, true, aReferrer);
+    let tab = BrowserTestUtils.addTab(gBrowser, aURL, { referrerInfo });
     tabs.push(tab);
     return BrowserTestUtils.browserLoaded(tab.linkedBrowser);
   }
 
   await addTab("http://mochi.test:8888/#0");
   gBrowser.selectedTab = tabs[0];
   await addTab("http://mochi.test:8888/#1");
   await addTab("http://mochi.test:8888/#2", gBrowser.currentURI);
--- a/browser/base/content/test/trackingUI/browser_trackingUI_cryptominers.js
+++ b/browser/base/content/test/trackingUI/browser_trackingUI_cryptominers.js
@@ -1,24 +1,27 @@
 /* eslint-disable mozilla/no-arbitrary-setTimeout */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 const TRACKING_PAGE = "http://example.org/browser/browser/base/content/test/trackingUI/trackingPage.html";
-const CM_PREF = "privacy.trackingprotection.cryptomining.enabled";
+const CM_PROTECTION_PREF = "privacy.trackingprotection.cryptomining.enabled";
+const CM_ANNOTATION_PREF = "privacy.trackingprotection.cryptomining.annotate.enabled";
 
 add_task(async function setup() {
   await SpecialPowers.pushPrefEnv({set: [
     [ ContentBlocking.prefIntroCount, ContentBlocking.MAX_INTROS ],
     [ "urlclassifier.features.cryptomining.blacklistHosts", "cryptomining.example.com" ],
+    [ "urlclassifier.features.cryptomining.annotate.blacklistHosts", "cryptomining.example.com" ],
     [ "privacy.trackingprotection.enabled", false ],
     [ "privacy.trackingprotection.annotate_channels", false ],
     [ "privacy.trackingprotection.fingerprinting.enabled", false ],
+    [ "privacy.trackingprotection.fingerprinting.annotate.enabled", false ],
   ]});
 });
 
 async function testIdentityState(hasException) {
   let promise = BrowserTestUtils.openNewForegroundTab({url: TRACKING_PAGE, gBrowser});
   let [tab] = await Promise.all([promise, waitForContentBlockingEvent()]);
 
   if (hasException) {
@@ -99,19 +102,21 @@ async function testSubview(hasException)
     ContentBlocking.enableForCurrentPage();
     await loaded;
   }
 
   BrowserTestUtils.removeTab(tab);
 }
 
 add_task(async function test() {
-  Services.prefs.setBoolPref(CM_PREF, true);
+  Services.prefs.setBoolPref(CM_PROTECTION_PREF, true);
+  Services.prefs.setBoolPref(CM_ANNOTATION_PREF, true);
 
   await testIdentityState(false);
   await testIdentityState(true);
 
   await testSubview(false);
   await testSubview(true);
 
-  Services.prefs.clearUserPref(CM_PREF);
+  Services.prefs.clearUserPref(CM_PROTECTION_PREF);
+  Services.prefs.clearUserPref(CM_ANNOTATION_PREF);
 });
 
--- a/browser/base/content/test/trackingUI/browser_trackingUI_fingerprinters.js
+++ b/browser/base/content/test/trackingUI/browser_trackingUI_fingerprinters.js
@@ -1,24 +1,27 @@
 /* eslint-disable mozilla/no-arbitrary-setTimeout */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 const TRACKING_PAGE = "http://example.org/browser/browser/base/content/test/trackingUI/trackingPage.html";
-const FP_PREF = "privacy.trackingprotection.fingerprinting.enabled";
+const FP_PROTECTION_PREF = "privacy.trackingprotection.fingerprinting.enabled";
+const FP_ANNOTATION_PREF = "privacy.trackingprotection.fingerprinting.annotate.enabled";
 
 add_task(async function setup() {
   await SpecialPowers.pushPrefEnv({set: [
     [ ContentBlocking.prefIntroCount, ContentBlocking.MAX_INTROS ],
     [ "urlclassifier.features.fingerprinting.blacklistHosts", "fingerprinting.example.com" ],
+    [ "urlclassifier.features.fingerprinting.annotate.blacklistHosts", "fingerprinting.example.com" ],
     [ "privacy.trackingprotection.enabled", false ],
     [ "privacy.trackingprotection.annotate_channels", false ],
     [ "privacy.trackingprotection.cryptomining.enabled", false ],
+    [ "privacy.trackingprotection.cryptomining.annotate.enabled", false ],
   ]});
 });
 
 async function testIdentityState(hasException) {
   let promise = BrowserTestUtils.openNewForegroundTab({url: TRACKING_PAGE, gBrowser});
   let [tab] = await Promise.all([promise, waitForContentBlockingEvent()]);
 
   if (hasException) {
@@ -99,19 +102,20 @@ async function testSubview(hasException)
     ContentBlocking.enableForCurrentPage();
     await loaded;
   }
 
   BrowserTestUtils.removeTab(tab);
 }
 
 add_task(async function test() {
-  Services.prefs.setBoolPref(FP_PREF, true);
+  Services.prefs.setBoolPref(FP_PROTECTION_PREF, true);
+  Services.prefs.setBoolPref(FP_ANNOTATION_PREF, true);
 
   await testIdentityState(false);
   await testIdentityState(true);
 
   await testSubview(false);
   await testSubview(true);
 
-  Services.prefs.clearUserPref(FP_PREF);
+  Services.prefs.clearUserPref(FP_PROTECTION_PREF);
+  Services.prefs.clearUserPref(FP_ANNOTATION_PREF);
 });
-
--- a/browser/base/content/test/webrtc/browser.ini
+++ b/browser/base/content/test/webrtc/browser.ini
@@ -19,9 +19,8 @@ skip-if = (os == 'linux') # Bug 1503991
 [browser_devices_get_user_media_tear_off_tab.js]
 [browser_devices_get_user_media_unprompted_access.js]
 [browser_devices_get_user_media_unprompted_access_in_frame.js]
 [browser_devices_get_user_media_unprompted_access_tear_off_tab.js]
 skip-if = (os == "win" && bits == 64) # win8: bug 1334752
 [browser_devices_get_user_media_unprompted_access_queue_request.js]
 [browser_webrtc_hooks.js]
 [browser_devices_get_user_media_queue_request.js]
-skip-if = (os == 'mac') || (os =='linux') # Bug 1518946
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -17,30 +17,29 @@ file, You can obtain one at http://mozil
 ]>
 
 <bindings id="urlbarBindings" xmlns="http://www.mozilla.org/xbl"
           xmlns:html="http://www.w3.org/1999/xhtml"
           xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
           xmlns:xbl="http://www.mozilla.org/xbl">
 
   <binding id="urlbar" extends="chrome://global/content/bindings/textbox.xml#textbox">
-    <content>
+    <content role="combobox">
       <children includes="box"/>
       <xul:moz-input-box anonid="moz-input-box"
                          tooltip="aHTMLTooltip"
                          class="urlbar-input-box"
                          flex="1">
         <children/>
         <html:input anonid="scheme"
                     class="urlbar-scheme textbox-input"
                     required="required"
                     xbl:inherits="textoverflow,focused"/>
         <html:input anonid="input"
                     class="urlbar-input textbox-input"
-                    role="combobox"
                     aria-owns="urlbarView-results"
                     aria-controls="urlbarView-results"
                     aria-autocomplete="both"
                     allowevents="true"
                     inputmode="mozAwesomebar"
                     xbl:inherits="value,maxlength,disabled,size,readonly,placeholder,tabindex,accesskey,focused,textoverflow"/>
       </xul:moz-input-box>
       <xul:image anonid="urlbar-go-button"
--- a/browser/base/content/utilityOverlay.js
+++ b/browser/base/content/utilityOverlay.js
@@ -97,37 +97,36 @@ function doGetProtocolFlags(aURI) {
  *
  * @param url {string}
  * @param event {Event | Object} Event or JSON object representing an Event
  * @param {Boolean | Object} aIgnoreButton
  * @param {Boolean} aIgnoreButton
  * @param {Boolean} aIgnoreAlt
  * @param {Boolean} aAllowThirdPartyFixup
  * @param {Object} aPostData
- * @param {nsIURI} aReferrerURI
+ * @param {Object} aReferrerInfo
  */
 function openUILink(url, event, aIgnoreButton, aIgnoreAlt, aAllowThirdPartyFixup,
-                    aPostData, aReferrerURI) {
+                    aPostData, aReferrerInfo) {
   event = getRootEvent(event);
   let params;
 
   if (aIgnoreButton && typeof aIgnoreButton == "object") {
     params = aIgnoreButton;
 
     // don't forward "ignoreButton" and "ignoreAlt" to openUILinkIn
     aIgnoreButton = params.ignoreButton;
     aIgnoreAlt = params.ignoreAlt;
     delete params.ignoreButton;
     delete params.ignoreAlt;
   } else {
     params = {
       allowThirdPartyFixup: aAllowThirdPartyFixup,
       postData: aPostData,
-      referrerURI: aReferrerURI,
-      referrerPolicy: Ci.nsIHttpChannel.REFERRER_POLICY_UNSET,
+      referrerInfo: aReferrerInfo,
       initiatingDoc: event ? event.target.ownerDocument : null,
     };
   }
 
   if (!params.triggeringPrincipal) {
     throw new Error("Required argument triggeringPrincipal missing within openUILink");
   }
 
@@ -275,25 +274,25 @@ function openWebLinkIn(url, where, param
  * allowThirdPartyFixup controls whether third party services such as Google's
  * I Feel Lucky are allowed to interpret this URL. This parameter may be
  * undefined, which is treated as false.
  *
  * Instead of aAllowThirdPartyFixup, you may also pass an object with any of
  * these properties:
  *   allowThirdPartyFixup (boolean)
  *   postData             (nsIInputStream)
- *   referrerURI          (nsIURI)
+ *   referrerInfo         (nsIReferrerInfo)
  *   relatedToCurrent     (boolean)
  *   skipTabAnimation     (boolean)
  *   allowPinnedTabHostChange (boolean)
  *   allowPopups          (boolean)
  *   userContextId        (unsigned int)
  *   targetBrowser        (XUL browser)
  */
-function openUILinkIn(url, where, aAllowThirdPartyFixup, aPostData, aReferrerURI) {
+function openUILinkIn(url, where, aAllowThirdPartyFixup, aPostData, aReferrerInfo) {
   var params;
 
   if (arguments.length == 3 && typeof arguments[2] == "object") {
     params = aAllowThirdPartyFixup;
   }
   if (!params || !params.triggeringPrincipal) {
     throw new Error("Required argument triggeringPrincipal missing within openUILinkIn");
   }
@@ -302,62 +301,63 @@ function openUILinkIn(url, where, aAllow
 
   openLinkIn(url, where, params);
 }
 
 /* eslint-disable complexity */
 function openLinkIn(url, where, params) {
   if (!where || !url)
     return;
+  let ReferrerInfo = Components.Constructor("@mozilla.org/referrer-info;1",
+                                            "nsIReferrerInfo",
+                                            "init");
 
   var aFromChrome           = params.fromChrome;
   var aAllowThirdPartyFixup = params.allowThirdPartyFixup;
   var aPostData             = params.postData;
   var aCharset              = params.charset;
-  var aReferrerURI          = params.referrerURI;
-  var aReferrerPolicy       = ("referrerPolicy" in params ?
-      params.referrerPolicy : Ci.nsIHttpChannel.REFERRER_POLICY_UNSET);
+  var aReferrerInfo       = ("referrerInfo" in params)
+    ? params.referrerInfo
+    : new ReferrerInfo(Ci.nsIHttpChannel.REFERRER_POLICY_UNSET, true, null);
   var aRelatedToCurrent     = params.relatedToCurrent;
   var aAllowInheritPrincipal = !!params.allowInheritPrincipal;
   var aAllowMixedContent    = params.allowMixedContent;
   var aForceAllowDataURI    = params.forceAllowDataURI;
   var aInBackground         = params.inBackground;
   var aInitiatingDoc        = params.initiatingDoc;
   var aIsPrivate            = params.private;
   var aSkipTabAnimation     = params.skipTabAnimation;
   var aAllowPinnedTabHostChange = !!params.allowPinnedTabHostChange;
-  var aNoReferrer           = params.noReferrer;
   var aAllowPopups          = !!params.allowPopups;
   var aUserContextId        = params.userContextId;
   var aIndicateErrorPageLoad = params.indicateErrorPageLoad;
   var aPrincipal            = params.originPrincipal;
   var aTriggeringPrincipal  = params.triggeringPrincipal;
   var aCsp                  = params.csp;
   var aForceAboutBlankViewerInCurrent =
       params.forceAboutBlankViewerInCurrent;
   var aResolveOnNewTabCreated = params.resolveOnNewTabCreated;
 
   if (!aTriggeringPrincipal) {
     throw new Error("Must load with a triggering Principal");
   }
 
   if (where == "save") {
     // TODO(1073187): propagate referrerPolicy.
-
     // ContentClick.jsm passes isContentWindowPrivate for saveURL instead of passing a CPOW initiatingDoc
     if ("isContentWindowPrivate" in params) {
-      saveURL(url, null, null, true, true, aNoReferrer ? null : aReferrerURI,
+      saveURL(url, null, null, true, true, aReferrerInfo.sendReferrer ? aReferrerInfo.originalReferrer : null,
               null, params.isContentWindowPrivate, aPrincipal);
     } else {
       if (!aInitiatingDoc) {
         Cu.reportError("openUILink/openLinkIn was called with " +
           "where == 'save' but without initiatingDoc.  See bug 814264.");
         return;
       }
-      saveURL(url, null, null, true, true, aNoReferrer ? null : aReferrerURI, aInitiatingDoc);
+      saveURL(url, null, null, true, true, aReferrerInfo.sendReferrer ? aReferrerInfo.originalReferrer : null, aInitiatingDoc);
     }
     return;
   }
 
   // Establish which window we'll load the link in.
   let w;
   if (where == "current" && params.targetBrowser) {
     w = params.targetBrowser.ownerGlobal;
@@ -391,17 +391,17 @@ function openLinkIn(url, where, params) 
   aTriggeringPrincipal = useOAForPrincipal(aTriggeringPrincipal);
 
   if (!w || where == "window") {
     let features = "chrome,dialog=no,all";
     if (aIsPrivate) {
       features += ",private";
       // To prevent regular browsing data from leaking to private browsing sites,
       // strip the referrer when opening a new private window. (See Bug: 1409226)
-      aNoReferrer = true;
+      aReferrerInfo.sendReferrer = false;
     }
 
     // This propagates to window.arguments.
     var sa = Cc["@mozilla.org/array;1"].
              createInstance(Ci.nsIMutableArray);
 
     var wuri = Cc["@mozilla.org/supports-string;1"].
                createInstance(Ci.nsISupportsString);
@@ -413,37 +413,25 @@ function openLinkIn(url, where, params) 
                   .createInstance(Ci.nsISupportsString);
       charset.data = "charset=" + aCharset;
     }
 
     var allowThirdPartyFixupSupports = Cc["@mozilla.org/supports-PRBool;1"].
                                        createInstance(Ci.nsISupportsPRBool);
     allowThirdPartyFixupSupports.data = aAllowThirdPartyFixup;
 
-    var referrerURISupports = null;
-    if (aReferrerURI && !aNoReferrer) {
-      referrerURISupports = Cc["@mozilla.org/supports-string;1"].
-                            createInstance(Ci.nsISupportsString);
-      referrerURISupports.data = aReferrerURI.spec;
-    }
-
-    var referrerPolicySupports = Cc["@mozilla.org/supports-PRUint32;1"].
-                                 createInstance(Ci.nsISupportsPRUint32);
-    referrerPolicySupports.data = aReferrerPolicy;
-
     var userContextIdSupports = Cc["@mozilla.org/supports-PRUint32;1"].
                                  createInstance(Ci.nsISupportsPRUint32);
     userContextIdSupports.data = aUserContextId;
 
     sa.appendElement(wuri);
     sa.appendElement(charset);
-    sa.appendElement(referrerURISupports);
+    sa.appendElement(aReferrerInfo);
     sa.appendElement(aPostData);
     sa.appendElement(allowThirdPartyFixupSupports);
-    sa.appendElement(referrerPolicySupports);
     sa.appendElement(userContextIdSupports);
     sa.appendElement(aPrincipal);
     sa.appendElement(aTriggeringPrincipal);
     sa.appendElement(null); // allowInheritPrincipal
     sa.appendElement(aCsp);
 
     const sourceWindow = (w || window);
     let win;
@@ -553,25 +541,21 @@ function openLinkIn(url, where, params) 
     // start a new recording.
     if (targetBrowser.hasAttribute("recordExecution") &&
         targetBrowser.currentURI.spec != "about:blank") {
       w.gBrowser.updateBrowserRemoteness(targetBrowser,
                                          { recordExecution: "*", newFrameloader: true,
                                            remoteType: E10SUtils.DEFAULT_REMOTE_TYPE });
     }
 
-    let ReferrerInfo = Components.Constructor("@mozilla.org/referrer-info;1",
-                                              "nsIReferrerInfo",
-                                              "init");
     targetBrowser.loadURI(url, {
       triggeringPrincipal: aTriggeringPrincipal,
       csp: aCsp,
-      referrerInfo: new ReferrerInfo(
-        aReferrerPolicy, !aNoReferrer, aReferrerURI),
       flags,
+      referrerInfo: aReferrerInfo,
       postData: aPostData,
       userContextId: aUserContextId,
     });
 
     // Don't focus the content area if focus is in the address bar and we're
     // loading the New Tab page.
     focusUrlBar = w.document.activeElement == w.gURLBar.inputField &&
                   w.isBlankPageURL(url);
@@ -579,26 +563,24 @@ function openLinkIn(url, where, params) 
   case "tabshifted":
     loadInBackground = !loadInBackground;
     // fall through
   case "tab":
     focusUrlBar = !loadInBackground && w.isBlankPageURL(url)
       && !aboutNewTabService.willNotifyUser;
 
     let tabUsedForLoad = w.gBrowser.loadOneTab(url, {
-      referrerURI: aReferrerURI,
-      referrerPolicy: aReferrerPolicy,
+      referrerInfo: aReferrerInfo,
       charset: aCharset,
       postData: aPostData,
       inBackground: loadInBackground,
       allowThirdPartyFixup: aAllowThirdPartyFixup,
       relatedToCurrent: aRelatedToCurrent,
       skipAnimation: aSkipTabAnimation,
       allowMixedContent: aAllowMixedContent,
-      noReferrer: aNoReferrer,
       userContextId: aUserContextId,
       originPrincipal: aPrincipal,
       triggeringPrincipal: aTriggeringPrincipal,
       allowInheritPrincipal: aAllowInheritPrincipal,
       csp: aCsp,
       focusUrlBar,
     });
     targetBrowser = tabUsedForLoad.linkedBrowser;
--- a/browser/components/BrowserContentHandler.jsm
+++ b/browser/components/BrowserContentHandler.jsm
@@ -240,21 +240,20 @@ function openBrowserWindow(cmdLine, trig
       });
       args = [uriArray];
     } else {
       // Always pass at least 3 arguments to avoid the "|"-splitting behavior,
       // ie. avoid the loadOneOrMoreURIs function.
       // Also, we need to pass the triggering principal.
       args = [urlOrUrlList,
               null, // charset
-              null, // referer
+              null, // refererInfo
               postData,
               undefined, // allowThirdPartyFixup; this would be `false` but that
                          // needs a conversion. Hopefully bug 1485961 will fix.
-              undefined, // referrer policy
               undefined, // user context id
               null, // origin principal
               triggeringPrincipal];
     }
   }
 
   if (cmdLine && cmdLine.state == Ci.nsICommandLine.STATE_INITIAL_LAUNCH) {
     let win = Services.wm.getMostRecentWindow("navigator:blank");
--- a/browser/components/about/AboutPrivateBrowsingHandler.jsm
+++ b/browser/components/about/AboutPrivateBrowsingHandler.jsm
@@ -5,30 +5,35 @@
 "use strict";
 
 var EXPORTED_SYMBOLS = ["AboutPrivateBrowsingHandler"];
 
 const {RemotePages} = ChromeUtils.import("resource://gre/modules/remotepagemanager/RemotePageManagerParent.jsm");
 const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 var AboutPrivateBrowsingHandler = {
+  _inited: false,
   _topics: [
     "DontShowIntroPanelAgain",
     "OpenPrivateWindow",
     "SearchHandoff",
   ],
 
   init() {
     this.pageListener = new RemotePages("about:privatebrowsing");
     for (let topic of this._topics) {
       this.pageListener.addMessageListener(topic, this.receiveMessage.bind(this));
     }
+    this._inited = true;
   },
 
   uninit() {
+    if (!this._inited) {
+      return;
+    }
     for (let topic of this._topics) {
       this.pageListener.removeMessageListener(topic);
     }
     this.pageListener.destroy();
   },
 
   receiveMessage(aMessage) {
     switch (aMessage.name) {
--- a/browser/components/contextualidentity/test/browser/browser_relatedTab.js
+++ b/browser/components/contextualidentity/test/browser/browser_relatedTab.js
@@ -1,31 +1,38 @@
 "use strict";
 
 /*
  * Bug 1325014 - Adding tab related to current tab inherits current tab's container usercontextid unless otherwise specified
  */
 
 add_task(async function() {
   let tab = BrowserTestUtils.addTab(gBrowser, "about:blank", {userContextId: 1});
+  let ReferrerInfo = Components.Constructor("@mozilla.org/referrer-info;1",
+                                            "nsIReferrerInfo",
+                                            "init");
 
   gBrowser.selectedTab = tab;
   let relatedTab = BrowserTestUtils.addTab(gBrowser, "about:blank", {relatedToCurrent: true});
   is(relatedTab.getAttribute("usercontextid"), 1, "Related tab (relatedToCurrent) inherits current tab's usercontextid");
   BrowserTestUtils.removeTab(relatedTab);
 
   gBrowser.selectedTab = tab;
   relatedTab = BrowserTestUtils.addTab(gBrowser, "about:blank", {relatedToCurrent: true, userContextId: 2});
   is(relatedTab.getAttribute("usercontextid"), 2, "Related tab (relatedToCurrent) with overridden usercontextid");
   BrowserTestUtils.removeTab(relatedTab);
 
   gBrowser.selectedTab = tab;
-  relatedTab = BrowserTestUtils.addTab(gBrowser, "about:blank", {referrerURI: gBrowser.currentURI});
+  let referrerInfo = new ReferrerInfo(
+    Ci.nsIHttpChannel.REFERRER_POLICY_UNSET, true, gBrowser.currentURI);
+  relatedTab = BrowserTestUtils.addTab(gBrowser, "about:blank", { referrerInfo });
   is(relatedTab.getAttribute("usercontextid"), 1, "Related tab (referrer) inherits current tab's usercontextid");
   BrowserTestUtils.removeTab(relatedTab);
 
   gBrowser.selectedTab = tab;
-  relatedTab = BrowserTestUtils.addTab(gBrowser, "about:blank", {referrerURI: gBrowser.currentURI, userContextId: 2});
+  referrerInfo = new ReferrerInfo(
+    Ci.nsIHttpChannel.REFERRER_POLICY_UNSET, true, gBrowser.currentURI);
+  relatedTab = BrowserTestUtils.addTab(gBrowser, "about:blank", { referrerInfo, userContextId: 2});
   is(relatedTab.getAttribute("usercontextid"), 2, "Related tab (referrer) with overridden usercontextid");
   BrowserTestUtils.removeTab(relatedTab);
 
   BrowserTestUtils.removeTab(tab);
 });
--- a/browser/components/contextualidentity/test/browser/browser_usercontextid_new_window.js
+++ b/browser/components/contextualidentity/test/browser/browser_usercontextid_new_window.js
@@ -19,17 +19,16 @@ function openWindowWithUserContextId(use
   let urlSupports = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
   urlSupports.data = TEST_URI;
   args.appendElement(urlSupports);
 
   args.appendElement(null);
   args.appendElement(null);
   args.appendElement(null);
   args.appendElement(null);
-  args.appendElement(null);
 
   let userContextIdSupports = Cc["@mozilla.org/supports-PRUint32;1"].createInstance(Ci.nsISupportsPRUint32);
   userContextIdSupports.data = userContextId;
   args.appendElement(userContextIdSupports);
 
   args.appendElement(Services.scriptSecurityManager.getSystemPrincipal());
   args.appendElement(Services.scriptSecurityManager.getSystemPrincipal());
 
--- a/browser/components/extensions/parent/ext-browser.js
+++ b/browser/components/extensions/parent/ext-browser.js
@@ -239,18 +239,18 @@ global.TabContext = class extends EventE
     windowTracker.removeListener("progress", this);
     windowTracker.removeListener("TabSelect", this);
     tabTracker.off("tab-adopted", this.tabAdopted);
   }
 };
 
 // This promise is used to wait for the search service to be initialized.
 // None of the code in the WebExtension modules requests that initialization.
-// It is assumed that it is started at some point. If tests start to fail
-// because this promise never resolves, that's likely the cause.
+// It is assumed that it is started at some point. That might never happen,
+// e.g. if the application shuts down before the search service initializes.
 XPCOMUtils.defineLazyGetter(global, "searchInitialized", () => {
   if (Services.search.isInitialized) {
     return Promise.resolve();
   }
   return ExtensionUtils.promiseObserved("browser-search-service", (_, data) => data == "init-complete");
 });
 
 class WindowTracker extends WindowTrackerBase {
--- a/browser/components/extensions/parent/ext-chrome-settings-overrides.js
+++ b/browser/components/extensions/parent/ext-chrome-settings-overrides.js
@@ -74,16 +74,23 @@ async function handleInitialHomepagePopu
     if (currentUrl == homepageUrl && gBrowser.selectedTab == tab) {
       homepagePopup.open();
       return;
     }
   }
   homepagePopup.addObserver(extensionId);
 }
 
+// When an extension starts up, a search engine may asynchronously be
+// registered, without blocking the startup. When an extension is
+// uninstalled, we need to wait for this registration to finish
+// before running the uninstallation handler.
+// Map[extension id -> Promise]
+var pendingSearchSetupTasks = new Map();
+
 this.chrome_settings_overrides = class extends ExtensionAPI {
   static async processDefaultSearchSetting(action, id) {
     await ExtensionSettingsStore.initialize();
     let item = ExtensionSettingsStore.getSetting(DEFAULT_SEARCH_STORE_TYPE, DEFAULT_SEARCH_SETTING_NAME);
     if (!item) {
       return;
     }
     if (Services.search.defaultEngine.name != item.value &&
@@ -126,17 +133,21 @@ this.chrome_settings_overrides = class e
 
   static removeSearchSettings(id) {
     return Promise.all([
       this.processDefaultSearchSetting("removeSetting", id),
       this.removeEngine(id),
     ]);
   }
 
-  static onUninstall(id) {
+  static async onUninstall(id) {
+    let searchStartupPromise = pendingSearchSetupTasks.get(id);
+    if (searchStartupPromise) {
+      await searchStartupPromise;
+    }
     // Note: We do not have to deal with homepage here as it is managed by
     // the ExtensionPreferencesManager.
     return Promise.all([
       this.removeSearchSettings(id),
       homepagePopup.clearConfirmation(id),
     ]);
   }
 
@@ -185,78 +196,100 @@ this.chrome_settings_overrides = class e
         close: () => {
           if (extension.shutdownReason == "ADDON_DISABLE") {
             homepagePopup.clearConfirmation(extension.id);
           }
         },
       });
     }
     if (manifest.chrome_settings_overrides.search_provider) {
-      await searchInitialized;
-      extension.callOnClose({
-        close: () => {
-          if (extension.shutdownReason == "ADDON_DISABLE") {
-            chrome_settings_overrides.processDefaultSearchSetting("disable", extension.id);
-            chrome_settings_overrides.removeEngine(extension.id);
+      // Registering a search engine can potentially take a long while,
+      // or not complete at all (when searchInitialized is never resolved),
+      // so we are deliberately not awaiting the returned promise here.
+      let searchStartupPromise =
+        this.processSearchProviderManifestEntry().finally(() => {
+          if (pendingSearchSetupTasks.get(extension.id) === searchStartupPromise) {
+            pendingSearchSetupTasks.delete(extension.id);
           }
-        },
-      });
+        });
+
+      // Save the promise so we can await at onUninstall.
+      pendingSearchSetupTasks.set(extension.id, searchStartupPromise);
+    }
+  }
+
+  async processSearchProviderManifestEntry() {
+    await searchInitialized;
 
-      let searchProvider = manifest.chrome_settings_overrides.search_provider;
-      let engineName = searchProvider.name.trim();
-      if (searchProvider.is_default) {
-        let engine = Services.search.getEngineByName(engineName);
-        let defaultEngines = await Services.search.getDefaultEngines();
-        if (engine && defaultEngines.some(defaultEngine => defaultEngine.name == engineName)) {
-          // Needs to be called every time to handle reenabling, but
-          // only sets default for install or enable.
-          await this.setDefault(engineName);
-          // For built in search engines, we don't do anything further
-          return;
+    let {extension} = this;
+    if (!extension) {
+      Cu.reportError(`Extension shut down before search provider was registered`);
+      return;
+    }
+    extension.callOnClose({
+      close: () => {
+        if (extension.shutdownReason == "ADDON_DISABLE") {
+          chrome_settings_overrides.processDefaultSearchSetting("disable", extension.id);
+          chrome_settings_overrides.removeEngine(extension.id);
         }
+      },
+    });
+
+    let {manifest} = extension;
+    let searchProvider = manifest.chrome_settings_overrides.search_provider;
+    let engineName = searchProvider.name.trim();
+    if (searchProvider.is_default) {
+      let engine = Services.search.getEngineByName(engineName);
+      let defaultEngines = await Services.search.getDefaultEngines();
+      if (engine && defaultEngines.some(defaultEngine => defaultEngine.name == engineName)) {
+        // Needs to be called every time to handle reenabling, but
+        // only sets default for install or enable.
+        await this.setDefault(engineName);
+        // For built in search engines, we don't do anything further
+        return;
       }
-      await this.addSearchEngine();
-      if (searchProvider.is_default) {
-        if (extension.startupReason === "ADDON_INSTALL") {
-          // Don't ask if it already the current engine
-          let engine = Services.search.getEngineByName(engineName);
-          let defaultEngine = await Services.search.getDefault();
-          if (defaultEngine.name != engine.name) {
-            let subject = {
-              wrappedJSObject: {
-                // This is a hack because we don't have the browser of
-                // the actual install. This means the popup might show
-                // in a different window. Will be addressed in a followup bug.
-                browser: windowTracker.topWindow.gBrowser.selectedBrowser,
-                name: this.extension.name,
-                icon: this.extension.iconURL,
-                currentEngine: defaultEngine.name,
-                newEngine: engineName,
-                respond(allow) {
-                  if (allow) {
-                    ExtensionSettingsStore.addSetting(
-                      extension.id, DEFAULT_SEARCH_STORE_TYPE, DEFAULT_SEARCH_SETTING_NAME, engineName, () => defaultEngine.name);
-                    Services.search.defaultEngine = Services.search.getEngineByName(engineName);
-                  }
-                },
+    }
+    await this.addSearchEngine();
+    if (searchProvider.is_default) {
+      if (extension.startupReason === "ADDON_INSTALL") {
+        // Don't ask if it already the current engine
+        let engine = Services.search.getEngineByName(engineName);
+        let defaultEngine = await Services.search.getDefault();
+        if (defaultEngine.name != engine.name) {
+          let subject = {
+            wrappedJSObject: {
+              // This is a hack because we don't have the browser of
+              // the actual install. This means the popup might show
+              // in a different window. Will be addressed in a followup bug.
+              browser: windowTracker.topWindow.gBrowser.selectedBrowser,
+              name: this.extension.name,
+              icon: this.extension.iconURL,
+              currentEngine: defaultEngine.name,
+              newEngine: engineName,
+              respond(allow) {
+                if (allow) {
+                  ExtensionSettingsStore.addSetting(
+                    extension.id, DEFAULT_SEARCH_STORE_TYPE, DEFAULT_SEARCH_SETTING_NAME, engineName, () => defaultEngine.name);
+                  Services.search.defaultEngine = Services.search.getEngineByName(engineName);
+                }
               },
-            };
-            Services.obs.notifyObservers(subject, "webextension-defaultsearch-prompt");
-          }
-        } else {
-          // Needs to be called every time to handle reenabling, but
-          // only sets default for install or enable.
-          this.setDefault(engineName);
+            },
+          };
+          Services.obs.notifyObservers(subject, "webextension-defaultsearch-prompt");
         }
-      } else if (ExtensionSettingsStore.hasSetting(extension.id,
-                                                   DEFAULT_SEARCH_STORE_TYPE,
-                                                   DEFAULT_SEARCH_SETTING_NAME)) {
-        // is_default has been removed, but we still have a setting. Remove it.
-        chrome_settings_overrides.processDefaultSearchSetting("removeSetting", extension.id);
+      } else {
+        // Needs to be called every time to handle reenabling, but
+        // only sets default for install or enable.
+        this.setDefault(engineName);
       }
+    } else if (ExtensionSettingsStore.hasSetting(extension.id,
+                                                 DEFAULT_SEARCH_STORE_TYPE,
+                                                 DEFAULT_SEARCH_SETTING_NAME)) {
+      // is_default has been removed, but we still have a setting. Remove it.
+      chrome_settings_overrides.processDefaultSearchSetting("removeSetting", extension.id);
     }
   }
 
   async setDefault(engineName) {
     let {extension} = this;
     if (extension.startupReason === "ADDON_INSTALL") {
       let defaultEngine = await Services.search.getDefault();
       let item = await ExtensionSettingsStore.addSetting(
--- a/browser/components/extensions/parent/ext-windows.js
+++ b/browser/components/extensions/parent/ext-windows.js
@@ -199,20 +199,19 @@ this.windows = class extends ExtensionAP
                 !context.checkLoadURL(url, {dontReportErrors: true})) {
               // The extension principal cannot directly load about:-URLs,
               // except for about:blank. So use the system principal instead.
               principal = Services.scriptSecurityManager.getSystemPrincipal();
             }
           }
 
           args.appendElement(null); // unused
-          args.appendElement(null); // referrer
+          args.appendElement(null); // referrerInfo
           args.appendElement(null); // postData
           args.appendElement(null); // allowThirdPartyFixup
-          args.appendElement(null); // referrerPolicy
 
           if (createData.cookieStoreId) {
             let userContextIdSupports = Cc["@mozilla.org/supports-PRUint32;1"].createInstance(Ci.nsISupportsPRUint32);
             // May throw if validation fails.
             userContextIdSupports.data = getUserContextIdForCookieStoreId(extension, createData.cookieStoreId, createData.incognito);
             args.appendElement(userContextIdSupports); // userContextId
           } else {
             args.appendElement(null);
--- a/browser/components/extensions/test/browser/browser_ext_search.js
+++ b/browser/components/extensions/test/browser/browser_ext_search.js
@@ -1,15 +1,19 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
+const {AddonTestUtils} = ChromeUtils.import("resource://testing-common/AddonTestUtils.jsm");
+
 const SEARCH_TERM = "test";
 const SEARCH_URL = "https://localhost/?q={searchTerms}";
 
+AddonTestUtils.initMochitest(this);
+
 add_task(async function test_search() {
   async function background(SEARCH_TERM) {
     function awaitSearchResult() {
       return new Promise(resolve => {
         async function listener(tabId, info, changedTab) {
           if (changedTab.url == "about:blank") {
             // Ignore events related to the initial tab open.
             return;
@@ -52,16 +56,17 @@ add_task(async function test_search() {
           "search_url": SEARCH_URL,
         },
       },
     },
     background: `(${background})("${SEARCH_TERM}")`,
     useAddonManager: "temporary",
   });
   await extension.startup();
+  await AddonTestUtils.waitForSearchProviderStartup(extension);
 
   let addonEngines = await extension.awaitMessage("engines");
   let engines = (await Services.search.getEngines()).filter(engine => !engine.hidden);
   is(addonEngines.length, engines.length, "Engine lengths are the same.");
   let defaultEngine = addonEngines.filter(engine => engine.isDefault === true);
   is(defaultEngine.length, 1, "One default engine");
   is(defaultEngine[0].name, (await Services.search.getDefault()).name, "Default engine is correct");
 
--- a/browser/components/extensions/test/browser/browser_ext_settings_overrides_default_search.js
+++ b/browser/components/extensions/test/browser/browser_ext_settings_overrides_default_search.js
@@ -1,19 +1,23 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 
 "use strict";
 
 ChromeUtils.defineModuleGetter(this, "AddonManager",
                                "resource://gre/modules/AddonManager.jsm");
 
+const {AddonTestUtils} = ChromeUtils.import("resource://testing-common/AddonTestUtils.jsm");
+
 const EXTENSION1_ID = "extension1@mozilla.com";
 const EXTENSION2_ID = "extension2@mozilla.com";
 
+AddonTestUtils.initMochitest(this);
+
 var defaultEngineName;
 
 async function restoreDefaultEngine() {
   let engine = Services.search.getEngineByName(defaultEngineName);
   await Services.search.setDefault(engine);
 }
 
 add_task(async function setup() {
@@ -32,16 +36,17 @@ add_task(async function test_extension_s
           "is_default": true,
         },
       },
     },
     useAddonManager: "temporary",
   });
 
   await ext1.startup();
+  await AddonTestUtils.waitForSearchProviderStartup(ext1);
 
   is((await Services.search.getDefault()).name, "DuckDuckGo", "Default engine is DuckDuckGo");
 
   await ext1.unload();
 
   is((await Services.search.getDefault()).name, defaultEngineName, `Default engine is ${defaultEngineName}`);
 });
 
@@ -145,20 +150,22 @@ add_task(async function test_extension_s
           "is_default": true,
         },
       },
     },
     useAddonManager: "temporary",
   });
 
   await ext1.startup();
+  await AddonTestUtils.waitForSearchProviderStartup(ext1);
 
   is((await Services.search.getDefault()).name, "DuckDuckGo", "Default engine is DuckDuckGo");
 
   await ext2.startup();
+  await AddonTestUtils.waitForSearchProviderStartup(ext2);
 
   is((await Services.search.getDefault()).name, "Twitter", "Default engine is Twitter");
 
   await ext2.unload();
 
   is((await Services.search.getDefault()).name, "DuckDuckGo", "Default engine is DuckDuckGo");
 
   await ext1.unload();
@@ -191,20 +198,22 @@ add_task(async function test_extension_s
           "is_default": true,
         },
       },
     },
     useAddonManager: "temporary",
   });
 
   await ext1.startup();
+  await AddonTestUtils.waitForSearchProviderStartup(ext1);
 
   is((await Services.search.getDefault()).name, "DuckDuckGo", "Default engine is DuckDuckGo");
 
   await ext2.startup();
+  await AddonTestUtils.waitForSearchProviderStartup(ext2);
 
   is((await Services.search.getDefault()).name, "Twitter", "Default engine is Twitter");
 
   await ext1.unload();
 
   is((await Services.search.getDefault()).name, "Twitter", "Default engine is Twitter");
 
   await ext2.unload();
@@ -224,16 +233,17 @@ add_task(async function test_user_changi
           "is_default": true,
         },
       },
     },
     useAddonManager: "temporary",
   });
 
   await ext1.startup();
+  await AddonTestUtils.waitForSearchProviderStartup(ext1);
 
   is((await Services.search.getDefault()).name, "DuckDuckGo", "Default engine is DuckDuckGo");
 
   let engine = Services.search.getEngineByName("Twitter");
   await Services.search.setDefault(engine);
 
   await ext1.unload();
 
@@ -258,16 +268,17 @@ add_task(async function test_user_change
           "is_default": true,
         },
       },
     },
     useAddonManager: "temporary",
   });
 
   await ext1.startup();
+  await AddonTestUtils.waitForSearchProviderStartup(ext1);
 
   is((await Services.search.getDefault()).name, "DuckDuckGo", "Default engine is DuckDuckGo");
 
   let engine = Services.search.getEngineByName("Twitter");
   await Services.search.setDefault(engine);
 
   is((await Services.search.getDefault()).name, "Twitter", "Default engine is Twitter");
 
@@ -323,27 +334,29 @@ add_task(async function test_two_addons_
           "is_default": true,
         },
       },
     },
     useAddonManager: "temporary",
   });
 
   await ext1.startup();
+  await AddonTestUtils.waitForSearchProviderStartup(ext1);
 
   is((await Services.search.getDefault()).name, "DuckDuckGo", "Default engine is DuckDuckGo");
 
   let disabledPromise = awaitEvent("shutdown", EXTENSION1_ID);
   let addon1 = await AddonManager.getAddonByID(EXTENSION1_ID);
   await addon1.disable();
   await disabledPromise;
 
   is((await Services.search.getDefault()).name, defaultEngineName, `Default engine is ${defaultEngineName}`);
 
   await ext2.startup();
+  await AddonTestUtils.waitForSearchProviderStartup(ext2);
 
   is((await Services.search.getDefault()).name, "Twitter", "Default engine is Twitter");
 
   let enabledPromise = awaitEvent("ready", EXTENSION1_ID);
   await addon1.enable();
   await enabledPromise;
 
   is((await Services.search.getDefault()).name, "Twitter", "Default engine is Twitter");
@@ -391,20 +404,22 @@ add_task(async function test_two_addons_
           "is_default": true,
         },
       },
     },
     useAddonManager: "temporary",
   });
 
   await ext1.startup();
+  await AddonTestUtils.waitForSearchProviderStartup(ext1);
 
   is((await Services.search.getDefault()).name, "DuckDuckGo", "Default engine is DuckDuckGo");
 
   await ext2.startup();
+  await AddonTestUtils.waitForSearchProviderStartup(ext2);
 
   is((await Services.search.getDefault()).name, "Twitter", "Default engine is Twitter");
 
   let disabledPromise = awaitEvent("shutdown", EXTENSION1_ID);
   let addon1 = await AddonManager.getAddonByID(EXTENSION1_ID);
   await addon1.disable();
   await disabledPromise;
 
@@ -459,20 +474,22 @@ add_task(async function test_two_addons_
           "is_default": true,
         },
       },
     },
     useAddonManager: "temporary",
   });
 
   await ext1.startup();
+  await AddonTestUtils.waitForSearchProviderStartup(ext1);
 
   is((await Services.search.getDefault()).name, "DuckDuckGo", "Default engine is DuckDuckGo");
 
   await ext2.startup();
+  await AddonTestUtils.waitForSearchProviderStartup(ext2);
 
   is((await Services.search.getDefault()).name, "Twitter", "Default engine is Twitter");
 
   let disabledPromise = awaitEvent("shutdown", EXTENSION2_ID);
   let addon2 = await AddonManager.getAddonByID(EXTENSION2_ID);
   await addon2.disable();
   await disabledPromise;
 
--- a/browser/components/extensions/test/xpcshell/test_ext_chrome_settings_overrides_update.js
+++ b/browser/components/extensions/test/xpcshell/test_ext_chrome_settings_overrides_update.js
@@ -60,16 +60,17 @@ add_task(async function test_overrides_u
   };
   let extension = ExtensionTestUtils.loadExtension(extensionInfo);
 
   let defaultHomepageURL = HomePage.get();
   let defaultEngineName = (await Services.search.getDefault()).name;
 
   let prefPromise = promisePrefChanged(HOMEPAGE_URI);
   await extension.startup();
+  await AddonTestUtils.waitForSearchProviderStartup(extension);
   await prefPromise;
 
   equal(extension.version, "1.0", "The installed addon has the expected version.");
   ok(HomePage.get().endsWith(HOMEPAGE_URI),
      "Home page url is overridden by the extension.");
   equal((await Services.search.getDefault()).name,
         "DuckDuckGo",
         "Default engine is overridden by the extension");
--- a/browser/components/extensions/test/xpcshell/test_ext_settings_overrides_search.js
+++ b/browser/components/extensions/test/xpcshell/test_ext_settings_overrides_search.js
@@ -37,16 +37,17 @@ add_task(async function test_extension_a
           "suggest_url": kSearchSuggestURL,
         },
       },
     },
     useAddonManager: "temporary",
   });
 
   await ext1.startup();
+  await AddonTestUtils.waitForSearchProviderStartup(ext1);
 
   let engine = Services.search.getEngineByName("MozSearch");
   ok(engine, "Engine should exist.");
 
   let {baseURI} = ext1.extension;
   equal(engine.iconURI.spec, baseURI.resolve("foo.ico"), "icon path matches");
   let icons = engine.getIcons();
   equal(icons.length, 2, "both icons avialable");
@@ -78,16 +79,17 @@ add_task(async function test_extension_a
           "search_url": "https://example.com/?q={searchTerms}",
         },
       },
     },
     useAddonManager: "temporary",
   });
 
   await ext1.startup();
+  await AddonTestUtils.waitForSearchProviderStartup(ext1);
 
   let engine = Services.search.getEngineByName("MozSearch");
   ok(engine, "Engine should exist.");
 
   await ext1.unload();
   await delay();
 
   engine = Services.search.getEngineByName("MozSearch");
@@ -111,16 +113,17 @@ add_task(async function test_upgrade_def
         },
       },
       "version": "0.1",
     },
     useAddonManager: "temporary",
   });
 
   await ext1.startup();
+  await AddonTestUtils.waitForSearchProviderStartup(ext1);
 
   let engine = Services.search.getEngineByName("MozSearch");
   await Services.search.setDefault(engine);
   await Services.search.moveEngine(engine, 1);
 
   await ext1.upgrade({
     manifest: {
       "chrome_settings_overrides": {
@@ -134,16 +137,17 @@ add_task(async function test_upgrade_def
         "gecko": {
           "id": "testengine@mozilla.com",
         },
       },
       "version": "0.2",
     },
     useAddonManager: "temporary",
   });
+  await AddonTestUtils.waitForSearchProviderStartup(ext1);
 
   engine = Services.search.getEngineByName("MozSearch");
   equal(Services.search.defaultEngine, engine, "Default engine should still be MozSearch");
   equal((await Services.search.getEngines()).map(e => e.name).indexOf(engine.name),
         1, "Engine is in position 1");
 
   await ext1.unload();
   await delay();
@@ -165,16 +169,17 @@ add_task(async function test_extension_p
           "suggest_url_post_params": "foo=bar&bar=foo",
         },
       },
     },
     useAddonManager: "temporary",
   });
 
   await ext1.startup();
+  await AddonTestUtils.waitForSearchProviderStartup(ext1);
 
   let engine = Services.search.getEngineByName("MozSearch");
   ok(engine, "Engine should exist.");
 
   let url = engine.wrappedJSObject._getURLOfType("text/html");
   equal(url.method, "POST", "Search URLs method is POST");
 
   let expectedURL = kSearchEngineURL.replace("{searchTerms}", kSearchTerm);
--- a/browser/components/extensions/test/xpcshell/test_ext_settings_overrides_search_mozParam.js
+++ b/browser/components/extensions/test/xpcshell/test_ext_settings_overrides_search_mozParam.js
@@ -55,16 +55,17 @@ add_task(async function test_extension_s
           "search_url": "https://example.com/?q={searchTerms}",
           "params": [...mozParams, ...params],
         },
       },
     },
     useAddonManager: "permanent",
   });
   await extension.startup();
+  await AddonTestUtils.waitForSearchProviderStartup(extension);
   equal(extension.extension.isPrivileged, true, "extension is priviledged");
 
   let engine = Services.search.getEngineByName("MozParamsTest");
 
   let extraParams = [];
   for (let p of params) {
     if (p.condition == "pref") {
       extraParams.push(`${p.name}=good`);
@@ -108,14 +109,15 @@ add_task(async function test_extension_s
             {name: "q", value: "{searchTerms}"},
           ],
         },
       },
     },
     useAddonManager: "permanent",
   });
   await extension.startup();
+  await AddonTestUtils.waitForSearchProviderStartup(extension);
   equal(extension.extension.isPrivileged, false, "extension is not priviledged");
   let engine = Services.search.getEngineByName("MozParamsTest");
   let expectedURL = engine.getSubmission("test", null, "contextmenu").uri.spec;
   equal(expectedURL, "https://example.com/?q=test", "engine cannot have conditional or pref params");
   await extension.unload();
 });
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/xpcshell/test_ext_settings_overrides_shutdown.js
@@ -0,0 +1,95 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+
+"use strict";
+
+const {AddonTestUtils} = ChromeUtils.import("resource://testing-common/AddonTestUtils.jsm");
+// Lazily import ExtensionParent to allow AddonTestUtils.createAppInfo to
+// override Services.appinfo.
+ChromeUtils.defineModuleGetter(this, "ExtensionParent",
+                               "resource://gre/modules/ExtensionParent.jsm");
+
+AddonTestUtils.init(this);
+AddonTestUtils.overrideCertDB();
+AddonTestUtils.createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "42", "42");
+
+add_task(async function shutdown_during_search_provider_startup() {
+  await AddonTestUtils.promiseStartupManager();
+
+  let extension = ExtensionTestUtils.loadExtension({
+    useAddonManager: "permanent",
+    manifest: {
+      chrome_settings_overrides: {
+        search_provider: {
+          name: "dummy name",
+          search_url: "https://example.com/",
+        },
+      },
+    },
+  });
+
+  info("Starting up search extension");
+  await extension.startup();
+  let extStartPromise = AddonTestUtils.waitForSearchProviderStartup(extension, {
+    // Search provider registration is expected to be pending because the search
+    // service has not been initialized yet.
+    expectPending: true,
+  });
+
+  let initialized = false;
+  ExtensionParent.apiManager.global.searchInitialized.then(() => {
+    initialized = true;
+  });
+
+  await extension.addon.disable();
+
+  info("Extension managed to shut down despite the uninitialized search");
+  // Initialize search after extension shutdown to check that it does not cause
+  // any problems, and that the test can continue to test uninstall behavior.
+  Assert.ok(!initialized, "Search service should not have been initialized");
+
+  extension.addon.enable();
+  await extension.awaitStartup();
+
+  // Check that uninstall is blocked until the search registration at startup
+  // has finished. This registration only finished once the search service is
+  // initialized.
+  let uninstallingPromise = new Promise(resolve => {
+    let Management = ExtensionParent.apiManager;
+    Management.on("uninstall", function listener(eventName, {id}) {
+      Management.off("uninstall", listener);
+      Assert.equal(id, extension.id, "Expected extension");
+      resolve();
+    });
+  });
+
+  let extRestartPromise = AddonTestUtils.waitForSearchProviderStartup(extension, {
+    // Search provider registration is expected to be pending again,
+    // because the search service has still not been initialized yet.
+    expectPending: true,
+  });
+
+  let uninstalledPromise = extension.addon.uninstall();
+  let uninstalled = false;
+  uninstalledPromise.then(() => { uninstalled = true; });
+
+  await uninstallingPromise;
+  Assert.ok(!uninstalled, "Uninstall should not be finished yet");
+  Assert.ok(!initialized, "Search service should still be uninitialized");
+  await Services.search.init();
+  Assert.ok(initialized, "Search service should be initialized");
+
+  // After initializing the search service, the search provider registration
+  // promises should settle eventually.
+
+  // Despite the interrupted startup, the promise should still resolve without
+  // an error.
+  await extStartPromise;
+  // The extension that is still active. The promise should just resolve.
+  await extRestartPromise;
+
+  // After initializing the search service, uninstall should eventually finish.
+  await uninstalledPromise;
+
+  await AddonTestUtils.promiseShutdownManager();
+});
--- a/browser/components/extensions/test/xpcshell/xpcshell-common.ini
+++ b/browser/components/extensions/test/xpcshell/xpcshell-common.ini
@@ -5,11 +5,12 @@
 [test_ext_browsingData_passwords.js]
 [test_ext_browsingData_settings.js]
 [test_ext_chrome_settings_overrides_update.js]
 [test_ext_distribution_popup.js]
 [test_ext_geckoProfiler_control.js]
 [test_ext_history.js]
 [test_ext_settings_overrides_search.js]
 [test_ext_settings_overrides_search_mozParam.js]
+[test_ext_settings_overrides_shutdown.js]
 [test_ext_url_overrides_newtab.js]
 [test_ext_url_overrides_newtab_update.js]
 
--- a/browser/components/newtab/common/Reducers.jsm
+++ b/browser/components/newtab/common/Reducers.jsm
@@ -479,16 +479,44 @@ function DiscoveryStream(prevState = INI
     case at.DISCOVERY_STREAM_SPOCS_ENDPOINT:
       return {
         ...prevState,
         spocs: {
           ...INITIAL_STATE.DiscoveryStream.spocs,
           spocs_endpoint: action.data || INITIAL_STATE.DiscoveryStream.spocs.spocs_endpoint,
         },
       };
+    case at.PLACES_LINK_BLOCKED:
+      // Return if action data is empty, or spocs or feeds data is not loaded
+      if (!action.data || !prevState.spocs.loaded || !prevState.feeds.loaded) {
+        return prevState;
+      }
+      // Filter spocs and recommendations data inside feeds by removing action.data.url
+      // received on PLACES_LINK_BLOCKED triggered by dismiss link menu option
+      return {
+        ...prevState,
+        spocs: {
+          ...prevState.spocs,
+          data: prevState.spocs.data.spocs ? {
+            spocs: prevState.spocs.data.spocs.filter(s => s.url !== action.data.url),
+          } : {},
+        },
+        feeds: {
+          ...prevState.feeds,
+          data: Object.keys(prevState.feeds.data).reduce((accumulator, feed_url) => {
+            accumulator[feed_url] = {
+              data: {
+                ...prevState.feeds.data[feed_url].data,
+                recommendations: prevState.feeds.data[feed_url].data.recommendations.filter(r => r.url !== action.data.url),
+              },
+            };
+            return accumulator;
+          }, {}),
+        },
+      };
     case at.DISCOVERY_STREAM_SPOCS_UPDATE:
       if (action.data) {
         return {
           ...prevState,
           spocs: {
             ...prevState.spocs,
             lastUpdated: action.data.lastUpdated,
             data: action.data.spocs,
--- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSCard/DSCard.jsx
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSCard/DSCard.jsx
@@ -55,17 +55,18 @@ export class DSCard extends React.PureCo
           </div>
           <ImpressionStats
             campaignId={this.props.campaignId}
             rows={[{id: this.props.id, pos: this.props.pos}]}
             dispatch={this.props.dispatch}
             source={this.props.type} />
         </SafeAnchor>
         <DSLinkMenu
-          index={this.props.index}
+          id={this.props.id}
+          index={this.props.pos}
           dispatch={this.props.dispatch}
           intl={this.props.intl}
           url={this.props.url}
           title={this.props.title}
           source={this.props.source}
           type={this.props.type} />
       </div>
     );
--- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSCard/_DSCard.scss
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSCard/_DSCard.scss
@@ -16,16 +16,20 @@
       }
 
       color: $blue-60;
     }
   }
 
   &:active {
     header {
+      @include dark-theme-only {
+        color: $blue-50;
+      }
+
       color: $blue-70;
     }
   }
 
   .img-wrapper {
     width: 100%;
   }
 
--- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSLinkMenu/DSLinkMenu.jsx
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSLinkMenu/DSLinkMenu.jsx
@@ -37,41 +37,43 @@ export class _DSLinkMenu extends React.P
       dsLinkMenuHostDiv.parentElement.classList.add("last-item");
     }
     dsLinkMenuHostDiv.parentElement.classList.add("active");
   }
 
   render() {
     const {index, dispatch} = this.props;
     const isContextMenuOpen = this.state.showContextMenu && this.state.activeCard === index;
-    const TOP_STORIES_SOURCE = "TOP_STORIES";
-    const TOP_STORIES_CONTEXT_MENU_OPTIONS = ["OpenInNewWindow", "OpenInPrivateWindow"];
+    const TOP_STORIES_CONTEXT_MENU_OPTIONS = ["OpenInNewWindow", "OpenInPrivateWindow", "Separator", "BlockUrl"];
     const title = this.props.title || this.props.source;
+    const type = this.props.type || "DISCOVERY_STREAM";
 
     return (<div>
       <button ref={this.contextMenuButtonRef}
               className="context-menu-button icon"
               title={this.props.intl.formatMessage({id: "context_menu_title"})}
               onClick={this.onMenuButtonClick}>
         <span className="sr-only">
           <FormattedMessage id="context_menu_button_sr" values={{title}} />
         </span>
       </button>
       {isContextMenuOpen &&
         <LinkMenu
           dispatch={dispatch}
           index={index}
-          source={TOP_STORIES_SOURCE}
+          source={type.toUpperCase()}
           onUpdate={this.onMenuUpdate}
           onShow={this.onMenuShow}
           options={TOP_STORIES_CONTEXT_MENU_OPTIONS}
+          shouldSendImpressionStats={true}
           site={{
             referrer: "https://getpocket.com/recommendations",
             title: this.props.title,
             type: this.props.type,
             url: this.props.url,
+            guid: this.props.id,
           }} />
       }
     </div>);
   }
 }
 
 export const DSLinkMenu = injectIntl(_DSLinkMenu);
--- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/Hero/Hero.jsx
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/Hero/Hero.jsx
@@ -27,17 +27,17 @@ export class Hero extends React.PureComp
       }));
     }
   }
 
   render() {
     const {data} = this.props;
 
     // Handle a render before feed has been fetched by displaying nothing
-    if (!data || !data.recommendations) {
+    if (!data || !data.recommendations || !data.recommendations.length) {
       return (
         <div />
       );
     }
 
     let [heroRec, ...otherRecs] = data.recommendations.slice(0, this.props.items);
     this.heroRec = heroRec;
 
@@ -92,17 +92,18 @@ export class Hero extends React.PureComp
               </div>
               <ImpressionStats
                 campaignId={heroRec.campaignId}
                 rows={[{id: heroRec.id, pos: heroRec.pos}]}
                 dispatch={this.props.dispatch}
                 source={this.props.type} />
             </SafeAnchor>
             <DSLinkMenu
-              index={this.props.index}
+              id={heroRec.id}
+              index={heroRec.pos}
               dispatch={this.props.dispatch}
               intl={this.props.intl}
               url={heroRec.url}
               title={heroRec.title}
               source={heroRec.domain}
               type={this.props.type} />
           </div>
           <div className={`${this.props.subComponentType}`}>
--- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/Hero/_Hero.scss
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/Hero/_Hero.scss
@@ -167,16 +167,17 @@
         padding-top: 100%; // 1:1 aspect ratio
       }
     }
 
     .cards {
       display: grid;
       grid-template-columns: repeat(2, 1fr);
       grid-column-gap: 24px;
+      grid-auto-rows: min-content;
     }
   }
 
   // "Full width layout"
   .ds-column-9 &,
   .ds-column-10 &,
   .ds-column-11 &,
   .ds-column-12 & {
@@ -226,16 +227,17 @@
         }
       }
     }
 
     .cards {
       display: grid;
       grid-template-columns: repeat(2, 1fr);
       grid-column-gap: 24px;
+      grid-auto-rows: min-content;
 
       .ds-card {
         &:hover {
           @include dark-theme-only {
             background: none;
 
             .title {
               color: $blue-40;
--- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/List/List.jsx
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/List/List.jsx
@@ -58,17 +58,18 @@ export class ListItem extends React.Pure
           <div className="ds-list-image" style={{backgroundImage: `url(${this.props.image_src})`}} />
           <ImpressionStats
             campaignId={this.props.campaignId}
             rows={[{id: this.props.id, pos: this.props.pos}]}
             dispatch={this.props.dispatch}
             source={this.props.type} />
         </SafeAnchor>
         <DSLinkMenu
-          index={this.props.index}
+          id={this.props.id}
+          index={this.props.pos}
           dispatch={this.props.dispatch}
           intl={this.props.intl}
           url={this.props.url}
           title={this.props.title}
           source={this.props.source}
           type={this.props.type} />
       </li>
     );
--- a/browser/components/newtab/css/activity-stream-linux.css
+++ b/browser/components/newtab/css/activity-stream-linux.css
@@ -2060,17 +2060,18 @@ main {
       height: 0;
       padding-top: 100%; }
   .ds-column-5 .ds-hero .cards,
   .ds-column-6 .ds-hero .cards,
   .ds-column-7 .ds-hero .cards,
   .ds-column-8 .ds-hero .cards {
     display: grid;
     grid-template-columns: repeat(2, 1fr);
-    grid-column-gap: 24px; }
+    grid-column-gap: 24px;
+    grid-auto-rows: min-content; }
   .ds-column-9 .ds-hero,
   .ds-column-10 .ds-hero,
   .ds-column-11 .ds-hero,
   .ds-column-12 .ds-hero {
     display: grid;
     grid-template-columns: repeat(2, 1fr);
     grid-column-gap: 24px; }
     .ds-column-9 .ds-hero.ds-hero-border,
@@ -2138,17 +2139,18 @@ main {
         .ds-column-12 .ds-hero .wrapper .meta .source {
           margin-bottom: 0; }
     .ds-column-9 .ds-hero .cards,
     .ds-column-10 .ds-hero .cards,
     .ds-column-11 .ds-hero .cards,
     .ds-column-12 .ds-hero .cards {
       display: grid;
       grid-template-columns: repeat(2, 1fr);
-      grid-column-gap: 24px; }
+      grid-column-gap: 24px;
+      grid-auto-rows: min-content; }
       [lwt-newtab-brighttext]:not(.force-light-theme) .ds-column-9 .ds-hero .cards .ds-card:hover, [lwt-newtab-brighttext]:not(.force-light-theme)
       .ds-column-10 .ds-hero .cards .ds-card:hover, [lwt-newtab-brighttext]:not(.force-light-theme)
       .ds-column-11 .ds-hero .cards .ds-card:hover, [lwt-newtab-brighttext]:not(.force-light-theme)
       .ds-column-12 .ds-hero .cards .ds-card:hover {
         background: none; }
         [lwt-newtab-brighttext]:not(.force-light-theme) .ds-column-9 .ds-hero .cards .ds-card:hover .title, [lwt-newtab-brighttext]:not(.force-light-theme)
         .ds-column-10 .ds-hero .cards .ds-card:hover .title, [lwt-newtab-brighttext]:not(.force-light-theme)
         .ds-column-11 .ds-hero .cards .ds-card:hover .title, [lwt-newtab-brighttext]:not(.force-light-theme)
@@ -2555,16 +2557,18 @@ main {
   flex-direction: column;
   position: relative; }
   .ds-card:hover header {
     color: #0060DF; }
     [lwt-newtab-brighttext]:not(.force-light-theme) .ds-card:hover header {
       color: #45A1FF; }
   .ds-card:active header {
     color: #003EAA; }
+    [lwt-newtab-brighttext]:not(.force-light-theme) .ds-card:active header {
+      color: #0A84FF; }
   .ds-card .img-wrapper {
     width: 100%; }
   .ds-card .img {
     background-color: var(--newtab-card-placeholder-color);
     background-position: center;
     background-repeat: no-repeat;
     background-size: cover;
     border-radius: 4px;
--- a/browser/components/newtab/css/activity-stream-mac.css
+++ b/browser/components/newtab/css/activity-stream-mac.css
@@ -2063,17 +2063,18 @@ main {
       height: 0;
       padding-top: 100%; }
   .ds-column-5 .ds-hero .cards,
   .ds-column-6 .ds-hero .cards,
   .ds-column-7 .ds-hero .cards,
   .ds-column-8 .ds-hero .cards {
     display: grid;
     grid-template-columns: repeat(2, 1fr);
-    grid-column-gap: 24px; }
+    grid-column-gap: 24px;
+    grid-auto-rows: min-content; }
   .ds-column-9 .ds-hero,
   .ds-column-10 .ds-hero,
   .ds-column-11 .ds-hero,
   .ds-column-12 .ds-hero {
     display: grid;
     grid-template-columns: repeat(2, 1fr);
     grid-column-gap: 24px; }
     .ds-column-9 .ds-hero.ds-hero-border,
@@ -2141,17 +2142,18 @@ main {
         .ds-column-12 .ds-hero .wrapper .meta .source {
           margin-bottom: 0; }
     .ds-column-9 .ds-hero .cards,
     .ds-column-10 .ds-hero .cards,
     .ds-column-11 .ds-hero .cards,
     .ds-column-12 .ds-hero .cards {
       display: grid;
       grid-template-columns: repeat(2, 1fr);
-      grid-column-gap: 24px; }
+      grid-column-gap: 24px;
+      grid-auto-rows: min-content; }
       [lwt-newtab-brighttext]:not(.force-light-theme) .ds-column-9 .ds-hero .cards .ds-card:hover, [lwt-newtab-brighttext]:not(.force-light-theme)
       .ds-column-10 .ds-hero .cards .ds-card:hover, [lwt-newtab-brighttext]:not(.force-light-theme)
       .ds-column-11 .ds-hero .cards .ds-card:hover, [lwt-newtab-brighttext]:not(.force-light-theme)
       .ds-column-12 .ds-hero .cards .ds-card:hover {
         background: none; }
         [lwt-newtab-brighttext]:not(.force-light-theme) .ds-column-9 .ds-hero .cards .ds-card:hover .title, [lwt-newtab-brighttext]:not(.force-light-theme)
         .ds-column-10 .ds-hero .cards .ds-card:hover .title, [lwt-newtab-brighttext]:not(.force-light-theme)
         .ds-column-11 .ds-hero .cards .ds-card:hover .title, [lwt-newtab-brighttext]:not(.force-light-theme)
@@ -2558,16 +2560,18 @@ main {
   flex-direction: column;
   position: relative; }
   .ds-card:hover header {
     color: #0060DF; }
     [lwt-newtab-brighttext]:not(.force-light-theme) .ds-card:hover header {
       color: #45A1FF; }
   .ds-card:active header {
     color: #003EAA; }
+    [lwt-newtab-brighttext]:not(.force-light-theme) .ds-card:active header {
+      color: #0A84FF; }
   .ds-card .img-wrapper {
     width: 100%; }
   .ds-card .img {
     background-color: var(--newtab-card-placeholder-color);
     background-position: center;
     background-repeat: no-repeat;
     background-size: cover;
     border-radius: 4px;
--- a/browser/components/newtab/css/activity-stream-windows.css
+++ b/browser/components/newtab/css/activity-stream-windows.css
@@ -2060,17 +2060,18 @@ main {
       height: 0;
       padding-top: 100%; }
   .ds-column-5 .ds-hero .cards,
   .ds-column-6 .ds-hero .cards,
   .ds-column-7 .ds-hero .cards,
   .ds-column-8 .ds-hero .cards {
     display: grid;
     grid-template-columns: repeat(2, 1fr);
-    grid-column-gap: 24px; }
+    grid-column-gap: 24px;
+    grid-auto-rows: min-content; }
   .ds-column-9 .ds-hero,
   .ds-column-10 .ds-hero,
   .ds-column-11 .ds-hero,
   .ds-column-12 .ds-hero {
     display: grid;
     grid-template-columns: repeat(2, 1fr);
     grid-column-gap: 24px; }
     .ds-column-9 .ds-hero.ds-hero-border,
@@ -2138,17 +2139,18 @@ main {
         .ds-column-12 .ds-hero .wrapper .meta .source {
           margin-bottom: 0; }
     .ds-column-9 .ds-hero .cards,
     .ds-column-10 .ds-hero .cards,
     .ds-column-11 .ds-hero .cards,
     .ds-column-12 .ds-hero .cards {
       display: grid;
       grid-template-columns: repeat(2, 1fr);
-      grid-column-gap: 24px; }
+      grid-column-gap: 24px;
+      grid-auto-rows: min-content; }
       [lwt-newtab-brighttext]:not(.force-light-theme) .ds-column-9 .ds-hero .cards .ds-card:hover, [lwt-newtab-brighttext]:not(.force-light-theme)
       .ds-column-10 .ds-hero .cards .ds-card:hover, [lwt-newtab-brighttext]:not(.force-light-theme)
       .ds-column-11 .ds-hero .cards .ds-card:hover, [lwt-newtab-brighttext]:not(.force-light-theme)
       .ds-column-12 .ds-hero .cards .ds-card:hover {
         background: none; }
         [lwt-newtab-brighttext]:not(.force-light-theme) .ds-column-9 .ds-hero .cards .ds-card:hover .title, [lwt-newtab-brighttext]:not(.force-light-theme)
         .ds-column-10 .ds-hero .cards .ds-card:hover .title, [lwt-newtab-brighttext]:not(.force-light-theme)
         .ds-column-11 .ds-hero .cards .ds-card:hover .title, [lwt-newtab-brighttext]:not(.force-light-theme)
@@ -2555,16 +2557,18 @@ main {
   flex-direction: column;
   position: relative; }
   .ds-card:hover header {
     color: #0060DF; }
     [lwt-newtab-brighttext]:not(.force-light-theme) .ds-card:hover header {
       color: #45A1FF; }
   .ds-card:active header {
     color: #003EAA; }
+    [lwt-newtab-brighttext]:not(.force-light-theme) .ds-card:active header {
+      color: #0A84FF; }
   .ds-card .img-wrapper {
     width: 100%; }
   .ds-card .img {
     background-color: var(--newtab-card-placeholder-color);
     background-position: center;
     background-repeat: no-repeat;
     background-size: cover;
     border-radius: 4px;
--- a/browser/components/newtab/data/content/activity-stream.bundle.js
+++ b/browser/components/newtab/data/content/activity-stream.bundle.js
@@ -7291,19 +7291,19 @@ class DSLinkMenu_DSLinkMenu extends exte
       dsLinkMenuHostDiv.parentElement.classList.add("last-item");
     }
     dsLinkMenuHostDiv.parentElement.classList.add("active");
   }
 
   render() {
     const { index, dispatch } = this.props;
     const isContextMenuOpen = this.state.showContextMenu && this.state.activeCard === index;
-    const TOP_STORIES_SOURCE = "TOP_STORIES";
-    const TOP_STORIES_CONTEXT_MENU_OPTIONS = ["OpenInNewWindow", "OpenInPrivateWindow"];
+    const TOP_STORIES_CONTEXT_MENU_OPTIONS = ["OpenInNewWindow", "OpenInPrivateWindow", "Separator", "BlockUrl"];
     const title = this.props.title || this.props.source;
+    const type = this.props.type || "DISCOVERY_STREAM";
 
     return external_React_default.a.createElement(
       "div",
       null,
       external_React_default.a.createElement(
         "button",
         { ref: this.contextMenuButtonRef,
           className: "context-menu-button icon",
@@ -7313,25 +7313,27 @@ class DSLinkMenu_DSLinkMenu extends exte
           "span",
           { className: "sr-only" },
           external_React_default.a.createElement(external_ReactIntl_["FormattedMessage"], { id: "context_menu_button_sr", values: { title } })
         )
       ),
       isContextMenuOpen && external_React_default.a.createElement(LinkMenu["LinkMenu"], {
         dispatch: dispatch,
         index: index,
-        source: TOP_STORIES_SOURCE,
+        source: type.toUpperCase(),
         onUpdate: this.onMenuUpdate,
         onShow: this.onMenuShow,
         options: TOP_STORIES_CONTEXT_MENU_OPTIONS,
+        shouldSendImpressionStats: true,
         site: {
           referrer: "https://getpocket.com/recommendations",
           title: this.props.title,
           type: this.props.type,
-          url: this.props.url
+          url: this.props.url,
+          guid: this.props.id
         } })
     );
   }
 }
 
 const DSLinkMenu = Object(external_ReactIntl_["injectIntl"])(DSLinkMenu_DSLinkMenu);
 // EXTERNAL MODULE: ./content-src/components/DiscoveryStreamImpressionStats/ImpressionStats.jsx
 var ImpressionStats = __webpack_require__(33);
@@ -7478,17 +7480,18 @@ class DSCard_DSCard extends external_Rea
         ),
         external_React_default.a.createElement(ImpressionStats["ImpressionStats"], {
           campaignId: this.props.campaignId,
           rows: [{ id: this.props.id, pos: this.props.pos }],
           dispatch: this.props.dispatch,
           source: this.props.type })
       ),
       external_React_default.a.createElement(DSLinkMenu, {
-        index: this.props.index,
+        id: this.props.id,
+        index: this.props.pos,
         dispatch: this.props.dispatch,
         intl: this.props.intl,
         url: this.props.url,
         title: this.props.title,
         source: this.props.source,
         type: this.props.type })
     );
   }
@@ -7665,17 +7668,18 @@ class List_ListItem extends external_Rea
         external_React_default.a.createElement("div", { className: "ds-list-image", style: { backgroundImage: `url(${this.props.image_src})` } }),
         external_React_default.a.createElement(ImpressionStats["ImpressionStats"], {
           campaignId: this.props.campaignId,
           rows: [{ id: this.props.id, pos: this.props.pos }],
           dispatch: this.props.dispatch,
           source: this.props.type })
       ),
       external_React_default.a.createElement(DSLinkMenu, {
-        index: this.props.index,
+        id: this.props.id,
+        index: this.props.pos,
         dispatch: this.props.dispatch,
         intl: this.props.intl,
         url: this.props.url,
         title: this.props.title,
         source: this.props.source,
         type: this.props.type })
     );
   }
@@ -7759,17 +7763,17 @@ class Hero_Hero extends external_React_d
       }));
     }
   }
 
   render() {
     const { data } = this.props;
 
     // Handle a render before feed has been fetched by displaying nothing
-    if (!data || !data.recommendations) {
+    if (!data || !data.recommendations || !data.recommendations.length) {
       return external_React_default.a.createElement("div", null);
     }
 
     let [heroRec, ...otherRecs] = data.recommendations.slice(0, this.props.items);
     this.heroRec = heroRec;
 
     let cards = otherRecs.map((rec, index) => external_React_default.a.createElement(DSCard_DSCard, {
       campaignId: rec.campaign_id,
@@ -7847,17 +7851,18 @@ class Hero_Hero extends external_React_d
             ),
             external_React_default.a.createElement(ImpressionStats["ImpressionStats"], {
               campaignId: heroRec.campaignId,
               rows: [{ id: heroRec.id, pos: heroRec.pos }],
               dispatch: this.props.dispatch,
               source: this.props.type })
           ),
           external_React_default.a.createElement(DSLinkMenu, {
-            index: this.props.index,
+            id: heroRec.id,
+            index: heroRec.pos,
             dispatch: this.props.dispatch,
             intl: this.props.intl,
             url: heroRec.url,
             title: heroRec.title,
             source: heroRec.domain,
             type: this.props.type })
         ),
         external_React_default.a.createElement(
@@ -12352,16 +12357,40 @@ function DiscoveryStream(prevState = INI
         })
       });
     case Actions["actionTypes"].DISCOVERY_STREAM_SPOCS_ENDPOINT:
       return Object.assign({}, prevState, {
         spocs: Object.assign({}, INITIAL_STATE.DiscoveryStream.spocs, {
           spocs_endpoint: action.data || INITIAL_STATE.DiscoveryStream.spocs.spocs_endpoint
         })
       });
+    case Actions["actionTypes"].PLACES_LINK_BLOCKED:
+      // Return if action data is empty, or spocs or feeds data is not loaded
+      if (!action.data || !prevState.spocs.loaded || !prevState.feeds.loaded) {
+        return prevState;
+      }
+      // Filter spocs and recommendations data inside feeds by removing action.data.url
+      // received on PLACES_LINK_BLOCKED triggered by dismiss link menu option
+      return Object.assign({}, prevState, {
+        spocs: Object.assign({}, prevState.spocs, {
+          data: prevState.spocs.data.spocs ? {
+            spocs: prevState.spocs.data.spocs.filter(s => s.url !== action.data.url)
+          } : {}
+        }),
+        feeds: Object.assign({}, prevState.feeds, {
+          data: Object.keys(prevState.feeds.data).reduce((accumulator, feed_url) => {
+            accumulator[feed_url] = {
+              data: Object.assign({}, prevState.feeds.data[feed_url].data, {
+                recommendations: prevState.feeds.data[feed_url].data.recommendations.filter(r => r.url !== action.data.url)
+              })
+            };
+            return accumulator;
+          }, {})
+        })
+      });
     case Actions["actionTypes"].DISCOVERY_STREAM_SPOCS_UPDATE:
       if (action.data) {
         return Object.assign({}, prevState, {
           spocs: Object.assign({}, prevState.spocs, {
             lastUpdated: action.data.lastUpdated,
             data: action.data.spocs,
             loaded: true
           })
--- a/browser/components/newtab/lib/ActivityStream.jsm
+++ b/browser/components/newtab/lib/ActivityStream.jsm
@@ -238,16 +238,20 @@ const PREFS_CONFIG = new Map([
         api_key_pref: "extensions.pocket.oAuthConsumerKey",
         enabled: isEnabled,
         show_spocs: geo === "US",
         // This is currently an exmple layout used for dev purposes.
         layout_endpoint: "https://getpocket.cdn.mozilla.net/v3/newtab/layout?version=1&consumer_key=$apiKey&layout_variant=basic",
       });
     },
   }],
+  ["discoverystream.endpoints", {
+    title: "Endpoint prefixes (comma-separated) that are allowed to be requested",
+    value: "https://getpocket.cdn.mozilla.net/",
+  }],
   ["discoverystream.optOut.0", {
     title: "Opt out of new layout v0",
     value: false,
   }],
   ["discoverystream.spoc.impressions", {
     title: "Track spoc impressions",
     skipBroadcast: true,
     value: "{}",
--- a/browser/components/newtab/lib/DiscoveryStreamFeed.jsm
+++ b/browser/components/newtab/lib/DiscoveryStreamFeed.jsm
@@ -1,29 +1,31 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+const {NewTabUtils} = ChromeUtils.import("resource://gre/modules/NewTabUtils.jsm");
 const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
 XPCOMUtils.defineLazyGlobalGetters(this, ["fetch"]);
 ChromeUtils.defineModuleGetter(this, "perfService", "resource://activity-stream/common/PerfService.jsm");
 
 const {actionTypes: at, actionCreators: ac} = ChromeUtils.import("resource://activity-stream/common/Actions.jsm");
 const {PersistentCache} = ChromeUtils.import("resource://activity-stream/lib/PersistentCache.jsm");
 
 const CACHE_KEY = "discovery_stream";
 const LAYOUT_UPDATE_TIME = 30 * 60 * 1000; // 30 minutes
 const STARTUP_CACHE_EXPIRE_TIME = 7 * 24 * 60 * 60 * 1000; // 1 week
 const COMPONENT_FEEDS_UPDATE_TIME = 30 * 60 * 1000; // 30 minutes
 const SPOCS_FEEDS_UPDATE_TIME = 30 * 60 * 1000; // 30 minutes
 const DEFAULT_RECS_EXPIRE_TIME = 60 * 60 * 1000; // 1 hour
 const MAX_LIFETIME_CAP = 500; // Guard against misconfiguration on the server
 const PREF_CONFIG = "discoverystream.config";
+const PREF_ENDPOINTS = "discoverystream.endpoints";
 const PREF_OPT_OUT = "discoverystream.optOut.0";
 const PREF_SHOW_SPONSORED = "showSponsored";
 const PREF_SPOC_IMPRESSIONS = "discoverystream.spoc.impressions";
 const PREF_REC_IMPRESSIONS = "discoverystream.rec.impressions";
 
 this.DiscoveryStreamFeed = class DiscoveryStreamFeed {
   constructor() {
     // Internal state for checking if we've intialized all our data
@@ -83,27 +85,30 @@ this.DiscoveryStreamFeed = class Discove
   }
 
   async fetchFromEndpoint(endpoint) {
     if (!endpoint) {
       Cu.reportError("Tried to fetch endpoint but none was configured.");
       return null;
     }
     try {
+      // Make sure the requested endpoint is allowed
+      const allowed = this.store.getState().Prefs.values[PREF_ENDPOINTS].split(",");
+      if (!allowed.some(prefix => endpoint.startsWith(prefix))) {
+        throw new Error(`Not one of allowed prefixes (${allowed})`);
+      }
+
       const response = await fetch(endpoint, {credentials: "omit"});
       if (!response.ok) {
-        // istanbul ignore next
-        throw new Error(`${endpoint} returned unexpected status: ${response.status}`);
+        throw new Error(`Unexpected status (${response.status})`);
       }
       return response.json();
     } catch (error) {
-      // istanbul ignore next
       Cu.reportError(`Failed to fetch ${endpoint}: ${error.message}`);
     }
-    // istanbul ignore next
     return null;
   }
 
   /**
    * Returns true if data in the cache for a particular key has expired or is missing.
    * @param {object} cachedData data returned from cache.get()
    * @param {string} key a cache key
    * @param {string?} url for "feed" only, the URL of the feed.
@@ -195,30 +200,35 @@ this.DiscoveryStreamFeed = class Discove
   buildFeedPromise({newFeedsPromises, newFeeds}, isStartup) {
     return component => {
       const {url} = component.feed;
 
       if (!newFeeds[url]) {
         // We initially stub this out so we don't fetch dupes,
         // we then fill in with the proper object inside the promise.
         newFeeds[url] = {};
-
         const feedPromise = this.getComponentFeed(url, isStartup);
-
-        feedPromise.then(data => {
-          newFeeds[url] = data;
+        feedPromise.then(feed => {
+          newFeeds[url] = this.filterRecommendations(feed);
         }).catch(/* istanbul ignore next */ error => {
           Cu.reportError(`Error trying to load component feed ${url}: ${error}`);
         });
 
         newFeedsPromises.push(feedPromise);
       }
     };
   }
 
+  filterRecommendations(feed) {
+    if (feed && feed.data && feed.data.recommendations && feed.data.recommendations.length) {
+      return {data: this.filterBlocked(feed.data, "recommendations")};
+    }
+    return feed;
+  }
+
   /**
    * reduceFeedComponents - Filters out components with no feeds, and combines
    *                        all feeds on this component with the feeds from other components.
    * @param {Boolean} isStartup We have different cache handling for startup.
    * @returns {Function} We return a function so we can contain the scope for isStartup.
    *                     Reduces feeds into promises and feed data.
    */
   reduceFeedComponents(isStartup) {
@@ -304,22 +314,34 @@ this.DiscoveryStreamFeed = class Discove
       lastUpdated: Date.now(),
       data: {},
     };
 
     sendUpdate({
       type: at.DISCOVERY_STREAM_SPOCS_UPDATE,
       data: {
         lastUpdated: spocs.lastUpdated,
-        spocs: this.transform(this.filterSpocs(spocs.data)),
+        spocs: this.transform(this.frequencyCapSpocs(spocs.data)),
       },
     });
   }
 
-  transform(data) {
+  filterBlocked(data, type) {
+    if (data && data[type] && data[type].length) {
+      const filteredItems = data[type].filter(item => !NewTabUtils.blockedLinks.isBlocked({"url": item.url}));
+      return {
+        ...data,
+        [type]: filteredItems,
+      };
+    }
+    return data;
+  }
+
+  transform(spocs) {
+    const data = this.filterBlocked(spocs, "spocs");
     if (data && data.spocs && data.spocs.length) {
       const spocsPerDomain = this.store.getState().DiscoveryStream.spocs.spocs_per_domain || 1;
       const campaignMap = {};
       return {
         ...data,
         spocs: data.spocs
           .map(s => ({...s, score: s.item_score}))
           .filter(s => s.score >= s.min_score)
@@ -335,17 +357,17 @@ this.DiscoveryStreamFeed = class Discove
             return false;
           }),
       };
     }
     return data;
   }
 
   // Filter spocs based on frequency caps
-  filterSpocs(data) {
+  frequencyCapSpocs(data) {
     if (data && data.spocs && data.spocs.length) {
       const {spocs} = data;
       const impressions = this.readImpressionsPref(PREF_SPOC_IMPRESSIONS);
       return {
         ...data,
         spocs: spocs.filter(s => this.isBelowFrequencyCap(impressions, s)),
       };
     }
@@ -706,17 +728,17 @@ this.DiscoveryStreamFeed = class Discove
 
           const cachedData = await this.cache.get() || {};
           const {spocs} = cachedData;
 
           this.store.dispatch(ac.AlsoToPreloaded({
             type: at.DISCOVERY_STREAM_SPOCS_UPDATE,
             data: {
               lastUpdated: spocs.lastUpdated,
-              spocs: this.transform(this.filterSpocs(spocs.data)),
+              spocs: this.transform(this.frequencyCapSpocs(spocs.data)),
             },
           }));
         }
         break;
       case at.UNINIT:
         // When this feed is shutting down:
         this.uninitPrefs();
         break;
--- a/browser/components/newtab/locales-src/id/strings.properties
+++ b/browser/components/newtab/locales-src/id/strings.properties
@@ -88,16 +88,18 @@ section_disclaimer_topstories_buttontext
 # for a "Firefox Home" section. "Firefox" should be treated as a brand and kept
 # in English, while "Home" should be localized matching the about:preferences
 # sidebar mozilla-central string for the panel that has preferences related to
 # what is shown for the homepage, new windows, and new tabs.
 prefs_home_header=Konten Beranda Firefox
 prefs_home_description=Pilih konten yang ingin Anda tampilkan dalam Beranda Firefox.
 
 prefs_content_discovery_header=Beranda Firefox
+prefs_content_discovery_description=Penemuan Konten dalam Firefox Home memungkinkan Anda untuk menemukan artikel bermutu tinggi dan relevan dari seluruh web.
+prefs_content_discovery_button=Matikan Penemuan Konten
 
 # LOCALIZATION NOTE (prefs_section_rows_option): This is a semi-colon list of
 # plural forms used in a drop down of multiple row options (1 row, 2 rows).
 # See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
 prefs_section_rows_option={num} baris
 prefs_search_header=Pencarian Web
 prefs_topsites_description=Situs yang sering Anda kunjungi
 prefs_topstories_description2=Konten bermutu dari seluruh web, khusus untuk Anda
--- a/browser/components/newtab/locales-src/lij/strings.properties
+++ b/browser/components/newtab/locales-src/lij/strings.properties
@@ -1,9 +1,9 @@
-newtab_page_title=Neuvo Feuggio
+newtab_page_title=Neuvo feuggio
 
 header_top_sites=I megio sciti
 header_highlights=In evidensa
 # LOCALIZATION NOTE(header_recommended_by): This is followed by the name
 # of the corresponding content provider.
 header_recommended_by=Consegiou da {provider}
 
 # LOCALIZATION NOTE(context_menu_button_sr): This is for screen readers when
@@ -47,18 +47,18 @@ menu_action_archive_pocket=Archivia in P
 
 # LOCALIZATION NOTE (menu_action_show_file_*): These are platform specific strings
 # found in the context menu of an item that has been downloaded. The intention behind
 # "this action" is that it will show where the downloaded file exists on the file system
 # for each operating system.
 menu_action_show_file_mac_os=Fanni vedde in Finder
 menu_action_show_file_windows=Arvi cartella
 menu_action_show_file_linux=Arvi cartella
-menu_action_show_file_default=Fanni vedde file
-menu_action_open_file=Arvi file
+menu_action_show_file_default=Mostra o schedaio
+menu_action_open_file=Arvi schedaio
 
 # LOCALIZATION NOTE (menu_action_copy_download_link, menu_action_go_to_download_page):
 # "Download" here, in both cases, is not a verb, it is a noun. As in, "Copy the
 # link that belongs to this downloaded item"
 menu_action_copy_download_link=Còpia indirisso òrigine
 menu_action_go_to_download_page=Vanni a-a pagina de descaregamento
 menu_action_remove_download=Scancella da-a stöia
 
@@ -88,16 +88,17 @@ section_disclaimer_topstories_buttontext=Va ben, ò capio
 # for a "Firefox Home" section. "Firefox" should be treated as a brand and kept
 # in English, while "Home" should be localized matching the about:preferences
 # sidebar mozilla-central string for the panel that has preferences related to
 # what is shown for the homepage, new windows, and new tabs.
 prefs_home_header=Pagina iniçiâ de Firefox
 prefs_home_description=Çerni i contegnui che ti veu vedde inta pagina iniçiâ de Firefox.
 
 prefs_content_discovery_header=Pagina iniçiâ de Firefox
+prefs_content_discovery_button=Dizabilita a descoverta de neuvi contegnui
 
 # LOCALIZATION NOTE (prefs_section_rows_option): This is a semi-colon list of
 # plural forms used in a drop down of multiple row options (1 row, 2 rows).
 # See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
 prefs_section_rows_option={num} riga;{num} righe
 prefs_search_header=Çerca into Web
 prefs_topsites_description=I sciti che ti vixiti de ciù
 prefs_topstories_description2=I megio contegnui pigiæ in gio pe-a ræ, personalizæ pe ti
--- a/browser/components/newtab/locales-src/pt-BR/strings.properties
+++ b/browser/components/newtab/locales-src/pt-BR/strings.properties
@@ -127,24 +127,24 @@ edit_topsites_edit_button=Editar este si
 
 # LOCALIZATION NOTE (topsites_form_*): This is shown in the New/Edit Topsite modal.
 topsites_form_add_header=Novo site popular
 topsites_form_edit_header=Editar site popular
 topsites_form_title_label=Título
 topsites_form_title_placeholder=Digite um título
 topsites_form_url_label=URL
 topsites_form_image_url_label=URL de imagem personalizada
-topsites_form_url_placeholder=Digite ou cole um URL
+topsites_form_url_placeholder=Digite ou cole uma URL
 topsites_form_use_image_link=Usar uma imagem personalizada…
 # LOCALIZATION NOTE (topsites_form_*_button): These are verbs/actions.
 topsites_form_preview_button=Visualizar
 topsites_form_add_button=Adicionar
 topsites_form_save_button=Salvar
 topsites_form_cancel_button=Cancelar
-topsites_form_url_validation=É necessário um URL válido
+topsites_form_url_validation=É necessário uma URL válida
 topsites_form_image_validation=Não foi possível carregar a imagem. Tente uma URL diferente.
 
 # LOCALIZATION NOTE (pocket_read_more): This is shown at the bottom of the
 # trending stories section and precedes a list of links to popular topics.
 pocket_read_more=Tópicos populares:
 # LOCALIZATION NOTE (pocket_read_even_more): This is shown as a link at the
 # end of the list of popular topic links.
 pocket_read_even_more=Ver mais histórias
--- a/browser/components/newtab/prerendered/locales/id/activity-stream-strings.js
+++ b/browser/components/newtab/prerendered/locales/id/activity-stream-strings.js
@@ -36,18 +36,18 @@ window.gActivityStreamStrings = {
   "search_header": "Pencarian {search_engine_name}",
   "search_web_placeholder": "Cari di Web",
   "section_disclaimer_topstories": "Kisah paling menarik di web, dipilih berdasarkan yang Anda baca. Dari Pocket, kini bagian dari Mozilla.",
   "section_disclaimer_topstories_linktext": "Pelajari cara kerjanya.",
   "section_disclaimer_topstories_buttontext": "Oke, paham",
   "prefs_home_header": "Konten Beranda Firefox",
   "prefs_home_description": "Pilih konten yang ingin Anda tampilkan dalam Beranda Firefox.",
   "prefs_content_discovery_header": "Beranda Firefox",
-  "prefs_content_discovery_description": "Content Discovery in Firefox Home allows you to discover high-quality, relevant articles from across the web.",
-  "prefs_content_discovery_button": "Turn Off Content Discovery",
+  "prefs_content_discovery_description": "Penemuan Konten dalam Firefox Home memungkinkan Anda untuk menemukan artikel bermutu tinggi dan relevan dari seluruh web.",
+  "prefs_content_discovery_button": "Matikan Penemuan Konten",
   "prefs_section_rows_option": "{num} baris",
   "prefs_search_header": "Pencarian Web",
   "prefs_topsites_description": "Situs yang sering Anda kunjungi",
   "prefs_topstories_description2": "Konten bermutu dari seluruh web, khusus untuk Anda",
   "prefs_topstories_options_sponsored_label": "Konten Sponsor",
   "prefs_topstories_sponsored_learn_more": "Pelajari lebih lanjut",
   "prefs_highlights_description": "Sejumlah situs yang Anda simpan atau kunjungi",
   "prefs_highlights_options_visited_label": "Laman yang Dikunjungi",
--- a/browser/components/newtab/prerendered/locales/lij/activity-stream-noscripts.html
+++ b/browser/components/newtab/prerendered/locales/lij/activity-stream-noscripts.html
@@ -1,14 +1,14 @@
 <!doctype html>
 <html lang="lij" dir="ltr">
   <head>
     <meta charset="utf-8">
     <meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'unsafe-inline' resource: chrome:; connect-src https:; img-src https: data: blob:; style-src 'unsafe-inline';">
-    <title>Neuvo Feuggio</title>
+    <title>Neuvo feuggio</title>
     <link rel="icon" type="image/png" href="chrome://branding/content/icon32.png"/>
     <link rel="stylesheet" href="chrome://browser/content/contentSearchUI.css" />
     <link rel="stylesheet" href="resource://activity-stream/css/activity-stream.css" />
   </head>
   <body class="activity-stream">
     <div id="root"><!-- Regular React Rendering --></div>
     <div id="snippets-container">
       <div id="snippets"></div>
--- a/browser/components/newtab/prerendered/locales/lij/activity-stream-prerendered-noscripts.html
+++ b/browser/components/newtab/prerendered/locales/lij/activity-stream-prerendered-noscripts.html
@@ -1,14 +1,14 @@
 <!doctype html>
 <html lang="lij" dir="ltr">
   <head>
     <meta charset="utf-8">
     <meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'unsafe-inline' resource: chrome:; connect-src https:; img-src https: data: blob:; style-src 'unsafe-inline';">
-    <title>Neuvo Feuggio</title>
+    <title>Neuvo feuggio</title>
     <link rel="icon" type="image/png" href="chrome://branding/content/icon32.png"/>
     <link rel="stylesheet" href="chrome://browser/content/contentSearchUI.css" />
     <link rel="stylesheet" href="resource://activity-stream/css/activity-stream.css" />
   </head>
   <body class="activity-stream">
     <div id="root"><div data-reactroot=""><div class="outer-wrapper fixed-to-top"><main><div class="non-collapsible-section"><div class="search-wrapper"><div class="search-inner-wrapper"><label for="newtab-search-text" class="search-label"><span class="sr-only"><span>Çerca inta Ræ</span></span></label><input type="search" id="newtab-search-text" maxLength="256" placeholder="Çerca inta Ræ" title="Çerca inta Ræ"/><button id="searchSubmit" class="search-button" title="Çerca"><span class="sr-only"><span>Çerca</span></span></button></div></div></div><div class="body-wrapper"><div class="sections-list"><section class="collapsible-section top-sites animation-enabled" data-section-id="topsites"><div class="section-top-bar"><h3 class="section-title"><span class="click-target-container"><span class="click-target"><span class="icon icon-small-spacer icon-topsites"></span><span>I megio sciti</span></span><span class="click-target"></span><span class="learn-more-link-wrapper"></span></span></h3><div><button class="context-menu-button icon" title="Arvi menû"><span class="sr-only"><span>Arvi into menû contesto pe-a seçion</span></span></button></div></div><div class="section-body"><ul class="top-sites-list"><li class="top-site-outer placeholder "><div class="top-site-inner"><a tabindex="0" draggable="true"><div class="tile" aria-hidden="true"><div class="screenshot" style="background-image:none"></div></div><div class="title "><span dir="auto"></span></div></a><button class="context-menu-button edit-button icon" title="Cangia sto scito"></button></div></li><li class="top-site-outer placeholder "><div class="top-site-inner"><a tabindex="0" draggable="true"><div class="tile" aria-hidden="true"><div class="screenshot" style="background-image:none"></div></div><div class="title "><span dir="auto"></span></div></a><button class="context-menu-button edit-button icon" title="Cangia sto scito"></button></div></li><li class="top-site-outer placeholder "><div class="top-site-inner"><a tabindex="0" draggable="true"><div class="tile" aria-hidden="true"><div class="screenshot" style="background-image:none"></div></div><div class="title "><span dir="auto"></span></div></a><button class="context-menu-button edit-button icon" title="Cangia sto scito"></button></div></li><li class="top-site-outer placeholder "><div class="top-site-inner"><a tabindex="0" draggable="true"><div class="tile" aria-hidden="true"><div class="screenshot" style="background-image:none"></div></div><div class="title "><span dir="auto"></span></div></a><button class="context-menu-button edit-button icon" title="Cangia sto scito"></button></div></li><li class="top-site-outer placeholder "><div class="top-site-inner"><a tabindex="0" draggable="true"><div class="tile" aria-hidden="true"><div class="screenshot" style="background-image:none"></div></div><div class="title "><span dir="auto"></span></div></a><button class="context-menu-button edit-button icon" title="Cangia sto scito"></button></div></li><li class="top-site-outer placeholder "><div class="top-site-inner"><a tabindex="0" draggable="true"><div class="tile" aria-hidden="true"><div class="screenshot" style="background-image:none"></div></div><div class="title "><span dir="auto"></span></div></a><button class="context-menu-button edit-button icon" title="Cangia sto scito"></button></div></li><li class="top-site-outer placeholder hide-for-narrow"><div class="top-site-inner"><a tabindex="0" draggable="true"><div class="tile" aria-hidden="true"><div class="screenshot" style="background-image:none"></div></div><div class="title "><span dir="auto"></span></div></a><button class="context-menu-button edit-button icon" title="Cangia sto scito"></button></div></li><li class="top-site-outer placeholder hide-for-narrow"><div class="top-site-inner"><a tabindex="0" draggable="true"><div class="tile" aria-hidden="true"><div class="screenshot" style="background-image:none"></div></div><div class="title "><span dir="auto"></span></div></a><button class="context-menu-button edit-button icon" title="Cangia sto scito"></button></div></li></ul><div class="edit-topsites-wrapper"></div></div></section><section class="collapsible-section section normal-cards animation-enabled" data-section-id="topstories"><div class="section-top-bar"><h3 class="section-title"><span class="click-target-container"><span class="click-target"><span class="icon icon-small-spacer icon-pocket"></span><span>Consegiou da Pocket</span></span><span class="click-target"></span><span class="learn-more-link-wrapper"></span></span></h3><div><button class="context-menu-button icon" title="Arvi menû"><span class="sr-only"><span>Arvi into menû contesto pe-a seçion</span></span></button></div></div><div class="section-body"><ul class="section-list" style="padding:0"></ul><div class="top-stories-bottom-container"><div class="wrapper-more-recommendations"></div></div></div></section><section class="collapsible-section section normal-cards animation-enabled" data-section-id="highlights"><div class="section-top-bar"><h3 class="section-title"><span class="click-target-container"><span class="click-target"><span class="icon icon-small-spacer icon-highlights"></span><span>In evidensa</span></span><span class="click-target"></span><span class="learn-more-link-wrapper"></span></span></h3><div><button class="context-menu-button icon" title="Arvi menû"><span class="sr-only"><span>Arvi into menû contesto pe-a seçion</span></span></button></div></div><div class="section-body"><ul class="section-list" style="padding:0"></ul></div></section></div><div class="prefs-button"><button class="icon icon-settings" title="Personalizza a teu pagina Neuvo feuggio"></button></div></div></main></div></div></div>
     <div id="snippets-container">
       <div id="snippets"></div>
--- a/browser/components/newtab/prerendered/locales/lij/activity-stream-prerendered.html
+++ b/browser/components/newtab/prerendered/locales/lij/activity-stream-prerendered.html
@@ -1,14 +1,14 @@
 <!doctype html>
 <html lang="lij" dir="ltr">
   <head>
     <meta charset="utf-8">
     <meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'unsafe-inline' resource: chrome:; connect-src https:; img-src https: data: blob:; style-src 'unsafe-inline';">
-    <title>Neuvo Feuggio</title>
+    <title>Neuvo feuggio</title>
     <link rel="icon" type="image/png" href="chrome://branding/content/icon32.png"/>
     <link rel="stylesheet" href="chrome://browser/content/contentSearchUI.css" />
     <link rel="stylesheet" href="resource://activity-stream/css/activity-stream.css" />
   </head>
   <body class="activity-stream">
     <div id="root"><div data-reactroot=""><div class="outer-wrapper fixed-to-top"><main><div class="non-collapsible-section"><div class="search-wrapper"><div class="search-inner-wrapper"><label for="newtab-search-text" class="search-label"><span class="sr-only"><span>Çerca inta Ræ</span></span></label><input type="search" id="newtab-search-text" maxLength="256" placeholder="Çerca inta Ræ" title="Çerca inta Ræ"/><button id="searchSubmit" class="search-button" title="Çerca"><span class="sr-only"><span>Çerca</span></span></button></div></div></div><div class="body-wrapper"><div class="sections-list"><section class="collapsible-section top-sites animation-enabled" data-section-id="topsites"><div class="section-top-bar"><h3 class="section-title"><span class="click-target-container"><span class="click-target"><span class="icon icon-small-spacer icon-topsites"></span><span>I megio sciti</span></span><span class="click-target"></span><span class="learn-more-link-wrapper"></span></span></h3><div><button class="context-menu-button icon" title="Arvi menû"><span class="sr-only"><span>Arvi into menû contesto pe-a seçion</span></span></button></div></div><div class="section-body"><ul class="top-sites-list"><li class="top-site-outer placeholder "><div class="top-site-inner"><a tabindex="0" draggable="true"><div class="tile" aria-hidden="true"><div class="screenshot" style="background-image:none"></div></div><div class="title "><span dir="auto"></span></div></a><button class="context-menu-button edit-button icon" title="Cangia sto scito"></button></div></li><li class="top-site-outer placeholder "><div class="top-site-inner"><a tabindex="0" draggable="true"><div class="tile" aria-hidden="true"><div class="screenshot" style="background-image:none"></div></div><div class="title "><span dir="auto"></span></div></a><button class="context-menu-button edit-button icon" title="Cangia sto scito"></button></div></li><li class="top-site-outer placeholder "><div class="top-site-inner"><a tabindex="0" draggable="true"><div class="tile" aria-hidden="true"><div class="screenshot" style="background-image:none"></div></div><div class="title "><span dir="auto"></span></div></a><button class="context-menu-button edit-button icon" title="Cangia sto scito"></button></div></li><li class="top-site-outer placeholder "><div class="top-site-inner"><a tabindex="0" draggable="true"><div class="tile" aria-hidden="true"><div class="screenshot" style="background-image:none"></div></div><div class="title "><span dir="auto"></span></div></a><button class="context-menu-button edit-button icon" title="Cangia sto scito"></button></div></li><li class="top-site-outer placeholder "><div class="top-site-inner"><a tabindex="0" draggable="true"><div class="tile" aria-hidden="true"><div class="screenshot" style="background-image:none"></div></div><div class="title "><span dir="auto"></span></div></a><button class="context-menu-button edit-button icon" title="Cangia sto scito"></button></div></li><li class="top-site-outer placeholder "><div class="top-site-inner"><a tabindex="0" draggable="true"><div class="tile" aria-hidden="true"><div class="screenshot" style="background-image:none"></div></div><div class="title "><span dir="auto"></span></div></a><button class="context-menu-button edit-button icon" title="Cangia sto scito"></button></div></li><li class="top-site-outer placeholder hide-for-narrow"><div class="top-site-inner"><a tabindex="0" draggable="true"><div class="tile" aria-hidden="true"><div class="screenshot" style="background-image:none"></div></div><div class="title "><span dir="auto"></span></div></a><button class="context-menu-button edit-button icon" title="Cangia sto scito"></button></div></li><li class="top-site-outer placeholder hide-for-narrow"><div class="top-site-inner"><a tabindex="0" draggable="true"><div class="tile" aria-hidden="true"><div class="screenshot" style="background-image:none"></div></div><div class="title "><span dir="auto"></span></div></a><button class="context-menu-button edit-button icon" title="Cangia sto scito"></button></div></li></ul><div class="edit-topsites-wrapper"></div></div></section><section class="collapsible-section section normal-cards animation-enabled" data-section-id="topstories"><div class="section-top-bar"><h3 class="section-title"><span class="click-target-container"><span class="click-target"><span class="icon icon-small-spacer icon-pocket"></span><span>Consegiou da Pocket</span></span><span class="click-target"></span><span class="learn-more-link-wrapper"></span></span></h3><div><button class="context-menu-button icon" title="Arvi menû"><span class="sr-only"><span>Arvi into menû contesto pe-a seçion</span></span></button></div></div><div class="section-body"><ul class="section-list" style="padding:0"></ul><div class="top-stories-bottom-container"><div class="wrapper-more-recommendations"></div></div></div></section><section class="collapsible-section section normal-cards animation-enabled" data-section-id="highlights"><div class="section-top-bar"><h3 class="section-title"><span class="click-target-container"><span class="click-target"><span class="icon icon-small-spacer icon-highlights"></span><span>In evidensa</span></span><span class="click-target"></span><span class="learn-more-link-wrapper"></span></span></h3><div><button class="context-menu-button icon" title="Arvi menû"><span class="sr-only"><span>Arvi into menû contesto pe-a seçion</span></span></button></div></div><div class="section-body"><ul class="section-list" style="padding:0"></ul></div></section></div><div class="prefs-button"><button class="icon icon-settings" title="Personalizza a teu pagina Neuvo feuggio"></button></div></div></main></div></div></div>
     <div id="snippets-container">
       <div id="snippets"></div>
--- a/browser/components/newtab/prerendered/locales/lij/activity-stream-strings.js
+++ b/browser/components/newtab/prerendered/locales/lij/activity-stream-strings.js
@@ -1,11 +1,11 @@
 // Note - this is a generated lij file.
 window.gActivityStreamStrings = {
-  "newtab_page_title": "Neuvo Feuggio",
+  "newtab_page_title": "Neuvo feuggio",
   "header_top_sites": "I megio sciti",
   "header_highlights": "In evidensa",
   "header_recommended_by": "Consegiou da {provider}",
   "context_menu_button_sr": "Arvi into menû contesto pe {title}",
   "section_context_menu_button_sr": "Arvi into menû contesto pe-a seçion",
   "type_label_visited": "Vixitou",
   "type_label_bookmarked": "Azonto a-i segnalibbri",
   "type_label_recommended": "De tentensa",
@@ -22,32 +22,32 @@ window.gActivityStreamStrings = {
   "confirm_history_delete_p1": "Te seguo de scancelâ tutte e ripetiçioin de sta pagina da stöia?",
   "confirm_history_delete_notice_p2": "Sta açion a no se peu anulâ.",
   "menu_action_save_to_pocket": "Sarva in Pocket",
   "menu_action_delete_pocket": "Scancella da Pocket",
   "menu_action_archive_pocket": "Archivia in Pocket",
   "menu_action_show_file_mac_os": "Fanni vedde in Finder",
   "menu_action_show_file_windows": "Arvi cartella",
   "menu_action_show_file_linux": "Arvi cartella",
-  "menu_action_show_file_default": "Fanni vedde file",
-  "menu_action_open_file": "Arvi file",
+  "menu_action_show_file_default": "Mostra o schedaio",
+  "menu_action_open_file": "Arvi schedaio",
   "menu_action_copy_download_link": "Còpia indirisso òrigine",
   "menu_action_go_to_download_page": "Vanni a-a pagina de descaregamento",
   "menu_action_remove_download": "Scancella da-a stöia",
   "search_button": "Çerca",
   "search_header": "Riçerca {search_engine_name}",
   "search_web_placeholder": "Çerca inta Ræ",
   "section_disclaimer_topstories": "E stöie ciù interesanti do Web, seleçionæ in baze a quello che ti lezi. Pigiæ da Pocket, che oua o l'é parte de Mozilla.",
   "section_disclaimer_topstories_linktext": "Descòvri comme fonçionn-a.",
   "section_disclaimer_topstories_buttontext": "Va ben, ò capio",
   "prefs_home_header": "Pagina iniçiâ de Firefox",
   "prefs_home_description": "Çerni i contegnui che ti veu vedde inta pagina iniçiâ de Firefox.",
   "prefs_content_discovery_header": "Pagina iniçiâ de Firefox",
   "prefs_content_discovery_description": "Content Discovery in Firefox Home allows you to discover high-quality, relevant articles from across the web.",
-  "prefs_content_discovery_button": "Turn Off Content Discovery",
+  "prefs_content_discovery_button": "Dizabilita a descoverta de neuvi contegnui",
   "prefs_section_rows_option": "{num} riga;{num} righe",
   "prefs_search_header": "Çerca into Web",
   "prefs_topsites_description": "I sciti che ti vixiti de ciù",
   "prefs_topstories_description2": "I megio contegnui pigiæ in gio pe-a ræ, personalizæ pe ti",
   "prefs_topstories_options_sponsored_label": "Stöie sponsorizæ",
   "prefs_topstories_sponsored_learn_more": "Atre informaçioin",
   "prefs_highlights_description": "'Na seleçion di sciti che t'ê sarvou ò vixitou",
   "prefs_highlights_options_visited_label": "Pagine vixitæ",
--- a/browser/components/newtab/prerendered/locales/lij/activity-stream.html
+++ b/browser/components/newtab/prerendered/locales/lij/activity-stream.html
@@ -1,14 +1,14 @@
 <!doctype html>
 <html lang="lij" dir="ltr">
   <head>
     <meta charset="utf-8">
     <meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'unsafe-inline' resource: chrome:; connect-src https:; img-src https: data: blob:; style-src 'unsafe-inline';">
-    <title>Neuvo Feuggio</title>
+    <title>Neuvo feuggio</title>
     <link rel="icon" type="image/png" href="chrome://branding/content/icon32.png"/>
     <link rel="stylesheet" href="chrome://browser/content/contentSearchUI.css" />
     <link rel="stylesheet" href="resource://activity-stream/css/activity-stream.css" />
   </head>
   <body class="activity-stream">
     <div id="root"><!-- Regular React Rendering --></div>
     <div id="snippets-container">
       <div id="snippets"></div>
--- a/browser/components/newtab/prerendered/locales/pt-BR/activity-stream-strings.js
+++ b/browser/components/newtab/prerendered/locales/pt-BR/activity-stream-strings.js
@@ -62,23 +62,23 @@ window.gActivityStreamStrings = {
   "edit_topsites_button_text": "Editar",
   "edit_topsites_edit_button": "Editar este site",
   "topsites_form_add_header": "Novo site popular",
   "topsites_form_edit_header": "Editar site popular",
   "topsites_form_title_label": "Título",
   "topsites_form_title_placeholder": "Digite um título",
   "topsites_form_url_label": "URL",
   "topsites_form_image_url_label": "URL de imagem personalizada",
-  "topsites_form_url_placeholder": "Digite ou cole um URL",
+  "topsites_form_url_placeholder": "Digite ou cole uma URL",
   "topsites_form_use_image_link": "Usar uma imagem personalizada…",
   "topsites_form_preview_button": "Visualizar",
   "topsites_form_add_button": "Adicionar",
   "topsites_form_save_button": "Salvar",
   "topsites_form_cancel_button": "Cancelar",
-  "topsites_form_url_validation": "É necessário um URL válido",
+  "topsites_form_url_validation": "É necessário uma URL válida",
   "topsites_form_image_validation": "Não foi possível carregar a imagem. Tente uma URL diferente.",
   "pocket_read_more": "Tópicos populares:",
   "pocket_read_even_more": "Ver mais histórias",
   "pocket_more_reccommendations": "Mais recomendações",
   "pocket_how_it_works": "Como funciona",
   "pocket_cta_button": "Adicionar o Pocket",
   "pocket_cta_text": "Salve as histórias que você gosta no Pocket e abasteça sua mente com leituras fascinantes.",
   "highlights_empty_state": "Comece a navegar e mostraremos aqui alguns ótimos artigos, vídeos e outras páginas que você favoritou ou visitou recentemente.",
--- a/browser/components/newtab/test/browser/browser.ini
+++ b/browser/components/newtab/test/browser/browser.ini
@@ -1,15 +1,16 @@
 [DEFAULT]
 support-files =
   blue_page.html
   red_page.html
   head.js
 prefs =
   browser.newtabpage.activity-stream.debug=false
+  browser.newtabpage.activity-stream.discoverystream.endpoints=data:
 
 [browser_activity_stream_strings.js]
 [browser_as_load_location.js]
 [browser_as_render.js]
 [browser_asrouter_targeting.js]
 [browser_asrouter_trigger_listeners.js]
 [browser_discovery_styles.js]
 [browser_enabled_newtabpage.js]
--- a/browser/components/newtab/test/unit/asrouter/CFRPageActions.test.js
+++ b/browser/components/newtab/test/unit/asrouter/CFRPageActions.test.js
@@ -26,16 +26,17 @@ describe("CFRPageActions", () => {
     "cfr-notification-footer-text",
     "cfr-notification-footer-filled-stars",
     "cfr-notification-footer-empty-stars",
     "cfr-notification-footer-users",
     "cfr-notification-footer-spacer",
     "cfr-notification-footer-learn-more-link",
     "cfr-notification-footer-pintab-animation-container",
     "cfr-notification-footer-animation-button",
+    "cfr-notification-footer-animation-label",
   ];
   const elementClassNames = [
     "popup-notification-body-container",
   ];
 
   beforeEach(() => {
     sandbox = sinon.createSandbox();
     clock = sandbox.useFakeTimers();
--- a/browser/components/newtab/test/unit/common/Reducers.test.js
+++ b/browser/components/newtab/test/unit/common/Reducers.test.js
@@ -697,16 +697,114 @@ describe("Reducers", () => {
         loaded: true,
       });
     });
     it("should handle no data from DISCOVERY_STREAM_SPOCS_UPDATE", () => {
       const data = null;
       const state = DiscoveryStream(undefined, {type: at.DISCOVERY_STREAM_SPOCS_UPDATE, data});
       assert.deepEqual(state.spocs, INITIAL_STATE.DiscoveryStream.spocs);
     });
+    it("should not update state for empty action.data on PLACES_LINK_BLOCKED", () => {
+      const newState = DiscoveryStream(undefined, {type: at.PLACES_LINK_BLOCKED});
+      assert.equal(newState, INITIAL_STATE.DiscoveryStream);
+    });
+    it("should not update state if feeds are not loaded", () => {
+      const deleteAction = {type: at.PLACES_LINK_BLOCKED, data: {url: "foo.com"}};
+      const newState = DiscoveryStream(undefined, deleteAction);
+      assert.equal(newState, INITIAL_STATE.DiscoveryStream);
+    });
+    it("should not update state if spocs and feeds data is undefined", () => {
+      const deleteAction = {type: at.PLACES_LINK_BLOCKED, data: {url: "foo.com"}};
+      const oldState = {
+        spocs: {
+          data: {},
+          loaded: true,
+        },
+        feeds: {
+          data: {},
+          loaded: true,
+        },
+      };
+      const newState = DiscoveryStream(oldState, deleteAction);
+      assert.deepEqual(newState, oldState);
+    });
+    it("should remove the site on PLACES_LINK_BLOCKED from spocs if feeds data is empty", () => {
+      const deleteAction = {type: at.PLACES_LINK_BLOCKED, data: {url: "https://foo.com"}};
+      const oldState = {
+        spocs: {
+          data: {
+            spocs: [
+              {url: "https://foo.com"},
+              {url: "test-spoc.com"},
+            ],
+          },
+          loaded: true,
+        },
+        feeds: {
+          data: {},
+          loaded: true,
+        },
+      };
+      const newState = DiscoveryStream(oldState, deleteAction);
+      assert.deepEqual(newState.spocs.data.spocs, [{url: "test-spoc.com"}]);
+    });
+    it("should remove the site on PLACES_LINK_BLOCKED from feeds if spocs data is empty", () => {
+      const deleteAction = {type: at.PLACES_LINK_BLOCKED, data: {url: "https://foo.com"}};
+      const oldState = {
+        spocs: {
+          data: {},
+          loaded: true,
+        },
+        feeds: {
+          data: {
+            "https://foo.com/feed1": {
+              data: {
+                recommendations: [
+                  {url: "https://foo.com"},
+                  {url: "test.com"},
+                ],
+              },
+            },
+          },
+          loaded: true,
+        },
+      };
+      const newState = DiscoveryStream(oldState, deleteAction);
+      assert.deepEqual(newState.feeds.data["https://foo.com/feed1"].data.recommendations, [{url: "test.com"}]);
+    });
+    it("should remove the site on PLACES_LINK_BLOCKED from both feeds and spocs", () => {
+      const oldState = {
+        feeds: {
+          data: {
+            "https://foo.com/feed1": {
+              data: {
+                recommendations: [
+                  {url: "https://foo.com"},
+                  {url: "test.com"},
+                ],
+              },
+            },
+          },
+          loaded: true,
+        },
+        spocs: {
+          data: {
+            spocs: [
+              {url: "https://foo.com"},
+              {url: "test-spoc.com"},
+            ],
+          },
+          loaded: true,
+        },
+      };
+      const deleteAction = {type: at.PLACES_LINK_BLOCKED, data: {url: "https://foo.com"}};
+      const newState = DiscoveryStream(oldState, deleteAction);
+      assert.deepEqual(newState.spocs.data.spocs, [{url: "test-spoc.com"}]);
+      assert.deepEqual(newState.feeds.data["https://foo.com/feed1"].data.recommendations, [{url: "test.com"}]);
+    });
   });
   describe("Search", () => {
     it("should return INITIAL_STATE by default", () => {
       assert.equal(Search(undefined, {type: "some_action"}), INITIAL_STATE.Search);
     });
     it("should set hide to true on HIDE_SEARCH", () => {
       const nextState = Search(undefined, {type: "HIDE_SEARCH"});
       assert.propertyVal(nextState, "hide", true);
--- a/browser/components/newtab/test/unit/content-src/components/DiscoveryStreamComponents/DSLinkMenu.test.jsx
+++ b/browser/components/newtab/test/unit/content-src/components/DiscoveryStreamComponents/DSLinkMenu.test.jsx
@@ -19,21 +19,21 @@ describe("<DSLinkMenu>", () => {
   });
 
   it("should render LinkMenu when context menu button is clicked", () => {
     let button = wrapper.find(".context-menu-button");
     button.simulate("click", {preventDefault: () => {}});
     assert.equal(wrapper.find(LinkMenu).length, 1);
   });
 
-  it("should pass dispatch, onUpdate, onShow, site, options, source and index to LinkMenu", () => {
+  it("should pass dispatch, onUpdate, onShow, site, options, shouldSendImpressionStats, source and index to LinkMenu", () => {
     wrapper.find(".context-menu-button").simulate("click", {preventDefault: () => {}});
     const linkMenuProps = wrapper.find(LinkMenu).props();
-    ["dispatch", "onUpdate", "onShow", "site", "index", "options", "source"].forEach(prop => assert.property(linkMenuProps, prop));
+    ["dispatch", "onUpdate", "onShow", "site", "index", "options", "source", "shouldSendImpressionStats"].forEach(prop => assert.property(linkMenuProps, prop));
   });
 
   it("should pass through the correct menu options to LinkMenu", () => {
     wrapper.find(".context-menu-button").simulate("click", {preventDefault: () => {}});
     const linkMenuProps = wrapper.find(LinkMenu).props();
     assert.deepEqual(linkMenuProps.options,
-      ["OpenInNewWindow", "OpenInPrivateWindow"]);
+      ["OpenInNewWindow", "OpenInPrivateWindow", "Separator", "BlockUrl"]);
   });
 });
--- a/browser/components/newtab/test/unit/lib/DiscoveryStreamFeed.test.js
+++ b/browser/components/newtab/test/unit/lib/DiscoveryStreamFeed.test.js
@@ -1,24 +1,30 @@
 import {actionCreators as ac, actionTypes as at, actionUtils as au} from "common/Actions.jsm";
 import {combineReducers, createStore} from "redux";
 import {DiscoveryStreamFeed} from "lib/DiscoveryStreamFeed.jsm";
+import {GlobalOverrider} from "test/unit/utils";
 import {reducers} from "common/Reducers.jsm";
 
 const CONFIG_PREF_NAME = "discoverystream.config";
+const DUMMY_ENDPOINT = "https://getpocket.cdn.mozilla.net/dummy";
+const ENDPOINTS_PREF_NAME = "discoverystream.endpoints";
 const SPOC_IMPRESSION_TRACKING_PREF = "discoverystream.spoc.impressions";
 const REC_IMPRESSION_TRACKING_PREF = "discoverystream.rec.impressions";
 const THIRTY_MINUTES = 30 * 60 * 1000;
 const ONE_WEEK = 7 * 24 * 60 * 60 * 1000; // 1 week
 
 describe("DiscoveryStreamFeed", () => {
   let feed;
   let sandbox;
   let fetchStub;
   let clock;
+  let fakeNewTabUtils;
+  let globals;
+
   const setPref = (name, value) => {
     const action = {
       type: at.PREF_CHANGED,
       data: {
         name,
         value: typeof value === "object" ? JSON.stringify(value) : value,
       },
     };
@@ -35,27 +41,82 @@ describe("DiscoveryStreamFeed", () => {
     // Time
     clock = sinon.useFakeTimers();
 
     // Feed
     feed = new DiscoveryStreamFeed();
     feed.store = createStore(combineReducers(reducers), {
       Prefs: {
         values: {
-          [CONFIG_PREF_NAME]: JSON.stringify({enabled: false, show_spocs: false, layout_endpoint: "foo"}),
+          [CONFIG_PREF_NAME]: JSON.stringify({enabled: false, show_spocs: false, layout_endpoint: DUMMY_ENDPOINT}),
+          [ENDPOINTS_PREF_NAME]: DUMMY_ENDPOINT,
         },
       },
     });
 
     sandbox.stub(feed, "_maybeUpdateCachedData").resolves();
+
+    globals = new GlobalOverrider();
+    fakeNewTabUtils = {
+      blockedLinks: {
+        links: [],
+        isBlocked: () => false,
+      },
+    };
+    globals.set("NewTabUtils", fakeNewTabUtils);
   });
 
   afterEach(() => {
     clock.restore();
     sandbox.restore();
+    globals.restore();
+  });
+
+  describe("#fetchFromEndpoint", () => {
+    beforeEach(() => {
+      fetchStub.resolves({
+        json: () => Promise.resolve("hi"),
+        ok: true,
+      });
+    });
+    it("should get a response", async () => {
+      const response = await feed.fetchFromEndpoint(DUMMY_ENDPOINT);
+
+      assert.equal(response, "hi");
+    });
+    it("should not send cookies", async () => {
+      await feed.fetchFromEndpoint(DUMMY_ENDPOINT);
+
+      assert.propertyVal(fetchStub.firstCall.args[1], "credentials", "omit");
+    });
+    it("should allow unexpected response", async () => {
+      fetchStub.resolves({ok: false});
+
+      const response = await feed.fetchFromEndpoint(DUMMY_ENDPOINT);
+
+      assert.equal(response, null);
+    });
+    it("should disallow unexpected endpoints", async () => {
+      feed.store.getState = () => ({
+        Prefs: {values: {[ENDPOINTS_PREF_NAME]: "https://other.site"}},
+      });
+
+      const response = await feed.fetchFromEndpoint(DUMMY_ENDPOINT);
+
+      assert.equal(response, null);
+    });
+    it("should allow multiple endpoints", async () => {
+      feed.store.getState = () => ({
+        Prefs: {values: {[ENDPOINTS_PREF_NAME]: `https://other.site,${DUMMY_ENDPOINT}`}},
+      });
+
+      const response = await feed.fetchFromEndpoint(DUMMY_ENDPOINT);
+
+      assert.equal(response, "hi");
+    });
   });
 
   describe("#loadLayout", () => {
     it("should fetch data and populate the cache if it is empty", async () => {
       const resp = {layout: ["foo", "bar"]};
       const fakeCache = {};
       sandbox.stub(feed.cache, "get").returns(Promise.resolve(fakeCache));
       sandbox.stub(feed.cache, "set").returns(Promise.resolve());
@@ -529,17 +590,90 @@ describe("DiscoveryStreamFeed", () => {
         {campaign_id: 1, item_score: 0.8, score: 0.8, min_score: 0.1},
         {campaign_id: 3, item_score: 0.7, score: 0.7, min_score: 0.1},
         {campaign_id: 3, item_score: 0.7, score: 0.7, min_score: 0.1},
         {campaign_id: 2, item_score: 0.6, score: 0.6, min_score: 0.1},
       ]);
     });
   });
 
-  describe("#filterSpocs", () => {
+  describe("#filterBlocked", () => {
+    it("should return initial data if spocs are empty", () => {
+      const result = feed.filterBlocked({spocs: []});
+
+      assert.equal(result.spocs.length, 0);
+    });
+    it("should return initial spocs data if links are not blocked", () => {
+      const result = feed.filterBlocked({
+        spocs: [
+          {url: "https://foo.com"},
+          {url: "test.com"},
+        ],
+      }, "spocs");
+      assert.equal(result.spocs.length, 2);
+    });
+    it("should return filtered out spocs based on blockedlist", () => {
+      fakeNewTabUtils.blockedLinks.links = [{url: "https://foo.com"}];
+      fakeNewTabUtils.blockedLinks.isBlocked = site => (fakeNewTabUtils.blockedLinks.links[0].url === site.url);
+
+      const result = feed.filterBlocked({
+        spocs: [
+          {url: "https://foo.com"},
+          {url: "test.com"},
+        ],
+      }, "spocs");
+
+      assert.lengthOf(result.spocs, 1);
+      assert.equal(result.spocs[0].url, "test.com");
+      assert.notInclude(result.spocs, fakeNewTabUtils.blockedLinks.links[0]);
+    });
+    it("should return initial recommendations data if links are not blocked", () => {
+      const result = feed.filterBlocked({
+        recommendations: [
+          {url: "https://foo.com"},
+          {url: "test.com"},
+        ],
+      }, "recommendations");
+      assert.equal(result.recommendations.length, 2);
+    });
+    it("should return filtered out recommendations based on blockedlist", () => {
+      fakeNewTabUtils.blockedLinks.links = [{url: "https://foo.com"}];
+      fakeNewTabUtils.blockedLinks.isBlocked = site => (fakeNewTabUtils.blockedLinks.links[0].url === site.url);
+
+      const result = feed.filterBlocked({
+        recommendations: [
+          {url: "https://foo.com"},
+          {url: "test.com"},
+        ],
+      }, "recommendations");
+
+      assert.lengthOf(result.recommendations, 1);
+      assert.equal(result.recommendations[0].url, "test.com");
+      assert.notInclude(result.recommendations, fakeNewTabUtils.blockedLinks.links[0]);
+    });
+    it("filterRecommendations based on blockedlist by passing feed data", () => {
+      fakeNewTabUtils.blockedLinks.links = [{url: "https://foo.com"}];
+      fakeNewTabUtils.blockedLinks.isBlocked = site => (fakeNewTabUtils.blockedLinks.links[0].url === site.url);
+
+      const result = feed.filterRecommendations({
+        data: {
+          recommendations: [
+            {url: "https://foo.com"},
+            {url: "test.com"},
+          ],
+        },
+      });
+
+      assert.lengthOf(result.data.recommendations, 1);
+      assert.equal(result.data.recommendations[0].url, "test.com");
+      assert.notInclude(result.data.recommendations, fakeNewTabUtils.blockedLinks.links[0]);
+    });
+  });
+
+  describe("#frequencyCapSpocs", () => {
     it("should return filtered out spocs based on frequency caps", () => {
       const fakeSpocs = {
         spocs: [
           {
             campaign_id: "seen",
             caps: {
               lifetime: 3,
               campaign: {
@@ -560,17 +694,17 @@ describe("DiscoveryStreamFeed", () => {
           },
         ],
       };
       const fakeImpressions = {
         "seen": [Date.now() - 1],
       };
       sandbox.stub(feed, "readImpressionsPref").returns(fakeImpressions);
 
-      const result = feed.filterSpocs(fakeSpocs);
+      const result = feed.frequencyCapSpocs(fakeSpocs);
 
       assert.equal(result.spocs.length, 1);
       assert.equal(result.spocs[0].campaign_id, "not-seen");
     });
   });
 
   describe("#isBelowFrequencyCap", () => {
     it("should return true if there are no campaign impressions", () => {
--- a/browser/components/preferences/in-content/tests/browser_extension_controlled.js
+++ b/browser/components/preferences/in-content/tests/browser_extension_controlled.js
@@ -4,16 +4,19 @@ const PROXY_PREF = "network.proxy.type";
 
 ChromeUtils.defineModuleGetter(this, "ExtensionSettingsStore",
                                "resource://gre/modules/ExtensionSettingsStore.jsm");
 XPCOMUtils.defineLazyServiceGetter(this, "aboutNewTabService",
                                    "@mozilla.org/browser/aboutnewtab-service;1",
                                    "nsIAboutNewTabService");
 XPCOMUtils.defineLazyPreferenceGetter(this, "proxyType", PROXY_PREF);
 
+const {AddonTestUtils} = ChromeUtils.import("resource://testing-common/AddonTestUtils.jsm");
+AddonTestUtils.initMochitest(this);
+
 const TEST_DIR = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
 const CHROME_URL_ROOT = TEST_DIR + "/";
 const PERMISSIONS_URL = "chrome://browser/content/preferences/sitePermissions.xul";
 let sitePermissionsDialog;
 
 function getSupportsFile(path) {
   let cr = Cc["@mozilla.org/chrome/chrome-registry;1"]
     .getService(Ci.nsIChromeRegistry);
@@ -463,16 +466,17 @@ add_task(async function testExtensionCon
   // Install an extension that will set the default search engine.
   let originalExtension = ExtensionTestUtils.loadExtension({
     useAddonManager: "permanent",
     manifest: Object.assign({}, manifest, {version: "1.0"}),
   });
 
   let messageShown = waitForMessageShown("browserDefaultSearchExtensionContent");
   await originalExtension.startup();
+  await AddonTestUtils.waitForSearchProviderStartup(originalExtension);
   await messageShown;
 
   let addon = await AddonManager.getAddonByID(extensionId);
   is(addon.version, "1.0", "The addon has the expected version.");
 
   // The default search engine has been set by the extension and the user is notified.
   let controlledLabel = controlledContent.querySelector("description");
   let extensionEngine = Services.search.defaultEngine;
@@ -507,16 +511,17 @@ add_task(async function testExtensionCon
   await waitForMessageHidden(controlledContent.id);
 
   // Update the extension and wait for "ready".
   let updatedExtension = ExtensionTestUtils.loadExtension({
     useAddonManager: "permanent",
     manifest: Object.assign({}, manifest, {version: "2.0"}),
   });
   await updatedExtension.startup();
+  await AddonTestUtils.waitForSearchProviderStartup(updatedExtension);
   addon = await AddonManager.getAddonByID(extensionId);
 
   // Verify the extension is updated and search engine didn't change.
   is(addon.version, "2.0", "The updated addon has the expected version");
   is(controlledContent.hidden, true, "The extension controlled row is hidden after update");
   is(initialEngine, Services.search.defaultEngine,
      "default search engine is still the initial engine after update");
 
--- a/browser/components/preferences/in-content/tests/browser_subdialogs.js
+++ b/browser/components/preferences/in-content/tests/browser_subdialogs.js
@@ -18,20 +18,26 @@ function open_subdialog_and_test_generic
     let rv = { acceptCount: 0 };
     let win = content.window;
     content.gSubDialog.open(args.url, null, rv);
     let subdialog = content.gSubDialog._topDialog;
 
     info("waiting for subdialog DOMFrameContentLoaded");
     let dialogOpenPromise;
     await new Promise(resolve => {
-      win.addEventListener("DOMFrameContentLoaded", () => {
+      win.addEventListener("DOMFrameContentLoaded", function frameContentLoaded(ev) {
+        // We can get events for loads in other frames, so we have to filter
+        // those out.
+        if (ev.target != subdialog._frame) {
+          return;
+        }
+        win.removeEventListener("DOMFrameContentLoaded", frameContentLoaded);
         dialogOpenPromise = ContentTaskUtils.waitForEvent(subdialog._overlay, "dialogopen");
         resolve();
-      }, { once: true, capture: true });
+      }, { capture: true });
     });
     let result;
     if (args.domcontentloadedFnStr) {
       // eslint-disable-next-line no-eval
       result = eval(args.domcontentloadedFnStr);
     }
 
     info("waiting for subdialog load");
--- a/browser/components/urlbar/UrlbarInput.jsm
+++ b/browser/components/urlbar/UrlbarInput.jsm
@@ -366,17 +366,19 @@ class UrlbarInput {
   pickResult(event, resultIndex) {
     let result = this.view.getResult(resultIndex);
     let isCanonized = this.setValueFromResult(result, event);
     let where = this._whereToOpen(event);
     let openParams = {
       allowInheritPrincipal: false,
     };
 
-    this.view.close();
+    if (!result.payload.isKeywordOffer) {
+      this.view.close();
+    }
     this.controller.recordSelectedResult(event, resultIndex);
 
     if (isCanonized) {
       this._loadURL(this.value, where, openParams);
       return;
     }
 
     let {url, postData} = UrlbarUtils.getUrlFromResult(result);
--- a/browser/components/urlbar/UrlbarProviderUnifiedComplete.jsm
+++ b/browser/components/urlbar/UrlbarProviderUnifiedComplete.jsm
@@ -243,17 +243,17 @@ function makeUrlbarResult(tokens, info) 
             engine: [action.params.engineName, true],
             suggestion: [action.params.searchSuggestion, true],
             keyword: [action.params.alias, true],
             query: [action.params.searchQuery.trim(), true],
             icon: [info.icon, false],
             isKeywordOffer: [
               action.params.alias &&
                 !action.params.searchQuery.trim() &&
-                !info.style.includes("heuristic"),
+                action.params.alias.startsWith("@"),
               false,
             ],
           })
         );
       case "keyword": {
         let title = info.comment;
         if (tokens && tokens.length > 1) {
           title = bundle.formatStringFromName("bookmarkKeywordSearch",
--- a/browser/components/urlbar/UrlbarView.jsm
+++ b/browser/components/urlbar/UrlbarView.jsm
@@ -40,16 +40,17 @@ class UrlbarView {
     this._rows.addEventListener("mousedown", this);
 
     // For the horizontal fade-out effect, set the overflow attribute on result
     // rows when they overflow.
     this._rows.addEventListener("overflow", this);
     this._rows.addEventListener("underflow", this);
 
     this.panel.addEventListener("popupshowing", this);
+    this.panel.addEventListener("popupshown", this);
     this.panel.addEventListener("popuphiding", this);
 
     this.controller.setView(this);
     this.controller.addQueryListener(this);
   }
 
   get oneOffSearchButtons() {
     if (!this._oneOffSearchButtons) {
@@ -652,19 +653,24 @@ class UrlbarView {
       event.target.toggleAttribute("overflow", false);
     }
   }
 
   _on_popupshowing() {
     this.window.addEventListener("resize", this);
   }
 
+  _on_popupshown() {
+    this.input.inputField.setAttribute("aria-expanded", "true");
+  }
+
   _on_popuphiding() {
     this.controller.cancelQuery();
     this.window.removeEventListener("resize", this);
+    this.input.inputField.setAttribute("aria-expanded", "false");
     this.input.inputField.removeAttribute("aria-activedescendant");
   }
 
   _on_resize() {
     // Close the popup as it would be wrongly sized. This can
     // happen when using special OS resize functions like Win+Arrow.
     this.close();
   }
--- a/browser/components/urlbar/tests/browser/browser_urlbarTokenAlias.js
+++ b/browser/components/urlbar/tests/browser/browser_urlbarTokenAlias.js
@@ -206,46 +206,111 @@ add_task(async function nonTokenAlias() 
 
   await UrlbarTestUtils.promisePopupClose(window,
     () => EventUtils.synthesizeKey("KEY_Escape"));
 
   engine.alias = ALIAS;
 });
 
 
-// Clicking on an @ alias in the popup should fill it in the urlbar input.
+// Clicking on an @ alias offer (an @ alias with an empty search string) in the
+// popup should fill it in the urlbar input.
 add_task(async function clickAndFillAlias() {
   // Do a search for "@" to show all the @ aliases.
   gURLBar.search("@");
   await promiseSearchComplete();
 
   // Find our test engine in the results.  It's probably last, but for
   // robustness don't assume it is.
   let testEngineItem;
   for (let i = 0; !testEngineItem; i++) {
     let details = await UrlbarTestUtils.getDetailsOfResultAt(window, i);
     if (details.searchParams && details.searchParams.keyword == ALIAS) {
       testEngineItem = await waitForAutocompleteResultAt(i);
     }
   }
 
   // Click it.
-  await UrlbarTestUtils.promisePopupClose(window, () => {
-    EventUtils.synthesizeMouseAtCenter(testEngineItem, {});
-  });
+  EventUtils.synthesizeMouseAtCenter(testEngineItem, {});
 
   // A new search will start and its result should be the alias.
   await promiseSearchComplete();
   await waitForAutocompleteResultAt(0);
   await assertAlias(true);
 
   // The urlbar input value should be the alias followed by a space so that it's
   // ready for the user to start typing.
   Assert.equal(gURLBar.textValue, `${ALIAS} `);
 
+  // Press the enter key a couple of times.  Nothing should happen except a new
+  // search will start and its result should be the alias again.  The urlbar
+  // should still contain the alias.  An empty search results page should not
+  // load.  The test will hang if that happens.
+  for (let i = 0; i < 2; i++) {
+    EventUtils.synthesizeKey("KEY_Enter");
+    await promiseSearchComplete();
+    await waitForAutocompleteResultAt(0);
+    await assertAlias(true);
+    Assert.equal(gURLBar.textValue, `${ALIAS} `);
+  }
+
+  await UrlbarTestUtils.promisePopupClose(window,
+    () => EventUtils.synthesizeKey("KEY_Escape"));
+});
+
+
+// Pressing enter on an @ alias offer (an @ alias with an empty search string)
+// in the popup should fill it in the urlbar input.
+add_task(async function enterAndFillAlias() {
+  // Do a search for "@" to show all the @ aliases.
+  gURLBar.search("@");
+  await promiseSearchComplete();
+
+  // Find our test engine in the results.  It's probably last, but for
+  // robustness don't assume it is.
+  let index = 0;
+  for (;; index++) {
+    let details = await UrlbarTestUtils.getDetailsOfResultAt(window, index);
+    if (details.searchParams && details.searchParams.keyword == ALIAS) {
+      index++;
+      break;
+    }
+  }
+
+  if (!UrlbarPrefs.get("quantumbar")) {
+    // With awesomebar, the first result ends up preselected, so one fewer key
+    // down is needed to reach the engine.
+    index--;
+  }
+
+  // Key down to it and press enter.
+  EventUtils.synthesizeKey("KEY_ArrowDown", { repeat: index });
+  EventUtils.synthesizeKey("KEY_Enter");
+
+  // A new search will start and its result should be the alias.
+  await promiseSearchComplete();
+  await waitForAutocompleteResultAt(0);
+  await assertAlias(true);
+
+  // The urlbar input value should be the alias followed by a space so that it's
+  // ready for the user to start typing.
+  Assert.equal(gURLBar.textValue, `${ALIAS} `);
+
+  // Press the enter key a couple of times.  Nothing should happen except a new
+  // search will start and its result should be the alias again.  The urlbar
+  // should still contain the alias.  An empty search results page should not
+  // load.  The test will hang if that happens.
+  for (let i = 0; i < 2; i++) {
+    EventUtils.synthesizeKey("KEY_Enter");
+    await promiseSearchComplete();
+    await waitForAutocompleteResultAt(0);
+    await assertAlias(true);
+    Assert.equal(gURLBar.textValue, `${ALIAS} `);
+  }
+
   await UrlbarTestUtils.promisePopupClose(window,
     () => EventUtils.synthesizeKey("KEY_Escape"));
 });
 
 
 async function doSimpleTest(revertBetweenSteps) {
   // When autofill is enabled, searching for "@tes" will autofill to "@test",
   // which gets in the way of this test task, so temporarily disable it.
--- a/browser/modules/ContentClick.jsm
+++ b/browser/modules/ContentClick.jsm
@@ -66,19 +66,17 @@ var ContentClick = {
     var where = window.whereToOpenLink(json);
     if (where == "current")
       return;
 
     // Todo(903022): code for where == save
 
     let params = {
       charset: browser.characterSet,
-      referrerURI: browser.documentURI,
-      referrerPolicy: json.referrerPolicy,
-      noReferrer: json.noReferrer,
+      referrerInfo: E10SUtils.deserializeReferrerInfo(json.referrerInfo),
       allowMixedContent: json.allowMixedContent,
       isContentWindowPrivate: json.isContentWindowPrivate,
       originPrincipal: json.originPrincipal,
       triggeringPrincipal: json.triggeringPrincipal,
       csp: json.csp ? E10SUtils.deserializeCSP(json.csp) : null,
       frameOuterWindowID: json.frameOuterWindowID,
     };
 
--- a/browser/themes/shared/aboutNetError-new.css
+++ b/browser/themes/shared/aboutNetError-new.css
@@ -189,16 +189,18 @@ body:not(:-moz-any(.clockSkewError,.badS
   padding: 1em 17.5%;
   box-sizing: border-box;
 }
 
 #certificateErrorText {
   font-family: monospace;
   white-space: pre-wrap;
   padding: 1em 0;
+  display: flex;
+  flex-wrap: wrap;
 }
 
 #cert_domain_link:not([href]) {
   color: var(--in-content-page-color);
   text-decoration: none;
 }
 
 .exceptionDialogButtonContainer {
--- a/browser/themes/shared/aboutNetError.css
+++ b/browser/themes/shared/aboutNetError.css
@@ -154,16 +154,18 @@ span#hostname {
   padding: 1em 17.5%;
   box-sizing: border-box;
 }
 
 #certificateErrorText {
   font-family: monospace;
   white-space: pre-wrap;
   padding: 1em 0;
+  display: flex;
+  flex-wrap: wrap;
 }
 
 #cert_domain_link:not([href]) {
   color: var(--in-content-page-color);
   text-decoration: none;
 }
 
 .exceptionDialogButtonContainer {
--- a/browser/themes/shared/browser.inc.css
+++ b/browser/themes/shared/browser.inc.css
@@ -211,16 +211,20 @@
   list-style-image: url(resource://activity-stream/data/content/assets/glyph-help-24.svg);
 }
 
 #cfr-notification-header-image:hover {
   background-color: hsla(0,0%,70%,.2);
   border-radius: 2px;
 }
 
+#contextual-feature-recommendation-notification {
+  width: 343px;
+}
+
 #contextual-feature-recommendation-notification .popup-notification-icon {
   margin-inline-end: 4px;
 }
 
 #contextual-feature-recommendation-notification .popup-notification-body-container {
   padding-bottom: 0;
 }
 
--- a/build.gradle
+++ b/build.gradle
@@ -76,17 +76,17 @@ buildscript {
     ext.jacoco_version = '0.8.1'
 
     if (gradle.mozconfig.substs.MOZ_ANDROID_GOOGLE_PLAY_SERVICES) {
         ext.google_play_services_version = '15.0.1'
         ext.google_play_services_cast_version = '16.0.0'
     }
 
     dependencies {
-        classpath 'org.mozilla.apilint:apilint:0.1.7'
+        classpath 'org.mozilla.apilint:apilint:0.1.8'
         classpath 'com.android.tools.build:gradle:3.1.4'
         classpath 'com.getkeepsafe.dexcount:dexcount-gradle-plugin:0.8.2'
         classpath 'org.apache.commons:commons-exec:1.3'
         classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
     }
 }
 
 // A stream that processes bytes line by line, prepending a tag before sending
--- a/build/autoconf/clang-plugin.m4
+++ b/build/autoconf/clang-plugin.m4
@@ -31,17 +31,21 @@ if test -n "$ENABLE_CLANG_PLUGIN"; then
     AC_MSG_RESULT([$LLVMCONFIG])
 
     if test -z "$LLVMCONFIG"; then
         AC_MSG_ERROR([Cannot find an llvm-config binary for building a clang plugin])
     fi
     dnl For some reason the llvm-config downloaded from clang.llvm.org for clang3_8
     dnl produces a -isysroot flag for a sysroot which might not ship when passed
     dnl --cxxflags. We use sed to remove this argument so that builds work on OSX
-    LLVM_CXXFLAGS=`$LLVMCONFIG --cxxflags | sed -e 's/-isysroot [[^ ]]*//'`
+    dnl
+    dnl For a similar reason, we remove any -gcc-toolchain arguments, since the
+    dnl directories specified by such arguments might not exist on the current
+    dnl machine.
+    LLVM_CXXFLAGS=`$LLVMCONFIG --cxxflags | sed -e 's/-isysroot [[^ ]]*//' -e 's/-gcc-toolchain [[^ ]]*//'`
 
     LLVM_LDFLAGS=`$LLVMCONFIG --ldflags | tr '\n' ' '`
 
     if test "${HOST_OS_ARCH}" = "Darwin"; then
         dnl We need to make sure that we use the symbols coming from the clang
         dnl binary. In order to do this, we need to pass -flat_namespace and
         dnl -undefined suppress to the linker. This makes sure that we link the
         dnl symbols into the flat namespace provided by clang, and thus get
--- a/build/build-clang/build-clang.py
+++ b/build/build-clang/build-clang.py
@@ -115,18 +115,33 @@ def delete(path):
         shutil.rmtree(path)
     else:
         try:
             os.unlink(path)
         except Exception:
             pass
 
 
-def install_libgcc(gcc_dir, clang_dir):
-    out = subprocess.check_output([os.path.join(gcc_dir, "bin", "gcc"),
+def install_libgcc(gcc_dir, clang_dir, is_final_stage):
+    gcc_bin_dir = os.path.join(gcc_dir, 'bin')
+
+    # Copy over gcc toolchain bits that clang looks for, to ensure that
+    # clang is using a consistent version of ld, since the system ld may
+    # be incompatible with the output clang produces.  But copy it to a
+    # target-specific directory so a cross-compiler to Mac doesn't pick
+    # up the (Linux-specific) ld with disastrous results.
+    #
+    # Only install this for the bootstrap process; we expect any consumers of
+    # the newly-built toolchain to provide an appropriate ld themselves.
+    if not is_final_stage:
+        x64_bin_dir = os.path.join(clang_dir, 'x86_64-unknown-linux-gnu', 'bin')
+        mkdir_p(x64_bin_dir)
+        shutil.copy2(os.path.join(gcc_bin_dir, 'ld'), x64_bin_dir)
+
+    out = subprocess.check_output([os.path.join(gcc_bin_dir, "gcc"),
                                    '-print-libgcc-file-name'])
 
     libgcc_dir = os.path.dirname(out.rstrip())
     clang_lib_dir = os.path.join(clang_dir, "lib", "gcc",
                                  "x86_64-unknown-linux-gnu",
                                  os.path.basename(libgcc_dir))
     mkdir_p(clang_lib_dir)
     copy_tree(libgcc_dir, clang_lib_dir)
@@ -304,17 +319,17 @@ def build_one_stage(cc, cxx, asm, ld, ar
     cmake_args += cmake_base_args(
         cc, cxx, asm, ld, ar, ranlib, libtool, inst_dir)
     cmake_args += [
         src_dir
     ]
     build_package(build_dir, cmake_args)
 
     if is_linux():
-        install_libgcc(gcc_dir, inst_dir)
+        install_libgcc(gcc_dir, inst_dir, is_final_stage)
     # For some reasons the import library clang.lib of clang.exe is not
     # installed, so we copy it by ourselves.
     if is_windows():
         # The compiler-rt cmake scripts don't allow to build it for multiple
         # targets at once on Windows, so manually build the 32-bits compiler-rt
         # during the final stage.
         build_32_bit = False
         if is_final_stage:
@@ -400,33 +415,39 @@ def get_tool(config, key):
 #           * (nothing will be deleted here)
 #   share/
 #     clang/
 #       clang-format-diff.py
 #       clang-tidy-diff.py
 #       run-clang-tidy.py
 def prune_final_dir_for_clang_tidy(final_dir, osx_cross_compile):
     # Make sure we only have what we expect.
-    dirs = ("bin", "include", "lib", "lib32", "libexec", "msbuild-bin", "share", "tools")
+    dirs = ["bin", "include", "lib", "lib32", "libexec", "msbuild-bin", "share", "tools"]
+    if is_linux():
+        dirs.append("x86_64-unknown-linux-gnu")
     for f in glob.glob("%s/*" % final_dir):
         if os.path.basename(f) not in dirs:
             raise Exception("Found unknown file %s in the final directory" % f)
         if not os.path.isdir(f):
             raise Exception("Expected %s to be a directory" % f)
 
     # In bin/, only keep clang-tidy and clang-apply-replacements. The last one
     # is used to auto-fix some of the issues detected by clang-tidy.
     re_clang_tidy = re.compile(
         r"^clang-(apply-replacements|format|tidy)(\.exe)?$", re.I)
     for f in glob.glob("%s/bin/*" % final_dir):
         if re_clang_tidy.search(os.path.basename(f)) is None:
             delete(f)
 
     # Keep include/ intact.
 
+    # Remove the target-specific files.
+    if is_linux():
+        shutil.rmtree(os.path.join(final_dir, "x86_64-unknown-linux-gnu"))
+
     # In lib/, only keep lib/clang/N.M.O/include and the LLVM shared library.
     re_ver_num = re.compile(r"^\d+\.\d+\.\d+$", re.I)
     for f in glob.glob("%s/lib/*" % final_dir):
         name = os.path.basename(f)
         if name == "clang":
             continue
         if osx_cross_compile and name == 'libLLVM.dylib':
             continue
@@ -671,19 +692,23 @@ if __name__ == "__main__":
         extra_cxxflags = ["-stdlib=libc++"]
         extra_cflags2 = []
         extra_cxxflags2 = ["-stdlib=libc++"]
         extra_asmflags = []
         extra_ldflags = []
     elif is_linux():
         extra_cflags = []
         extra_cxxflags = []
-        extra_cflags2 = ["-fPIC"]
+        # When building stage2 and stage3, we want the newly-built clang to pick
+        # up whatever headers were installed from the gcc we used to build stage1,
+        # always, rather than the system headers.  Providing -gcc-toolchain
+        # encourages clang to do that.
+        extra_cflags2 = ["-fPIC", '-gcc-toolchain', stage1_inst_dir]
         # Silence clang's warnings about arguments not being used in compilation.
-        extra_cxxflags2 = ["-fPIC", '-Qunused-arguments']
+        extra_cxxflags2 = ["-fPIC", '-Qunused-arguments', '-gcc-toolchain', stage1_inst_dir]
         extra_asmflags = []
         # Avoid libLLVM internal function calls going through the PLT.
         extra_ldflags = ['-Wl,-Bsymbolic-functions']
 
         if 'LD_LIBRARY_PATH' in os.environ:
             os.environ['LD_LIBRARY_PATH'] = ('%s/lib64/:%s' %
                                              (gcc_dir, os.environ['LD_LIBRARY_PATH']))
         else:
deleted file mode 100644
--- a/build/build-clang/clang-tidy-win32.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
-    "llvm_revision": "349247",
-    "stages": "1",
-    "build_libcxx": false,
-    "build_type": "Release",
-    "assertions": false,
-    "build_clang_tidy": true,
-    "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/tags/RELEASE_701/final",
-    "clang_repo": "https://llvm.org/svn/llvm-project/cfe/tags/RELEASE_701/final",
-    "extra_repo": "https://llvm.org/svn/llvm-project/clang-tools-extra/tags/RELEASE_701/final",
-    "compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/tags/RELEASE_701/final",
-    "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/tags/RELEASE_701/final",
-    "python_path": "c:/mozilla-build/python/python.exe",
-    "cc": "cl.exe",
-    "cxx": "cl.exe",
-    "patches": [
-    ]
-}
index 46f4626143abb7d3f0d4d5d2cfad72cfee067c20..fe1d307533a9a85b905ff3d49acee61e65122d7d
GIT binary patch
literal 229376
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
z00000000000000000000000000000000000000000000000000000000000000000
z000000O0=u8G<@~2`<V;{MTCix10E{l_tR+&*f2g3~Dfg#bU4$`cP<=;!F#5f;FC=
z!C>*|oD@2ZLE+KO#aU)*1S>p+iDt0SsT?+&XDZGiDiHMX;-9)V5l=J5=8j8Ga3MK+
zARbgYhi4?tF~pY7Vuz*2CDExoA0}@QC53Jv&eWG9Xya28*i?$1c#5tvfrw9MC9~L>
ztQaaqM?6_ui{OaoCQ_VS$uV>m4ZZOgylgFTuBH;f7%jx(q;q+6nud6)IyM!poJ;3q
z&^c=2sjAup1W(ImQBuBkNIy24rXtQ(#un5*8|^P8ajqh^c3e6$p36vJ(P;|e$@18e
zQrOf)29M6=$%&`RY7lI(btZkY#xmkOX&r)7+Zt0?G;x1QiSs4(2xL4XC56F4%Q3TK
zXbi4+UV?ZI2~~oX9Gy$0q|zxo9)+4rl1Fz2oBTYS;FjQ^pjuEYNEQqfbQh@cU-29H
zTlus3x%_B;Z@wv?kbft?PT<0q&9Bd2Be*X(Dp(~b7o-Y81={>~{7d|u{000`{5ZZJ
z-zq;Z|6%_0uHMA~00000000000000000000000000000000000000000000000000
z000000000000000000000000000000000000000000000|Ci_zT=03hJ9~|uVflP#
zwZNfo;*CVtmFw(mu#BmJ3r^`ODK4L$e4$!-?Tp(!L!{1FV;SUxg}e6OCwzKSpof22
zUSfHrKq<DHBmsxF>MYk1%eCmNQgbZVth2JFST3=%H8sI<jXTRV!g38e%Qe7q^*hVe
z!*X>y%hkbhwL8nz!g4h`%hkYg)jP{o!*W$S%T>X0l{@PzB`jC5v$6_Uu6$>?a#*fx
dXSp(1u5@R)Qdq8JXSoC{S3-y2BG*yw{{ynUeRu!>
index 6f96def91f9228b86ca8f1e8b98f798852d3b1a3..c40fb77c5bfb3176f51dac2cbcd68045f4d1da49
GIT binary patch
literal 294912
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
zBuawev#YE*+BE88w4^7B{l}{K^z6&b>v?Yn-0mGsu_#VejwHt<*OuohUk;4#^*mka
z@r|kT)bsNPsWP&jJ^X^y_&V<(qhm##(eaIbpHpz=z-;;s#SWJP)IaLTj^8;x>drgc
zK=iKQs9|8?gF))%xy6-pck?|w9!;_l-`G0v_@Ca{iHInB{<}vF00000`2PW#%`ejU
zw?y$P@pGHs0000000000000000000000000000000000000000000000000000000
z0000000000000000000000000000000000000000004mhhf$G|5lgDNbC7P0Abr+I
zC`rkvB_)Y@Xf~&k#?KSQ|A=4S{00C300000000000000000000000000000000000
z000000000000000000000000000000000000000000000000000000000000{0~7!
zN<>Whb3j5$N<>W)-$_Ior%M!fCr&qR;{S9u000000000000000000000000000000
z00000000000000000000000000000000000000000000000000000000000000000
z0QkRL5E<5;80jr1h7_S7-A_i!!e@>43k(QG1xNC0qk}^+9vC-ND4Jg@EYvF?7!w*P
z$AM(~IVTMZgGoRX#@83cZ;cKK#rS*52_X?br_i!+nFj}WV0_X1dSMuMv^&39U?|29
z6NL)J1o)Fmk~&Z$MgEwXbQ_nJZzww0|L22P2Zx1(ZqEN74~BIoJ8Vdd#6Fj!MN<Eq
zoq~mAzttZ%s8~p}wdB~4+kf7mWT7(&2=fm`Vf^_IV9*hAf=Gd1Id}Y#Q<`7*0KYrh
zHO!MgbTcyl=E6Kku3wS2TcO>;em*3>uD>V7AB_(F`4cH46@OJldUMHt{h0h>n~(A{
zXhcA8s8^);=4xd9QGRH3bhz}-)k?de<fM=i6x0&Q`pL_5#MH!WJZ@g7U|&=y%3m||
z*Ggp2hfscjzGxY@06&t=&7kt%*mN}UGeo5E9YpaR@gwm||6e~b000000000000000
z00000000000000000000000000000000000000000000000000000000000000000
z000000000000000004mhed1?`SQ$x0s8PP5=wN@;<{%&-l$lhF7#)uB-@N6AcE@bq
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
new file mode 100644
--- /dev/null
+++ b/build/pgo/certs/noSubjectAltName.certspec
@@ -0,0 +1,2 @@
+issuer:printableString/CN=Temporary Certificate Authority/O=Mozilla Testing/OU=Profile Guided Optimization
+subject:certificate without subjectAlternativeNames
--- a/build/pgo/server-locations.txt
+++ b/build/pgo/server-locations.txt
@@ -114,16 +114,17 @@ https://self-signed.example.com:443    p
 https://untrusted.example.com:443      privileged,cert=untrusted
 https://expired.example.com:443        privileged,cert=expired
 https://requestclientcert.example.com:443         privileged,clientauth=request
 https://requireclientcert.example.com:443         privileged,clientauth=require
 https://mismatch.expired.example.com:443	privileged,cert=expired
 https://mismatch.untrusted.example.com:443	privileged,cert=untrusted
 https://untrusted-expired.example.com:443	privileged,cert=untrustedandexpired
 https://mismatch.untrusted-expired.example.com:443	privileged,cert=untrustedandexpired
+https://no-subject-alt-name.example.com:443   cert=noSubjectAltName
 
 # Prevent safebrowsing tests from hitting the network for its-a-trap.html and
 # its-an-attack.html.
 http://www.itisatrap.org:80
 https://www.itisatrap.org:443
 
 #
 # These are subdomains of <ält.example.org>.
--- a/build/unix/build-gcc/build-gcc.sh
+++ b/build/unix/build-gcc/build-gcc.sh
@@ -34,32 +34,39 @@ build_binutils() {
   # if binutils_configure_flags is not set at all, give it the default value
   if [ -z "${binutils_configure_flags+xxx}" ];
   then
     # gold is disabled because we don't use it on automation, and also we ran into
     # some issues with it using this script in build-clang.py.
     #
     # --enable-targets builds extra target support in ld.
     # Enabling aarch64 support brings in arm support, so we don't need to specify that too.
-    binutils_configure_flags="--enable-targets=aarch64-unknown-linux-gnu --disable-gold --enable-plugins --disable-nls --with-sysroot=/"
+    #
+    # It is important to have the binutils --target and the gcc --target match,
+    # so binutils will install binaries in a place that gcc will look for them.
+    binutils_configure_flags="--enable-targets=aarch64-unknown-linux-gnu --build=x86_64-unknown-linux-gnu --target=x86_64-unknown-linux-gnu --disable-gold --enable-plugins --disable-nls --with-sysroot=/"
   fi
 
   mkdir $root_dir/binutils-objdir
   pushd $root_dir/binutils-objdir
   ../binutils-$binutils_version/configure --prefix=${prefix-/tools/gcc}/ $binutils_configure_flags
   make $make_flags
   make install $make_flags DESTDIR=$root_dir
   export PATH=$root_dir/${prefix-/tools/gcc}/bin:$PATH
   popd
 }
 
 build_gcc() {
+  # Be explicit about --build and --target so header and library install
+  # directories are consistent.
+  local target="${1:-x86_64-unknown-linux-gnu}"
+
   mkdir $root_dir/gcc-objdir
   pushd $root_dir/gcc-objdir
-  ../gcc-$gcc_version/configure --prefix=${prefix-/tools/gcc} --enable-languages=c,c++  --disable-nls --disable-gnu-unique-object --enable-__cxa_atexit --with-arch-32=pentiumpro --with-sysroot=/
+  ../gcc-$gcc_version/configure --prefix=${prefix-/tools/gcc} --build=x86_64-unknown-linux-gnu --target="${target}" --enable-languages=c,c++  --disable-nls --disable-gnu-unique-object --enable-__cxa_atexit --with-arch-32=pentiumpro --with-sysroot=/
   make $make_flags
   make $make_flags install DESTDIR=$root_dir
 
   cd $root_dir/tools
   ln -s gcc gcc/bin/cc
 
   tar caf $root_dir/gcc.tar.xz gcc/
   popd
--- a/caps/ContentPrincipal.cpp
+++ b/caps/ContentPrincipal.cpp
@@ -406,18 +406,17 @@ static nsresult GetSpecialBaseDomain(con
   }
 
   if (hasNoRelativeFlag) {
     *aHandled = true;
     return aCodebase->GetSpec(aBaseDomain);
   }
 
   bool isBehaved;
-  if (NS_SUCCEEDED(aCodebase->SchemeIs("indexeddb", &isBehaved)) &&
-      isBehaved) {
+  if (NS_SUCCEEDED(aCodebase->SchemeIs("indexeddb", &isBehaved)) && isBehaved) {
     *aHandled = true;
     return aCodebase->GetSpec(aBaseDomain);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/caps/nsScriptSecurityManager.cpp
+++ b/caps/nsScriptSecurityManager.cpp
@@ -284,22 +284,79 @@ static void InheritAndSetCSPOnPrincipalI
 
   // if the principalToInherit had a CSP, add it to the before
   // created NullPrincipal (unless it already has one)
   MOZ_ASSERT(aPrincipal->GetIsNullPrincipal(),
              "inheriting the CSP only valid for NullPrincipal");
   nsCOMPtr<nsIContentSecurityPolicy> nullPrincipalCSP;
   aPrincipal->GetCsp(getter_AddRefs(nullPrincipalCSP));
   if (nullPrincipalCSP) {
-    MOZ_ASSERT(nullPrincipalCSP == originalCSP,
-               "There should be no other CSP here.");
+#ifdef DEBUG
+    {
+      uint32_t nullPrincipalCSPCount = 0;
+      nullPrincipalCSP->GetPolicyCount(&nullPrincipalCSPCount);
+
+      uint32_t originalCSPCount = 0;
+      originalCSP->GetPolicyCount(&originalCSPCount);
+
+      MOZ_ASSERT(nullPrincipalCSPCount == originalCSPCount,
+                 "There should be no other CSP here.");
+
+      nsAutoString nullPrincipalCSPStr, originalCSPStr;
+      for (uint32_t i = 0; i < originalCSPCount; ++i) {
+        originalCSP->GetPolicyString(i, originalCSPStr);
+        nullPrincipalCSP->GetPolicyString(i, nullPrincipalCSPStr);
+        MOZ_ASSERT(originalCSPStr.Equals(nullPrincipalCSPStr),
+                   "There should be no other CSP string here.");
+      }
+    }
+#endif
     // CSPs are equal, no need to set it again.
     return;
   }
-  aPrincipal->SetCsp(originalCSP);
+
+  // After 965637 all that magical CSP inheritance goes away. For now,
+  // we have to create a clone of the current CSP and have to manually
+  // set it on the Principal.
+  uint32_t count = 0;
+  rv = originalCSP->GetPolicyCount(&count);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
+
+  if (count == 0) {
+    // fast path: if there is nothing to inherit, we can return here.
+    return;
+  }
+
+  RefPtr<nsCSPContext> newCSP = new nsCSPContext();
+  nsWeakPtr loadingContext =
+      static_cast<nsCSPContext*>(originalCSP.get())->GetLoadingContext();
+  nsCOMPtr<Document> doc = do_QueryReferent(loadingContext);
+
+  rv = doc ? newCSP->SetRequestContext(doc, nullptr)
+           : newCSP->SetRequestContext(nullptr, aPrincipal);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
+
+  for (uint32_t i = 0; i < count; ++i) {
+    const nsCSPPolicy* policy = originalCSP->GetPolicy(i);
+    MOZ_ASSERT(policy);
+
+    nsAutoString policyString;
+    policy->toString(policyString);
+
+    rv = newCSP->AppendPolicy(policyString, policy->getReportOnlyFlag(),
+                              policy->getDeliveredViaMetaTagFlag());
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return;
+    }
+  }
+  aPrincipal->SetCsp(newCSP);
 }
 
 nsresult nsScriptSecurityManager::GetChannelResultPrincipal(
     nsIChannel* aChannel, nsIPrincipal** aPrincipal, bool aIgnoreSandboxing) {
   MOZ_ASSERT(aChannel, "Must have channel!");
 
   // Check whether we have an nsILoadInfo that says what we should do.
   nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
--- a/devtools/client/debugger/new/dist/debugger.css
+++ b/devtools/client/debugger/new/dist/debugger.css
@@ -3174,17 +3174,18 @@ html .breakpoints-list .breakpoint.pause
 
 .breakpoints-list .breakpoint .breakpoint-line {
   font-size: 11px;
   color: var(--theme-comment);
   min-width: 16px;
   text-align: end;
 }
 
-.breakpoints-list .breakpoint:hover .breakpoint-line {
+.breakpoints-list .breakpoint:hover .breakpoint-line,
+.breakpoints-list .breakpoint-line-close:focus-within .breakpoint-line {
   color: transparent;
 }
 
 .breakpoints-list .breakpoint.paused:hover {
   border-color: var(--breakpoint-active-color-hover);
 }
 
 .breakpoints-list .breakpoint-label {
@@ -4003,18 +4004,16 @@ html[dir="rtl"] .event-listeners-content
 	margin-inline-start: 0px;
 	margin-top: 0px;
 	margin-bottom: 0px;
 }
 /* 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/>. */
 
-
-
 .scopes-content .toggle-map-scopes {
   border-bottom: 1px solid var(--theme-splitter-color);
   margin-bottom: 3px;
   margin-left: 10px;
   padding: 0.5em 0;
 }
 
 .scopes-content .toggle-map-scopes input {
--- a/devtools/client/debugger/new/src/actions/breakpoints/addBreakpoint.js
+++ b/devtools/client/debugger/new/src/actions/breakpoints/addBreakpoint.js
@@ -11,17 +11,17 @@ import {
   getASTLocation,
   makeBreakpointId,
   makeBreakpointLocation,
   findPosition
 } from "../../utils/breakpoint";
 import { PROMISE } from "../utils/middleware/promise";
 import {
   getSymbols,
-  getFirstVisibleBreakpointPosition,
+  getFirstBreakpointPosition,
   getBreakpointPositionsForSource,
   getSourceFromId
 } from "../../selectors";
 
 import { getTextAtPosition } from "../../utils/source";
 import { recordEvent } from "../../utils/telemetry";
 
 import type {
@@ -100,17 +100,17 @@ export function addBreakpoint(
   options: BreakpointOptions = {}
 ) {
   return async ({ dispatch, getState, sourceMaps, client }: ThunkArgs) => {
     recordEvent("add_breakpoint");
     let position;
     const { sourceId, column } = location;
 
     if (column === undefined) {
-      position = getFirstVisibleBreakpointPosition(getState(), location);
+      position = getFirstBreakpointPosition(getState(), location);
     } else {
       const positions = getBreakpointPositionsForSource(getState(), sourceId);
       position = findPosition(positions, location);
     }
 
     if (!position) {
       return;
     }
--- a/devtools/client/debugger/new/src/actions/breakpoints/syncBreakpoint.js
+++ b/devtools/client/debugger/new/src/actions/breakpoints/syncBreakpoint.js
@@ -13,17 +13,18 @@ import {
   findPosition,
   makeBreakpointLocation
 } from "../../utils/breakpoint";
 
 import { getTextAtPosition } from "../../utils/source";
 import { comparePosition } from "../../utils/location";
 
 import { originalToGeneratedId, isOriginalId } from "devtools-source-map";
-import { getSource } from "../../selectors";
+import { getSource, getBreakpointsList } from "../../selectors";
+import { removeBreakpoint } from ".";
 
 import type { ThunkArgs, Action } from "../types";
 
 import type {
   SourceLocation,
   ASTLocation,
   PendingBreakpoint,
   SourceId,
@@ -82,24 +83,37 @@ function createSyncData(
     { generatedLocation, location },
     overrides
   );
 
   assertBreakpoint(breakpoint);
   return { breakpoint, previousLocation };
 }
 
+// Look for an existing breakpoint at the specified generated location.
+function findExistingBreakpoint(state, generatedLocation) {
+  const breakpoints = getBreakpointsList(state);
+
+  return breakpoints.find(bp => {
+    return (
+      bp.generatedLocation.sourceUrl == generatedLocation.sourceUrl &&
+      bp.generatedLocation.line == generatedLocation.line &&
+      bp.generatedLocation.column == generatedLocation.column
+    );
+  });
+}
+
 // we have three forms of syncing: disabled syncing, existing server syncing
 // and adding a new breakpoint
 export async function syncBreakpointPromise(
   thunkArgs: ThunkArgs,
   sourceId: SourceId,
   pendingBreakpoint: PendingBreakpoint
 ): Promise<?BreakpointSyncData> {
-  const { getState, client } = thunkArgs;
+  const { getState, client, dispatch } = thunkArgs;
   assertPendingBreakpoint(pendingBreakpoint);
 
   const source = getSource(getState(), sourceId);
 
   const generatedSourceId = isOriginalId(sourceId)
     ? originalToGeneratedId(sourceId)
     : sourceId;
 
@@ -147,18 +161,21 @@ export async function syncBreakpointProm
       newLocation,
       newGeneratedLocation,
       previousLocation,
       text,
       originalText
     );
   }
 
-  // clear server breakpoints if they exist and we have moved
-  await client.removeBreakpoint(generatedLocation);
+  // Clear any breakpoint for the generated location.
+  const bp = findExistingBreakpoint(getState(), generatedLocation);
+  if (bp) {
+    await dispatch(removeBreakpoint(bp));
+  }
 
   if (!newGeneratedLocation) {
     return { previousLocation, breakpoint: null };
   }
 
   /** ******* Case 2: Add New Breakpoint ***********/
   // If we are not disabled, set the breakpoint on the server and get
   // that info so we can set it on our breakpoints.
--- a/devtools/client/debugger/new/src/actions/sources/loadSourceText.js
+++ b/devtools/client/debugger/new/src/actions/sources/loadSourceText.js
@@ -1,97 +1,128 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
 import { PROMISE } from "../utils/middleware/promise";
-import { getSource } from "../../selectors";
+import { getSource, getSourcesEpoch } from "../../selectors";
 import { setBreakpointPositions } from "../breakpoints";
 
 import * as parser from "../../workers/parser";
 import { isLoaded, isOriginal } from "../../utils/source";
 import { Telemetry } from "devtools-modules";
 
-import defer from "../../utils/defer";
 import type { ThunkArgs } from "../types";
 
 import type { Source } from "../../types";
 
 const requests = new Map();
 
 // Measures the time it takes for a source to load
 const loadSourceHistogram = "DEVTOOLS_DEBUGGER_LOAD_SOURCE_MS";
 const telemetry = new Telemetry();
 
-async function loadSource(state, source: Source, { sourceMaps, client }) {
+async function loadSource(
+  state,
+  source: Source,
+  { sourceMaps, client }
+): Promise<?{
+  text: string,
+  contentType: string
+}> {
   if (isOriginal(source)) {
-    return sourceMaps.getOriginalSourceText(source);
+    const result = await sourceMaps.getOriginalSourceText(source);
+    if (!result) {
+      // TODO: This allows pretty files to continue working the way they have
+      // been, but is very ugly. Remove this when we centralize pretty-printing
+      // in loadSource. https://github.com/firefox-devtools/debugger/issues/8071
+      if (source.isPrettyPrinted) {
+        return null;
+      }
+
+      // The way we currently try to load and select a pending
+      // selected location, it is possible that we will try to fetch the
+      // original source text right after the source map has been cleared
+      // after a navigation event.
+      throw new Error("Original source text unavailable");
+    }
+    return result;
   }
 
   if (!source.actors.length) {
     throw new Error("No source actor for loadSource");
   }
 
+  telemetry.start(loadSourceHistogram, source);
   const response = await client.sourceContents(source.actors[0]);
   telemetry.finish(loadSourceHistogram, source);
 
   return {
-    id: source.id,
     text: response.source,
     contentType: response.contentType || "text/javascript"
   };
 }
 
+async function loadSourceTextPromise(
+  source: Source,
+  epoch: number,
+  { dispatch, getState, client, sourceMaps }: ThunkArgs
+): Promise<?Source> {
+  if (isLoaded(source)) {
+    return source;
+  }
+
+  await dispatch({
+    type: "LOAD_SOURCE_TEXT",
+    sourceId: source.id,
+    epoch,
+    [PROMISE]: loadSource(getState(), source, { sourceMaps, client })
+  });
+
+  const newSource = getSource(getState(), source.id);
+  if (!newSource) {
+    return;
+  }
+
+  if (!newSource.isWasm && isLoaded(newSource)) {
+    parser.setSource(newSource);
+    await dispatch(setBreakpointPositions(newSource.id));
+  }
+
+  return newSource;
+}
+
 /**
  * @memberof actions/sources
  * @static
  */
-export function loadSourceText(source: ?Source) {
-  return async ({ dispatch, getState, client, sourceMaps }: ThunkArgs) => {
-    if (!source) {
+export function loadSourceText(inputSource: ?Source) {
+  return async (thunkArgs: ThunkArgs) => {
+    if (!inputSource) {
       return;
     }
+    // This ensures that the falsy check above is preserved into the IIFE
+    // below in a way that Flow is happy with.
+    const source = inputSource;
 
-    const id = source.id;
-    // Fetch the source text only once.
-    if (requests.has(id)) {
-      return requests.get(id);
-    }
+    const epoch = getSourcesEpoch(thunkArgs.getState());
 
-    if (isLoaded(source)) {
-      return Promise.resolve();
+    const id = `${epoch}:${source.id}`;
+    let promise = requests.get(id);
+    if (!promise) {
+      promise = (async () => {
+        try {
+          return await loadSourceTextPromise(source, epoch, thunkArgs);
+        } catch (e) {
+          // TODO: This swallows errors for now. Ideally we would get rid of
+          // this once we have a better handle on our async state management.
+        } finally {
+          requests.delete(id);
+        }
+      })();
+      requests.set(id, promise);
     }
 
-    const deferred = defer();
-    requests.set(id, deferred.promise);
-
-    telemetry.start(loadSourceHistogram, source);
-    try {
-      await dispatch({
-        type: "LOAD_SOURCE_TEXT",
-        sourceId: source.id,
-        [PROMISE]: loadSource(getState(), source, { sourceMaps, client })
-      });
-    } catch (e) {
-      deferred.resolve();
-      requests.delete(id);
-      return;
-    }
-
-    const newSource = getSource(getState(), source.id);
-    if (!newSource) {
-      return;
-    }
-
-    if (!newSource.isWasm && isLoaded(newSource)) {
-      parser.setSource(newSource);
-      await dispatch(setBreakpointPositions(newSource.id));
-    }
-
-    // signal that the action is finished
-    deferred.resolve();
-    requests.delete(id);
-
-    return source;
+    return promise;
   };
 }
--- a/devtools/client/debugger/new/src/actions/sources/newSources.js
+++ b/devtools/client/debugger/new/src/actions/sources/newSources.js
@@ -8,17 +8,17 @@
  * Redux actions for the sources state
  * @module actions/sources
  */
 
 import { generatedToOriginalId } from "devtools-source-map";
 import { flatten } from "lodash";
 
 import { toggleBlackBox } from "./blackbox";
-import { syncBreakpoint } from "../breakpoints";
+import { syncBreakpoint, addBreakpoint } from "../breakpoints";
 import { loadSourceText } from "./loadSourceText";
 import { togglePrettyPrint } from "./prettyPrint";
 import { selectLocation } from "../sources";
 import { getRawSourceURL, isPrettyURL, isOriginal } from "../../utils/source";
 import {
   getBlackBoxList,
   getSource,
   getPendingSelectedLocation,
@@ -181,18 +181,29 @@ function checkPendingBreakpoints(sourceI
 
     if (pendingBreakpoints.length === 0) {
       return;
     }
 
     // load the source text if there is a pending breakpoint for it
     await dispatch(loadSourceText(source));
 
+    // Matching pending breakpoints could have either the same generated or the
+    // same original source. We expect the generated source to appear first and
+    // will add a breakpoint at that location initially. If the original source
+    // appears later then we use syncBreakpoint to see if the generated location
+    // changed and we need to remove the breakpoint we added earlier.
     await Promise.all(
-      pendingBreakpoints.map(bp => dispatch(syncBreakpoint(sourceId, bp)))
+      pendingBreakpoints.map(bp => {
+        if (source.url == bp.location.sourceUrl) {
+          return dispatch(syncBreakpoint(sourceId, bp));
+        }
+        const { line, column } = bp.generatedLocation;
+        return dispatch(addBreakpoint({ sourceId, line, column }, bp.options));
+      })
     );
   };
 }
 
 function restoreBlackBoxedSources(sources: Source[]) {
   return async ({ dispatch }: ThunkArgs) => {
     const tabs = getBlackBoxList();
     if (tabs.length == 0) {
--- a/devtools/client/debugger/new/src/actions/types/SourceAction.js
+++ b/devtools/client/debugger/new/src/actions/types/SourceAction.js
@@ -5,17 +5,18 @@
 // @flow
 
 import type { Source, SourceLocation } from "../../types";
 import type { PromiseAction } from "../utils/middleware/promise";
 
 export type LoadSourceAction = PromiseAction<
   {|
     +type: "LOAD_SOURCE_TEXT",
-    +sourceId: string
+    +sourceId: string,
+    +epoch: number
   |},
   Source
 >;
 export type SourceAction =
   | LoadSourceAction
   | {|
       +type: "ADD_SOURCE",
       +source: Source
--- a/devtools/client/debugger/new/src/components/Editor/Tab.js
+++ b/devtools/client/debugger/new/src/components/Editor/Tab.js
@@ -18,16 +18,17 @@ import type { Source } from "../../types
 import actions from "../../actions";
 
 import {
   getDisplayPath,
   getFileURL,
   getRawSourceURL,
   getSourceQueryString,
   getTruncatedFileName,
+  isJavaScript,
   isPretty,
   shouldBlackbox
 } from "../../utils/source";
 import { copyToTheClipboard } from "../../utils/clipboard";
 import { getTabMenuItems } from "../../utils/tabs";
 
 import {
   getSelectedSource,
@@ -144,17 +145,17 @@ class Tab extends PureComponent<Props> {
           disabled: !shouldBlackbox(source),
           click: () => toggleBlackBox(source)
         }
       },
       {
         item: {
           ...tabMenuItems.prettyPrint,
           click: () => togglePrettyPrint(tab),
-          disabled: isPretty(sourceTab)
+          disabled: isPretty(source) || !isJavaScript(source)
         }
       }
     ];
 
     showMenu(e, buildMenu(items));
   }
 
   isProjectSearchEnabled() {
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/Breakpoints.css
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/Breakpoints.css
@@ -130,17 +130,18 @@ html .breakpoints-list .breakpoint.pause
 
 .breakpoints-list .breakpoint .breakpoint-line {
   font-size: 11px;
   color: var(--theme-comment);
   min-width: 16px;
   text-align: end;
 }
 
-.breakpoints-list .breakpoint:hover .breakpoint-line {
+.breakpoints-list .breakpoint:hover .breakpoint-line,
+.breakpoints-list .breakpoint-line-close:focus-within .breakpoint-line {
   color: transparent;
 }
 
 .breakpoints-list .breakpoint.paused:hover {
   border-color: var(--breakpoint-active-color-hover);
 }
 
 .breakpoints-list .breakpoint-label {
--- a/devtools/client/debugger/new/src/reducers/pending-breakpoints.js
+++ b/devtools/client/debugger/new/src/reducers/pending-breakpoints.js
@@ -156,14 +156,17 @@ export function getPendingBreakpointsFor
   source: Source
 ): PendingBreakpoint[] {
   const sources = getSourcesByURL(state, source.url);
   if (sources.length > 1 && isGenerated(source)) {
     // Don't return pending breakpoints for duplicated generated sources
     return [];
   }
 
-  return getPendingBreakpointList(state).filter(
-    pendingBreakpoint => pendingBreakpoint.location.sourceUrl === source.url
-  );
+  return getPendingBreakpointList(state).filter(pendingBreakpoint => {
+    return (
+      pendingBreakpoint.location.sourceUrl === source.url ||
+      pendingBreakpoint.generatedLocation.sourceUrl == source.url
+    );
+  });
 }
 
 export default update;
--- a/devtools/client/debugger/new/src/reducers/sources.js
+++ b/devtools/client/debugger/new/src/reducers/sources.js
@@ -31,16 +31,18 @@ import { mapValues, uniqBy, uniq } from 
 export type SourcesMap = { [SourceId]: Source };
 export type SourcesMapByThread = { [ThreadId]: SourcesMap };
 
 type UrlsMap = { [string]: SourceId[] };
 type DisplayedSources = { [ThreadId]: { [SourceId]: boolean } };
 type GetDisplayedSourcesSelector = OuterState => { [ThreadId]: SourcesMap };
 
 export type SourcesState = {
+  epoch: number,
+
   // All known sources.
   sources: SourcesMap,
 
   // All sources associated with a given URL. When using source maps, multiple
   // sources can have the same URL.
   urls: UrlsMap,
 
   // For each thread, all sources in that thread that are under the project root
@@ -58,16 +60,17 @@ const emptySources = {
   sources: {},
   urls: {},
   displayed: {}
 };
 
 export function initialSourcesState(): SourcesState {
   return {
     ...emptySources,
+    epoch: 1,
     selectedLocation: undefined,
     pendingSelectedLocation: prefs.pendingSelectedLocation,
     projectDirectoryRoot: prefs.projectDirectoryRoot,
     chromeAndExtenstionsEnabled: prefs.chromeAndExtenstionsEnabled,
     focusedItem: null
   };
 }
 
@@ -159,17 +162,20 @@ function update(
         return updateSource(state, { id, isBlackBoxed });
       }
       break;
 
     case "SET_PROJECT_DIRECTORY_ROOT":
       return updateProjectDirectoryRoot(state, action.url);
 
     case "NAVIGATE":
-      return initialSourcesState();
+      return {
+        ...initialSourcesState(),
+        epoch: state.epoch + 1
+      };
 
     case "SET_FOCUSED_SOURCE_ITEM":
       return { ...state, focusedItem: action.item };
   }
 
   return state;
 }
 
@@ -291,25 +297,36 @@ function updateProjectDirectoryRoot(stat
     relativeUrl: getRelativeUrl(source, root)
   }));
 }
 
 /*
  * Update a source's loaded state fields
  * i.e. loadedState, text, error
  */
-function updateLoadedState(state, action: LoadSourceAction): SourcesState {
+function updateLoadedState(
+  state: SourcesState,
+  action: LoadSourceAction
+): SourcesState {
   const { sourceId } = action;
   let source;
 
+  // If there was a navigation between the time the action was started and
+  // completed, we don't want to update the store.
+  if (action.epoch !== state.epoch) {
+    return state;
+  }
+
   if (action.status === "start") {
     source = { id: sourceId, loadedState: "loading" };
   } else if (action.status === "error") {
     source = { id: sourceId, error: action.error, loadedState: "loaded" };
   } else {
+    // TODO: Remove this once we centralize pretty-print and this can no longer
+    // return a null value when loading a in-progress prettyprinting file.
     if (!action.value) {
       return state;
     }
 
     source = {
       id: sourceId,
       text: action.value.text,
       contentType: action.value.contentType,
@@ -517,16 +534,20 @@ export function getHasSiblingOfSameName(
 
   return getSourcesUrlsInSources(state, source.url).length > 1;
 }
 
 export function getSources(state: OuterState) {
   return state.sources.sources;
 }
 
+export function getSourcesEpoch(state: OuterState) {
+  return state.sources.epoch;
+}
+
 export function getUrls(state: OuterState) {
   return state.sources.urls;
 }
 
 export function getSourceList(state: OuterState): Source[] {
   return (Object.values(getSources(state)): any);
 }
 
--- a/devtools/client/debugger/new/src/selectors/visibleColumnBreakpoints.js
+++ b/devtools/client/debugger/new/src/selectors/visibleColumnBreakpoints.js
@@ -168,24 +168,8 @@ export function getFirstBreakpointPositi
   if (!source || !positions) {
     return;
   }
 
   return positions.find(
     position => getSelectedLocation(position, source).line == line
   );
 }
-
-export function getFirstVisibleBreakpointPosition(
-  state: State,
-  { line }: SourceLocation
-) {
-  const positions = getVisibleBreakpointPositions(state);
-  const selectedSource = getSelectedSource(state);
-
-  if (!selectedSource || !positions) {
-    return;
-  }
-
-  return positions.find(
-    position => getSelectedLocation(position, selectedSource).line == line
-  );
-}
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-scroll-run-to-completion.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-scroll-run-to-completion.js
@@ -3,17 +3,17 @@
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 add_task(async function() {
   const dbg = await initDebugger("doc-scroll-run-to-completion.html");
   invokeInTab("pauseOnce", "doc-scroll-run-to-completion.html");
   await waitForPaused(dbg);
   assertPausedLocation(dbg);
 
-  const threadClient = dbg.toolbox.threadClient;
-  await checkEvaluateInTopFrame(threadClient, 'window.scrollBy(0, 10);', undefined);
+  const target = dbg.toolbox.target;
+  await checkEvaluateInTopFrame(target, 'window.scrollBy(0, 10);', undefined);
 
   // checkEvaluateInTopFrame does an implicit resume for some reason.
   await waitForPaused(dbg);
 
   resume(dbg);
   await once(Services.ppmm, "test passed");
 });
--- a/devtools/client/debugger/new/test/mochitest/helpers.js
+++ b/devtools/client/debugger/new/test/mochitest/helpers.js
@@ -1593,32 +1593,24 @@ function hideConsoleContextMenu(hud) {
 
   const onPopupHidden = once(popup, "popuphidden");
   popup.hidePopup();
   return onPopupHidden;
 }
 
 // Return a promise that resolves with the result of a thread evaluating a
 // string in the topmost frame.
-async function evaluateInTopFrame(threadClient, text) {
+async function evaluateInTopFrame(target, text) {
+  const threadClient = target.threadClient;
+  const consoleFront = await target.getFront("console");
   const { frames } = await threadClient.getFrames(0, 1);
   ok(frames.length == 1, "Got one frame");
-  const response = await threadClient.eval(frames[0].actor, text);
-  ok(response.type == "resumed", "Got resume response from eval");
-  let rval;
-  await threadClient.addOneTimeListener("paused", function(event, packet) {
-    ok(
-      packet.type == "paused" &&
-        packet.why.type == "clientEvaluated" &&
-        "return" in packet.why.frameFinished,
-      "Eval returned a value"
-    );
-    rval = packet.why.frameFinished.return;
-  });
-  return rval.type == "undefined" ? undefined : rval;
+  const options = { thread: threadClient.actor, frameActor: frames[0].actor };
+  const response = await consoleFront.evaluateJS(text, options);
+  return response.result.type == "undefined" ? undefined : response.result;
 }
 
 // Return a promise that resolves when a thread evaluates a string in the
 // topmost frame, ensuring the result matches the expected value.
-async function checkEvaluateInTopFrame(threadClient, text, expected) {
-  const rval = await evaluateInTopFrame(threadClient, text);
+async function checkEvaluateInTopFrame(target, text, expected) {
+  const rval = await evaluateInTopFrame(target, text);
   ok(rval == expected, `Eval returned ${expected}`);
 }
--- a/devtools/client/netmonitor/test/browser_net_autoscroll.js
+++ b/devtools/client/netmonitor/test/browser_net_autoscroll.js
@@ -49,17 +49,18 @@ add_task(async function() {
 
   // (4) Now select the first item in the list
   // and check that additional requests do not change the scroll position
   // from just below the headers.
   store.dispatch(Actions.selectRequestByIndex(0));
   await waitForNetworkEvents(monitor, 8);
   await waitSomeTime();
   const requestsContainerHeaders = document.querySelector(".requests-list-headers");
-  const headersHeight = requestsContainerHeaders.offsetHeight;
+  const headersHeight =
+    Math.floor(requestsContainerHeaders.getBoundingClientRect().height);
   is(requestsContainer.scrollTop, headersHeight, "Did not scroll.");
 
   // Stop doing requests.
   await ContentTask.spawn(tab.linkedBrowser, {}, function() {
     content.wrappedJSObject.stopRequests();
   });
 
   // Done: clean up.
--- a/devtools/client/responsive.html/components/App.js
+++ b/devtools/client/responsive.html/components/App.js
@@ -54,16 +54,17 @@ class App extends PureComponent {
       viewports: PropTypes.arrayOf(PropTypes.shape(Types.viewport)).isRequired,
     };
   }
 
   constructor(props) {
     super(props);
 
     this.onAddCustomDevice = this.onAddCustomDevice.bind(this);
+    this.onBrowserContextMenu = this.onBrowserContextMenu.bind(this);
     this.onBrowserMounted = this.onBrowserMounted.bind(this);
     this.onChangeDevice = this.onChangeDevice.bind(this);
     this.onChangeNetworkThrottling = this.onChangeNetworkThrottling.bind(this);
     this.onChangePixelRatio = this.onChangePixelRatio.bind(this);
     this.onChangeTouchSimulation = this.onChangeTouchSimulation.bind(this);
     this.onChangeUserAgent = this.onChangeUserAgent.bind(this);
     this.onContentResize = this.onContentResize.bind(this);
     this.onDeviceListUpdate = this.onDeviceListUpdate.bind(this);
@@ -77,21 +78,34 @@ class App extends PureComponent {
     this.onToggleReloadOnTouchSimulation =
       this.onToggleReloadOnTouchSimulation.bind(this);
     this.onToggleReloadOnUserAgent = this.onToggleReloadOnUserAgent.bind(this);
     this.onToggleUserAgentInput = this.onToggleUserAgentInput.bind(this);
     this.onUpdateDeviceDisplayed = this.onUpdateDeviceDisplayed.bind(this);
     this.onUpdateDeviceModal = this.onUpdateDeviceModal.bind(this);
   }
 
+  componentWillUnmount() {
+    this.browser.removeEventListener("contextmenu", this.onContextMenu);
+    this.browser = null;
+  }
+
   onAddCustomDevice(device) {
     this.props.dispatch(addCustomDevice(device));
   }
 
-  onBrowserMounted() {
+  onBrowserContextMenu() {
+    // Update the position of remote browser so that makes the context menu to show at
+    // proper position before showing.
+    this.browser.frameLoader.requestUpdatePosition();
+  }
+
+  onBrowserMounted(browser) {
+    this.browser = browser;
+    this.browser.addEventListener("contextmenu", this.onBrowserContextMenu);
     window.postMessage({ type: "browser-mounted" }, "*");
   }
 
   onChangeDevice(id, device, deviceType) {
     // TODO: Bug 1332754: Move messaging and logic into the action creator so that the
     // message is sent from the action creator and device property changes are sent from
     // there instead of this function.
     window.postMessage({
--- a/devtools/client/responsive.html/components/Browser.js
+++ b/devtools/client/responsive.html/components/Browser.js
@@ -67,17 +67,17 @@ class Browser extends PureComponent {
     // script now.
     if (!this.props.swapAfterMount) {
       await this.startFrameScript();
     }
 
     // Notify manager.js that this browser has mounted, so that it can trigger
     // a swap if needed and continue with the rest of its startup.
     await this.browserShown;
-    this.props.onBrowserMounted();
+    this.props.onBrowserMounted(this.browser);
 
     // If we are swapping browsers after mount, wait for the swap to complete
     // and start the frame script after that.
     if (this.props.swapAfterMount) {
       await message.wait(window, "start-frame-script");
       await this.startFrameScript();
       message.post(window, "start-frame-script:done");
     }
--- a/devtools/client/shared/components/SearchBox.js
+++ b/devtools/client/shared/components/SearchBox.js
@@ -1,89 +1,95 @@
 /* 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/. */
 
 /* global window */
 
 "use strict";
 
-const { Component, createFactory } = require("devtools/client/shared/vendor/react");
+const { createFactory, createRef, PureComponent } = require("devtools/client/shared/vendor/react");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
-const KeyShortcuts = require("devtools/client/shared/key-shortcuts");
-const AutocompletePopup = createFactory(require("devtools/client/shared/components/AutoCompletePopup"));
 
+loader.lazyGetter(this, "AutocompletePopup", function() {
+  return createFactory(require("devtools/client/shared/components/AutoCompletePopup"));
+});
 loader.lazyGetter(this, "MDNLink", function() {
   return createFactory(require("./MdnLink"));
 });
 
-class SearchBox extends Component {
+loader.lazyRequireGetter(this, "KeyShortcuts", "devtools/client/shared/key-shortcuts");
+
+class SearchBox extends PureComponent {
   static get propTypes() {
     return {
+      autocompleteProvider: PropTypes.func,
       delay: PropTypes.number,
       keyShortcut: PropTypes.string,
-      onChange: PropTypes.func,
+      learnMoreTitle: PropTypes.string,
+      learnMoreUrl: PropTypes.string,
+      onBlur: PropTypes.func,
+      onChange: PropTypes.func.isRequired,
       onFocus: PropTypes.func,
-      onBlur: PropTypes.func,
       onKeyDown: PropTypes.func,
-      placeholder: PropTypes.string,
+      placeholder: PropTypes.string.isRequired,
       plainStyle: PropTypes.bool,
-      type: PropTypes.string,
-      autocompleteProvider: PropTypes.func,
-      learnMoreUrl: PropTypes.string,
-      learnMoreTitle: PropTypes.string,
+      type: PropTypes.string.isRequired,
     };
   }
 
   constructor(props) {
     super(props);
 
     this.state = {
       value: "",
       focused: false,
     };
 
+    this.autocompleteRef = createRef();
+    this.inputRef = createRef();
+
+    this.onBlur = this.onBlur.bind(this);
     this.onChange = this.onChange.bind(this);
     this.onClearButtonClick = this.onClearButtonClick.bind(this);
     this.onFocus = this.onFocus.bind(this);
-    this.onBlur = this.onBlur.bind(this);
     this.onKeyDown = this.onKeyDown.bind(this);
   }
 
   componentDidMount() {
     if (!this.props.keyShortcut) {
       return;
     }
 
     this.shortcuts = new KeyShortcuts({
       window,
     });
     this.shortcuts.on(this.props.keyShortcut, event => {
       event.preventDefault();
-      this.refs.input.focus();
+      this.inputRef.current.focus();
     });
   }
 
   componentWillUnmount() {
     if (this.shortcuts) {
       this.shortcuts.destroy();
     }
 
     // Clean up an existing timeout.
     if (this.searchTimeout) {
       clearTimeout(this.searchTimeout);
     }
   }
 
   onChange() {
-    if (this.state.value !== this.refs.input.value) {
+    if (this.state.value !== this.inputRef.current.value) {
       this.setState({
         focused: true,
-        value: this.refs.input.value,
+        value: this.inputRef.current.value,
       });
     }
 
     if (!this.props.delay) {
       this.props.onChange(this.state.value);
       return;
     }
 
@@ -96,17 +102,17 @@ class SearchBox extends Component {
     // smoother if the user is typing quickly.
     this.searchTimeout = setTimeout(() => {
       this.searchTimeout = null;
       this.props.onChange(this.state.value);
     }, this.props.delay);
   }
 
   onClearButtonClick() {
-    this.refs.input.value = "";
+    this.setState({ value: "" });
     this.onChange();
   }
 
   onFocus() {
     if (this.props.onFocus) {
       this.props.onFocus();
     }
 
@@ -121,17 +127,17 @@ class SearchBox extends Component {
     this.setState({ focused: false });
   }
 
   onKeyDown(e) {
     if (this.props.onKeyDown) {
       this.props.onKeyDown();
     }
 
-    const { autocomplete } = this.refs;
+    const autocomplete = this.autocompleteRef.current;
     if (!autocomplete || autocomplete.state.list.length <= 0) {
       return;
     }
 
     switch (e.key) {
       case "ArrowDown":
         autocomplete.jumpBy(1);
         break;
@@ -159,63 +165,62 @@ class SearchBox extends Component {
       case "End":
         autocomplete.jumpToBottom();
         break;
     }
   }
 
   render() {
     let {
-      type = "search",
+      autocompleteProvider,
+      learnMoreTitle,
+      learnMoreUrl,
       placeholder,
-      autocompleteProvider,
       plainStyle,
-      learnMoreUrl,
-      learnMoreTitle,
+      type = "search",
     } = this.props;
     const { value } = this.state;
-    const divClassList = ["devtools-searchbox", "has-clear-btn"];
+    const showAutocomplete = autocompleteProvider && this.state.focused && value !== "";
+
     const inputClassList = [`devtools-${type}input`];
     if (plainStyle) {
       inputClassList.push("devtools-plaininput");
     }
-    const showAutocomplete = autocompleteProvider && this.state.focused && value !== "";
-
     if (value !== "") {
       inputClassList.push("filled");
       learnMoreUrl = false;
     }
 
     return dom.div(
-      { className: divClassList.join(" ") },
+      { className: "devtools-searchbox has-clear-btn" },
       dom.input({
         className: inputClassList.join(" "),
+        onBlur: this.onBlur,
         onChange: this.onChange,
         onFocus: this.onFocus,
-        onBlur: this.onBlur,
         onKeyDown: this.onKeyDown,
         placeholder,
-        ref: "input",
+        ref: this.inputRef,
         value,
       }),
       dom.button({
         className: "devtools-searchinput-clear",
-        hidden: value == "",
+        hidden: value === "",
         onClick: this.onClearButtonClick,
       }),
       learnMoreUrl && MDNLink({
+        title: learnMoreTitle,
         url: learnMoreUrl,
-        title: learnMoreTitle,
       }),
       showAutocomplete && AutocompletePopup({
         autocompleteProvider,
         filter: value,
-        ref: "autocomplete",
         onItemSelected: (itemValue) => {
           this.setState({ value: itemValue });
           this.onChange();
         },
+        ref: this.autocompleteRef,
       })
     );
   }
 }
 
 module.exports = SearchBox;
--- a/devtools/client/themes/toolbars.css
+++ b/devtools/client/themes/toolbars.css
@@ -29,16 +29,21 @@
   line-height: 24px;
   box-sizing: border-box;
 }
 
 .devtools-toolbar {
   padding: 0 3px;
 }
 
+/* <thead> nor <tr> support border, we have to style the <td> */
+.devtools-toolbar td {
+  border-bottom: 1px solid var(--theme-splitter-color);
+}
+
 .devtools-toolbar-bottom {
   border-top-width: 1px;
   border-bottom: none;
 }
 
 /* Expected space around a separator:
  * -----------------------
  *            4
--- a/devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-01.js
+++ b/devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-01.js
@@ -7,37 +7,37 @@
 "use strict";
 
 // Test basic breakpoint functionality in web replay.
 add_task(async function() {
   const dbg = await attachRecordingDebugger(
     "doc_rr_basic.html",
     { waitForRecording: true }
   );
-  const {threadClient, tab, toolbox} = dbg;
+  const {threadClient, tab, toolbox, target} = dbg;
 
   const bp = await setBreakpoint(threadClient, "doc_rr_basic.html", 21);
 
   // Visit a lot of breakpoints so that we are sure we have crossed major
   // checkpoint boundaries.
   await rewindToLine(threadClient, 21);
-  await checkEvaluateInTopFrame(threadClient, "number", 10);
+  await checkEvaluateInTopFrame(target, "number", 10);
   await rewindToLine(threadClient, 21);
-  await checkEvaluateInTopFrame(threadClient, "number", 9);
+  await checkEvaluateInTopFrame(target, "number", 9);
   await rewindToLine(threadClient, 21);
-  await checkEvaluateInTopFrame(threadClient, "number", 8);
+  await checkEvaluateInTopFrame(target, "number", 8);
   await rewindToLine(threadClient, 21);
-  await checkEvaluateInTopFrame(threadClient, "number", 7);
+  await checkEvaluateInTopFrame(target, "number", 7);
   await rewindToLine(threadClient, 21);
-  await checkEvaluateInTopFrame(threadClient, "number", 6);
+  await checkEvaluateInTopFrame(target, "number", 6);
   await resumeToLine(threadClient, 21);
-  await checkEvaluateInTopFrame(threadClient, "number", 7);
+  await checkEvaluateInTopFrame(target, "number", 7);
   await resumeToLine(threadClient, 21);
-  await checkEvaluateInTopFrame(threadClient, "number", 8);
+  await checkEvaluateInTopFrame(target, "number", 8);
   await resumeToLine(threadClient, 21);
-  await checkEvaluateInTopFrame(threadClient, "number", 9);
+  await checkEvaluateInTopFrame(target, "number", 9);
   await resumeToLine(threadClient, 21);
-  await checkEvaluateInTopFrame(threadClient, "number", 10);
+  await checkEvaluateInTopFrame(target, "number", 10);
 
   await threadClient.removeBreakpoint(bp);
   await toolbox.closeToolbox();
   await gBrowser.removeTab(tab);
 });
--- a/devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-02.js
+++ b/devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-02.js
@@ -5,23 +5,23 @@
 /* eslint-disable no-undef */
 
 "use strict";
 
 // Test unhandled divergence while evaluating at a breakpoint with Web Replay.
 add_task(async function() {
   const dbg = await attachRecordingDebugger("doc_rr_basic.html",
                                             { waitForRecording: true });
-  const {threadClient, tab, toolbox} = dbg;
+  const {threadClient, tab, toolbox, target} = dbg;
 
   const bp = await setBreakpoint(threadClient, "doc_rr_basic.html", 21);
   await rewindToLine(threadClient, 21);
-  await checkEvaluateInTopFrame(threadClient, "number", 10);
-  await checkEvaluateInTopFrameThrows(threadClient, "window.alert(3)");
-  await checkEvaluateInTopFrame(threadClient, "number", 10);
-  await checkEvaluateInTopFrameThrows(threadClient, "window.alert(3)");
-  await checkEvaluateInTopFrame(threadClient, "number", 10);
-  await checkEvaluateInTopFrame(threadClient, "testStepping2()", undefined);
+  await checkEvaluateInTopFrame(target, "number", 10);
+  await checkEvaluateInTopFrameThrows(target, "window.alert(3)");
+  await checkEvaluateInTopFrame(target, "number", 10);
+  await checkEvaluateInTopFrameThrows(target, "window.alert(3)");
+  await checkEvaluateInTopFrame(target, "number", 10);
+  await checkEvaluateInTopFrame(target, "testStepping2()", undefined);
 
   await threadClient.removeBreakpoint(bp);
   await toolbox.destroy();
   await gBrowser.removeTab(tab);
 });
--- a/devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-03.js
+++ b/devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-03.js
@@ -4,23 +4,23 @@
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 /* eslint-disable no-undef */
 
 "use strict";
 
 // Test some issues when stepping around after hitting a breakpoint while recording.
 add_task(async function() {
   const dbg = await attachRecordingDebugger("doc_rr_continuous.html");
-  const {threadClient, tab, toolbox} = dbg;
+  const {threadClient, tab, toolbox, target} = dbg;
 
   await threadClient.interrupt();
   const bp1 = await setBreakpoint(threadClient, "doc_rr_continuous.html", 19);
   await resumeToLine(threadClient, 19);
   await reverseStepOverToLine(threadClient, 18);
-  await checkEvaluateInTopFrame(threadClient,
+  await checkEvaluateInTopFrame(target,
     "SpecialPowers.Cu.recordReplayDirective(/* AlwaysTakeTemporarySnapshots */ 3)",
     undefined);
   await stepInToLine(threadClient, 22);
   const bp2 = await setBreakpoint(threadClient, "doc_rr_continuous.html", 24);
   await resumeToLine(threadClient, 24);
   const bp3 = await setBreakpoint(threadClient, "doc_rr_continuous.html", 22);
   await rewindToLine(threadClient, 22);
 
--- a/devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-04.js
+++ b/devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-04.js
@@ -5,30 +5,30 @@
 /* eslint-disable no-undef */
 
 "use strict";
 
 // Test navigating back to earlier breakpoints while recording, then resuming
 // recording.
 add_task(async function() {
   const dbg = await attachRecordingDebugger("doc_rr_continuous.html");
-  const {threadClient, tab, toolbox} = dbg;
+  const {threadClient, tab, toolbox, target} = dbg;
 
   const bp = await setBreakpoint(threadClient, "doc_rr_continuous.html", 14);
   await resumeToLine(threadClient, 14);
-  const value = await evaluateInTopFrame(threadClient, "number");
+  const value = await evaluateInTopFrame(target, "number");
   await resumeToLine(threadClient, 14);
-  await checkEvaluateInTopFrame(threadClient, "number", value + 1);
+  await checkEvaluateInTopFrame(target, "number", value + 1);
   await rewindToLine(threadClient, 14);
-  await checkEvaluateInTopFrame(threadClient, "number", value);
+  await checkEvaluateInTopFrame(target, "number", value);
   await resumeToLine(threadClient, 14);
-  await checkEvaluateInTopFrame(threadClient, "number", value + 1);
+  await checkEvaluateInTopFrame(target, "number", value + 1);
   await resumeToLine(threadClient, 14);
-  await checkEvaluateInTopFrame(threadClient, "number", value + 2);
+  await checkEvaluateInTopFrame(target, "number", value + 2);
   await resumeToLine(threadClient, 14);
-  await checkEvaluateInTopFrame(threadClient, "number", value + 3);
+  await checkEvaluateInTopFrame(target, "number", value + 3);
   await rewindToLine(threadClient, 14);
-  await checkEvaluateInTopFrame(threadClient, "number", value + 2);
+  await checkEvaluateInTopFrame(target, "number", value + 2);
 
   await threadClient.removeBreakpoint(bp);
   await toolbox.destroy();
   await gBrowser.removeTab(tab);
 });
--- a/devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-05.js
+++ b/devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-05.js
@@ -9,23 +9,23 @@
 // Test hitting breakpoints when rewinding past the point where the breakpoint
 // script was created.
 add_task(async function() {
   const dbg = await attachRecordingDebugger(
     "doc_rr_basic.html",
     { waitForRecording: true }
   );
 
-  const {threadClient, tab, toolbox} = dbg;
+  const {threadClient, tab, toolbox, target} = dbg;
 
   // Rewind to the beginning of the recording.
   await rewindToLine(threadClient, undefined);
 
   const bp = await setBreakpoint(threadClient, "doc_rr_basic.html", 21);
   await resumeToLine(threadClient, 21);
-  await checkEvaluateInTopFrame(threadClient, "number", 1);
+  await checkEvaluateInTopFrame(target, "number", 1);
   await resumeToLine(threadClient, 21);
-  await checkEvaluateInTopFrame(threadClient, "number", 2);
+  await checkEvaluateInTopFrame(target, "number", 2);
 
   await threadClient.removeBreakpoint(bp);
   await toolbox.destroy();
   await gBrowser.removeTab(tab);
 });
--- a/devtools/client/webreplay/mochitest/browser_dbg_rr_console_warp-01.js
+++ b/devtools/client/webreplay/mochitest/browser_dbg_rr_console_warp-01.js
@@ -8,31 +8,31 @@
 
 // Test basic console time warping functionality in web replay.
 add_task(async function() {
   const dbg = await attachRecordingDebugger(
     "doc_rr_error.html",
     { waitForRecording: true }
   );
 
-  const {tab, toolbox, threadClient} = dbg;
+  const {tab, toolbox, threadClient, target} = dbg;
   const console = await getDebuggerSplitConsole(dbg);
   const hud = console.hud;
 
   await warpToMessage(hud, dbg, "Number 5");
   await threadClient.interrupt();
 
-  await checkEvaluateInTopFrame(threadClient, "number", 5);
+  await checkEvaluateInTopFrame(target, "number", 5);
 
   // Initially we are paused inside the 'new Error()' call on line 19. The
   // first reverse step takes us to the start of that line.
   await reverseStepOverToLine(threadClient, 19);
   await reverseStepOverToLine(threadClient, 18);
   const bp = await setBreakpoint(threadClient, "doc_rr_error.html", 12);
   await rewindToLine(threadClient, 12);
-  await checkEvaluateInTopFrame(threadClient, "number", 4);
+  await checkEvaluateInTopFrame(target, "number", 4);
   await resumeToLine(threadClient, 12);
-  await checkEvaluateInTopFrame(threadClient, "number", 5);
+  await checkEvaluateInTopFrame(target, "number", 5);
 
   await threadClient.removeBreakpoint(bp);
   await toolbox.destroy();
   await gBrowser.removeTab(tab);
 });
--- a/devtools/client/webreplay/mochitest/browser_dbg_rr_logpoint-01.js
+++ b/devtools/client/webreplay/mochitest/browser_dbg_rr_logpoint-01.js
@@ -9,17 +9,17 @@
 // Test basic logpoint functionality in web replay. When logpoints are added,
 // new messages should appear in the correct order and allow time warping.
 add_task(async function() {
   const dbg = await attachRecordingDebugger(
     "doc_rr_basic.html",
     { waitForRecording: true }
   );
 
-  const {tab, toolbox, threadClient} = dbg;
+  const {tab, toolbox, threadClient, target} = dbg;
   const console = await getDebuggerSplitConsole(dbg);
   const hud = console.hud;
 
   const bp1 = await setBreakpoint(threadClient, "doc_rr_basic.html", 21,
                                   { logValue: `"Logpoint Number " + number` });
   const bp2 = await setBreakpoint(threadClient, "doc_rr_basic.html", 6,
                                   { logValue: `"Logpoint Beginning"` });
   const bp3 = await setBreakpoint(threadClient, "doc_rr_basic.html", 8,
@@ -30,17 +30,17 @@ add_task(async function() {
   for (let i = 1; i <= 10; i++) {
     ok(messages[i].textContent.includes("Number " + i));
   }
   ok(messages[11].textContent.includes("Ending"));
 
   await warpToMessage(hud, dbg, "Number 5");
   await threadClient.interrupt();
 
-  await checkEvaluateInTopFrame(threadClient, "number", 5);
+  await checkEvaluateInTopFrame(target, "number", 5);
   await reverseStepOverToLine(threadClient, 20);
 
   await threadClient.removeBreakpoint(bp1);
   await threadClient.removeBreakpoint(bp2);
   await threadClient.removeBreakpoint(bp3);
   await toolbox.destroy();
   await gBrowser.removeTab(tab);
 });
--- a/devtools/client/webreplay/mochitest/browser_dbg_rr_recovery-01.js
+++ b/devtools/client/webreplay/mochitest/browser_dbg_rr_recovery-01.js
@@ -8,24 +8,24 @@
 
 // Test basic recovery of crashed child processes in web replay.
 add_task(async function() {
   const tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" });
   gBrowser.selectedTab = tab;
   openTrustedLinkIn(EXAMPLE_URL + "doc_rr_recovery.html", "current");
   await once(Services.ppmm, "RecordingFinished");
 
-  const toolbox = await attachDebugger(tab), client = toolbox.threadClient;
+  const { target, toolbox } = await attachDebugger(tab), client = toolbox.threadClient;
   await client.interrupt();
   await setBreakpoint(client, "doc_rr_recovery.html", 21);
   await rewindToLine(client, 21);
   await checkEvaluateInTopFrame(client,
     "SpecialPowers.Cu.recordReplayDirective(/* CrashSoon */ 1)",
     undefined);
   await stepOverToLine(client, 22);
   await stepOverToLine(client, 23);
-  await checkEvaluateInTopFrame(client,
+  await checkEvaluateInTopFrame(target,
     "SpecialPowers.Cu.recordReplayDirective(/* CrashSoon */ 1); " +
     "SpecialPowers.Cu.recordReplayDirective(/* MaybeCrash */ 2)",
     undefined);
   await toolbox.destroy();
   await gBrowser.removeTab(tab);
 });
--- a/devtools/client/webreplay/mochitest/browser_dbg_rr_replay-01.js
+++ b/devtools/client/webreplay/mochitest/browser_dbg_rr_replay-01.js
@@ -20,23 +20,24 @@ add_task(async function() {
   ok(tabParent.saveRecording(recordingFile), "Saved recording");
   await once(Services.ppmm, "SaveRecordingFinished");
 
   const replayingTab = BrowserTestUtils.addTab(gBrowser, null,
                                                { replayExecution: recordingFile });
   gBrowser.selectedTab = replayingTab;
   await once(Services.ppmm, "HitRecordingEndpoint");
 
-  const toolbox = await attachDebugger(replayingTab), client = toolbox.threadClient;
+  const { target, toolbox } = await attachDebugger(replayingTab);
+  const client = toolbox.threadClient;
   await client.interrupt();
   const bp = await setBreakpoint(client, "doc_rr_basic.html", 21);
   await rewindToLine(client, 21);
-  await checkEvaluateInTopFrame(client, "number", 10);
+  await checkEvaluateInTopFrame(target, "number", 10);
   await rewindToLine(client, 21);
-  await checkEvaluateInTopFrame(client, "number", 9);
+  await checkEvaluateInTopFrame(target, "number", 9);
   await resumeToLine(client, 21);
-  await checkEvaluateInTopFrame(client, "number", 10);
+  await checkEvaluateInTopFrame(target, "number", 10);
 
   await client.removeBreakpoint(bp);
   await toolbox.destroy();
   await gBrowser.removeTab(recordingTab);
   await gBrowser.removeTab(replayingTab);
 });
--- a/devtools/client/webreplay/mochitest/browser_dbg_rr_replay-02.js
+++ b/devtools/client/webreplay/mochitest/browser_dbg_rr_replay-02.js
@@ -10,45 +10,50 @@
 add_task(async function() {
   waitForExplicitFinish();
 
   const recordingFile = newRecordingFile();
   const recordingTab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" });
   gBrowser.selectedTab = recordingTab;
   openTrustedLinkIn(EXAMPLE_URL + "doc_rr_continuous.html", "current");
 
-  let toolbox = await attachDebugger(recordingTab), client = toolbox.threadClient;
+  const firstTab = await attachDebugger(recordingTab);
+  let toolbox = firstTab.toolbox;
+  let target = firstTab.target;
+  let client = toolbox.threadClient;
   await client.interrupt();
   let bp = await setBreakpoint(client, "doc_rr_continuous.html", 14);
   await resumeToLine(client, 14);
   await resumeToLine(client, 14);
   await reverseStepOverToLine(client, 13);
-  const lastNumberValue = await evaluateInTopFrame(client, "number");
+  const lastNumberValue = await evaluateInTopFrame(target, "number");
 
   const tabParent = recordingTab.linkedBrowser.frameLoader.tabParent;
   ok(tabParent, "Found recording tab parent");
   ok(tabParent.saveRecording(recordingFile), "Saved recording");
   await once(Services.ppmm, "SaveRecordingFinished");
 
   await client.removeBreakpoint(bp);
   await toolbox.destroy();
   await gBrowser.removeTab(recordingTab);
 
   const replayingTab = BrowserTestUtils.addTab(gBrowser, null,
                                                { replayExecution: recordingFile });
   gBrowser.selectedTab = replayingTab;
   await once(Services.ppmm, "HitRecordingEndpoint");
 
-  toolbox = await attachDebugger(replayingTab);
+  const rplyTab = await attachDebugger(replayingTab);
+  toolbox = rplyTab.toolbox;
+  target = rplyTab.target;
   client = toolbox.threadClient;
   await client.interrupt();
-  await checkEvaluateInTopFrame(client, "number", lastNumberValue);
+  await checkEvaluateInTopFrame(target, "number", lastNumberValue);
   await reverseStepOverToLine(client, 13);
   bp = await setBreakpoint(client, "doc_rr_continuous.html", 14);
   await rewindToLine(client, 14);
-  await checkEvaluateInTopFrame(client, "number", lastNumberValue - 1);
+  await checkEvaluateInTopFrame(target, "number", lastNumberValue - 1);
   await resumeToLine(client, 14);
-  await checkEvaluateInTopFrame(client, "number", lastNumberValue);
+  await checkEvaluateInTopFrame(target, "number", lastNumberValue);
 
   await client.removeBreakpoint(bp);
   await toolbox.destroy();
   await gBrowser.removeTab(replayingTab);
 });
--- a/devtools/client/webreplay/mochitest/browser_dbg_rr_stepping-01.js
+++ b/devtools/client/webreplay/mochitest/browser_dbg_rr_stepping-01.js
@@ -8,23 +8,23 @@
 
 // Test basic step-over/back functionality in web replay.
 add_task(async function() {
   const tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" });
   gBrowser.selectedTab = tab;
   openTrustedLinkIn(EXAMPLE_URL + "doc_rr_basic.html", "current");
   await once(Services.ppmm, "RecordingFinished");
 
-  const toolbox = await attachDebugger(tab), client = toolbox.threadClient;
+  const { target, toolbox } = await attachDebugger(tab), client = toolbox.threadClient;
   await client.interrupt();
   const bp = await setBreakpoint(client, "doc_rr_basic.html", 21);
   await rewindToLine(client, 21);
-  await checkEvaluateInTopFrame(client, "number", 10);
+  await checkEvaluateInTopFrame(target, "number", 10);
   await reverseStepOverToLine(client, 20);
-  await checkEvaluateInTopFrame(client, "number", 9);
-  await checkEvaluateInTopFrameThrows(client, "window.alert(3)");
+  await checkEvaluateInTopFrame(target, "number", 9);
+  await checkEvaluateInTopFrameThrows(target, "window.alert(3)");
   await stepOverToLine(client, 21);
-  await checkEvaluateInTopFrame(client, "number", 10);
+  await checkEvaluateInTopFrame(target, "number", 10);
 
   await client.removeBreakpoint(bp);
   await toolbox.destroy();
   await gBrowser.removeTab(tab);
 });
--- a/devtools/client/webreplay/mochitest/browser_dbg_rr_stepping-02.js
+++ b/devtools/client/webreplay/mochitest/browser_dbg_rr_stepping-02.js
@@ -8,17 +8,17 @@
 
 // Test fixes for some simple stepping bugs.
 add_task(async function() {
   const tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" });
   gBrowser.selectedTab = tab;
   openTrustedLinkIn(EXAMPLE_URL + "doc_rr_basic.html", "current");
   await once(Services.ppmm, "RecordingFinished");
 
-  const toolbox = await attachDebugger(tab), client = toolbox.threadClient;
+  const { toolbox } = await attachDebugger(tab), client = toolbox.threadClient;
   await client.interrupt();
   const bp = await setBreakpoint(client, "doc_rr_basic.html", 22);
   await rewindToLine(client, 22);
   await stepInToLine(client, 25);
   await stepOverToLine(client, 26);
   await stepOverToLine(client, 27);
   await reverseStepInToLine(client, 33);
   await reverseStepOverToLine(client, 32);
--- a/devtools/client/webreplay/mochitest/browser_dbg_rr_stepping-03.js
+++ b/devtools/client/webreplay/mochitest/browser_dbg_rr_stepping-03.js
@@ -7,23 +7,24 @@
 "use strict";
 
 // Test stepping back while recording, then resuming recording.
 add_task(async function() {
   const tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" });
   gBrowser.selectedTab = tab;
   openTrustedLinkIn(EXAMPLE_URL + "doc_rr_continuous.html", "current");
 
-  const toolbox = await attachDebugger(tab), client = toolbox.threadClient;
+  const { toolbox, target } = await attachDebugger(tab);
+  const client = toolbox.threadClient;
   await client.interrupt();
   const bp = await setBreakpoint(client, "doc_rr_continuous.html", 13);
   await resumeToLine(client, 13);
-  const value = await evaluateInTopFrame(client, "number");
+  const value = await evaluateInTopFrame(target, "number");
   await reverseStepOverToLine(client, 12);
-  await checkEvaluateInTopFrame(client, "number", value - 1);
+  await checkEvaluateInTopFrame(target, "number", value - 1);
   await resumeToLine(client, 13);
   await resumeToLine(client, 13);
-  await checkEvaluateInTopFrame(client, "number", value + 1);
+  await checkEvaluateInTopFrame(target, "number", value + 1);
 
   await client.removeBreakpoint(bp);
   await toolbox.destroy();
   await gBrowser.removeTab(tab);
 });
--- a/devtools/client/webreplay/mochitest/browser_dbg_rr_stepping-04.js
+++ b/devtools/client/webreplay/mochitest/browser_dbg_rr_stepping-04.js
@@ -8,36 +8,36 @@
 
 // Stepping past the beginning or end of a frame should act like a step-out.
 add_task(async function() {
   const tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" });
   gBrowser.selectedTab = tab;
   openTrustedLinkIn(EXAMPLE_URL + "doc_rr_basic.html", "current");
   await once(Services.ppmm, "RecordingFinished");
 
-  const toolbox = await attachDebugger(tab), client = toolbox.threadClient;
+  const { target, toolbox } = await attachDebugger(tab), client = toolbox.threadClient;
   await client.interrupt();
   const bp = await setBreakpoint(client, "doc_rr_basic.html", 21);
   await rewindToLine(client, 21);
-  await checkEvaluateInTopFrame(client, "number", 10);
+  await checkEvaluateInTopFrame(target, "number", 10);
   await reverseStepOverToLine(client, 20);
   await reverseStepOverToLine(client, 12);
 
   // After reverse-stepping out of the topmost frame we should rewind to the
   // last breakpoint hit.
   await reverseStepOverToLine(client, 21);
-  await checkEvaluateInTopFrame(client, "number", 9);
+  await checkEvaluateInTopFrame(target, "number", 9);
 
   await stepOverToLine(client, 22);
   await stepOverToLine(client, 23);
   await stepOverToLine(client, 13);
   await stepOverToLine(client, 17);
   await stepOverToLine(client, 18);
 
   // After forward-stepping out of the topmost frame we should run forward to
   // the next breakpoint hit.
   await stepOverToLine(client, 21);
-  await checkEvaluateInTopFrame(client, "number", 10);
+  await checkEvaluateInTopFrame(target, "number", 10);
 
   await client.removeBreakpoint(bp);
   await toolbox.destroy();
   await gBrowser.removeTab(tab);
 });
--- a/devtools/client/webreplay/mochitest/head.js
+++ b/devtools/client/webreplay/mochitest/head.js
@@ -19,34 +19,34 @@ Services.scriptloader.loadSubScript(
 const EXAMPLE_URL =
   "http://example.com/browser/devtools/client/webreplay/mochitest/examples/";
 
 // Attach a debugger to a tab, returning a promise that resolves with the
 // debugger's toolbox.
 async function attachDebugger(tab) {
   const target = await TargetFactory.forTab(tab);
   const toolbox = await gDevTools.showToolbox(target, "jsdebugger");
-  return toolbox;
+  return { toolbox, target };
 }
 
 async function attachRecordingDebugger(url,
     { waitForRecording } = { waitForRecording: false }) {
   const tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" });
   gBrowser.selectedTab = tab;
   openTrustedLinkIn(EXAMPLE_URL + url, "current");
 
   if (waitForRecording) {
     await once(Services.ppmm, "RecordingFinished");
   }
-  const toolbox = await attachDebugger(tab);
+  const { target, toolbox } = await attachDebugger(tab);
   const dbg = createDebuggerContext(toolbox);
   const threadClient = dbg.toolbox.threadClient;
 
   await threadClient.interrupt();
-  return {...dbg, tab, threadClient};
+  return {...dbg, tab, threadClient, target};
 }
 
 // Return a promise that resolves when a breakpoint has been set.
 async function setBreakpoint(threadClient, expectedFile, lineno, options = {}) {
   const {sources} = await threadClient.getSources();
   ok(sources.length == 1, "Got one source");
   ok(RegExp(expectedFile).test(sources[0].url), "Source is " + expectedFile);
   const location = { sourceUrl: sources[0].url, line: lineno };
@@ -73,26 +73,24 @@ var reverseStepOverToLine = resumeThenPa
 var stepOverToLine = resumeThenPauseAtLineFunctionFactory("stepOver");
 var reverseStepInToLine = resumeThenPauseAtLineFunctionFactory("reverseStepIn");
 var stepInToLine = resumeThenPauseAtLineFunctionFactory("stepIn");
 var reverseStepOutToLine = resumeThenPauseAtLineFunctionFactory("reverseStepOut");
 var stepOutToLine = resumeThenPauseAtLineFunctionFactory("stepOut");
 
 // Return a promise that resolves when a thread evaluates a string in the
 // topmost frame, with the result throwing an exception.
-async function checkEvaluateInTopFrameThrows(threadClient, text) {
-  const {frames} = await threadClient.getFrames(0, 1);
+async function checkEvaluateInTopFrameThrows(target, text) {
+  const threadClient = target.threadClient;
+  const consoleFront = await target.getFront("console");
+  const { frames } = await threadClient.getFrames(0, 1);
   ok(frames.length == 1, "Got one frame");
-  const response = await threadClient.eval(frames[0].actor, text);
-  ok(response.type == "resumed", "Got resume response from eval");
-  await threadClient.addOneTimeListener("paused", function(event, packet) {
-    ok(packet.type == "paused" &&
-       packet.why.type == "clientEvaluated" &&
-       "throw" in packet.why.frameFinished, "Eval threw an exception");
-  });
+  const options = { thread: threadClient.actor, frameActor: frames[0].actor };
+  const response = await consoleFront.evaluateJS(text, options);
+  ok(response.exception, "Eval threw an exception");
 }
 
 // Return a pathname that can be used for a new recording file.
 function newRecordingFile() {
   ChromeUtils.import("resource://gre/modules/osfile.jsm", this);
   return OS.Path.join(OS.Constants.Path.tmpDir,
                       "MochitestRecording" + Math.round(Math.random() * 1000000000));
 }
--- a/devtools/server/actors/thread.js
+++ b/devtools/server/actors/thread.js
@@ -1025,52 +1025,16 @@ const ThreadActor = ActorClassWithSpec(t
       rewinding ? (frame.offset == frame.script.mainOffset) : frame.reportedPop;
     const stepFrame = endOfFrame ? frame.older : frame;
     if (!stepFrame || !stepFrame.script) {
       return null;
     }
     return stepFrame;
   },
 
-  onClientEvaluate: function(request) {
-    if (this.state !== "paused") {
-      return { error: "wrongState",
-               message: "Debuggee must be paused to evaluate code." };
-    }
-
-    const frame = this._requestFrame(request.frame);
-    if (!frame) {
-      return { error: "unknownFrame",
-               message: "Evaluation frame not found" };
-    }
-
-    if (!frame.environment) {
-      return { error: "notDebuggee",
-               message: "cannot access the environment of this frame." };
-    }
-
-    const youngest = this.youngestFrame;
-
-    // Put ourselves back in the running state and inform the client.
-    const resumedPacket = this._resumed();
-    this.conn.send(resumedPacket);
-
-    // Run the expression.
-    // XXX: test syntax errors
-    const completion = frame.eval(request.expression);
-
-    // Put ourselves back in the pause state.
-    const packet = this._paused(youngest);
-    packet.why = { type: "clientEvaluated",
-                   frameFinished: this.createProtocolCompletionValue(completion) };
-
-    // Return back to our previous pause's event loop.
-    return packet;
-  },
-
   onFrames: function(request) {
     if (this.state !== "paused") {
       return { error: "wrongState",
                message: "Stack frames are only available while the debuggee is paused."};
     }
 
     const start = request.start ? request.start : 0;
     const count = request.count;
@@ -1738,17 +1702,16 @@ const ThreadActor = ActorClassWithSpec(t
   },
 });
 
 Object.assign(ThreadActor.prototype.requestTypes, {
   "attach": ThreadActor.prototype.onAttach,
   "detach": ThreadActor.prototype.onDetach,
   "reconfigure": ThreadActor.prototype.onReconfigure,
   "resume": ThreadActor.prototype.onResume,
-  "clientEvaluate": ThreadActor.prototype.onClientEvaluate,
   "frames": ThreadActor.prototype.onFrames,
   "interrupt": ThreadActor.prototype.onInterrupt,
   "sources": ThreadActor.prototype.onSources,
   "threadGrips": ThreadActor.prototype.onThreadGrips,
   "skipBreakpoints": ThreadActor.prototype.onSkipBreakpoints,
   "pauseOnExceptions": ThreadActor.prototype.onPauseOnExceptions,
   "dumpThread": ThreadActor.prototype.onDump,
 });
--- a/devtools/server/tests/unit/test_blackboxing-02.js
+++ b/devtools/server/tests/unit/test_blackboxing-02.js
@@ -28,29 +28,19 @@ 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) {
-    gThreadClient.eval(packet.frame.actor, "doStuff", function(response) {
-      gThreadClient.addOneTimeListener("paused", function(event, packet) {
-        const obj = gThreadClient.pauseGrip(packet.why.frameFinished.return);
-        obj.getDefinitionSite(runWithSource);
-      });
-    });
-
-    function runWithSource(packet) {
-      const source = gThreadClient.source(packet.source);
-      gThreadClient.setBreakpoint({ sourceUrl: source.url, line: 2 }, {});
-      gThreadClient.resume(test_black_box_breakpoint);
-    }
+  gClient.addOneTimeListener("paused", async function(event, packet) {
+    gThreadClient.setBreakpoint({ sourceUrl: BLACK_BOXED_URL, line: 2 }, {});
+    gThreadClient.resume(test_black_box_breakpoint);
   });
 
   /* eslint-disable no-multi-spaces, no-undef */
   Cu.evalInSandbox(
     "" + function doStuff(k) { // line 1
       const arg = 15;            // line 2 - Break here
       k(arg);                  // line 3
     },                         // line 4
--- a/devtools/server/tests/unit/test_blackboxing-04.js
+++ b/devtools/server/tests/unit/test_blackboxing-04.js
@@ -26,28 +26,18 @@ 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) {
-    gThreadClient.eval(packet.frame.actor, "doStuff", function(response) {
-      gThreadClient.addOneTimeListener("paused", function(event, packet) {
-        const obj = gThreadClient.pauseGrip(packet.why.frameFinished.return);
-        obj.getDefinitionSite(runWithSource);
-      });
-    });
-
-    function runWithSource(packet) {
-      const source = gThreadClient.source(packet.source);
-      gThreadClient.setBreakpoint({ sourceUrl: source.url, line: 2 }, {});
-      test_black_box_paused();
-    }
+    gThreadClient.setBreakpoint({ sourceUrl: BLACK_BOXED_URL, line: 2 }, {});
+    test_black_box_paused();
   });
 
   /* eslint-disable no-multi-spaces, no-undef */
   Cu.evalInSandbox(
     "" + function doStuff(k) { // line 1
       debugger;                // line 2
       k(100);                  // line 3
     },                         // line 4
--- a/devtools/server/tests/unit/test_breakpoint-08.js
+++ b/devtools/server/tests/unit/test_breakpoint-08.js
@@ -6,55 +6,49 @@
 
 /**
  * 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", async function(event, packet) {
-          const obj = threadClient.pauseGrip(packet.why.frameFinished.return);
-          const site = await obj.getDefinitionSite();
+    threadClient.addOneTimeListener("paused", async function(event, packet) {
+      const line = debuggee.line0 + 3;
+      const source = await getSourceById(
+        threadClient,
+        packet.frame.where.actor
+      );
 
-          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);
+      // this test has been disabled for a long time so the functionality doesn't work
+      const response = await threadClient
+        .setBreakpoint({ sourceUrl: source.url, line: line }, {});
+      // 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);
+      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], response.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();
-          });
+        // Remove the breakpoint.
+        response.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
deleted file mode 100644
--- a/devtools/server/tests/unit/test_eval-01.js
+++ /dev/null
@@ -1,63 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-/* eslint-disable no-shadow, max-nested-callbacks */
-
-"use strict";
-
-/**
- * Check basic eval resume/re-pause
- */
-
-var gDebuggee;
-var gClient;
-var gThreadClient;
-
-function run_test() {
-  Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
-  registerCleanupFunction(() => {
-    Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
-  });
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-stack");
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-stack",
-                           function(response, targetFront, threadClient) {
-                             gThreadClient = threadClient;
-                             test_simple_eval();
-                           });
-  });
-  do_test_pending();
-}
-
-function test_simple_eval() {
-  gThreadClient.addOneTimeListener("paused", function(event, packet) {
-    const arg1Actor = packet.frame.arguments[0].actor;
-    gThreadClient.eval(null, "({ obj: true })", function(response) {
-      Assert.equal(response.type, "resumed");
-      // Expect a pause notification immediately.
-      gThreadClient.addOneTimeListener("paused", function(event, packet) {
-        // Check the return value...
-        Assert.equal(packet.type, "paused");
-        Assert.equal(packet.why.type, "clientEvaluated");
-        Assert.equal(packet.why.frameFinished.return.type, "object");
-        Assert.equal(packet.why.frameFinished.return.class, "Object");
-
-        // Make sure the previous pause lifetime was correctly dropped.
-        gClient.request({ to: arg1Actor, type: "bogusRequest" }, function(response) {
-          Assert.equal(response.error, "noSuchActor");
-          gThreadClient.resume(function() {
-            finishClient(gClient);
-          });
-        });
-      });
-    });
-  });
-
-  /* eslint-disable */
-  gDebuggee.eval("(" + function () {
-    function stopMe(arg1) { debugger; }
-    stopMe({obj: true});
-  } + ")()");
-  /* eslint-enable */
-}
deleted file mode 100644
--- a/devtools/server/tests/unit/test_eval-02.js
+++ /dev/null
@@ -1,56 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-/* eslint-disable no-shadow, max-nested-callbacks */
-
-"use strict";
-
-/**
- * Check eval resume/re-pause with a throw.
- */
-
-var gDebuggee;
-var gClient;
-var gThreadClient;
-
-function run_test() {
-  Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
-  registerCleanupFunction(() => {
-    Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
-  });
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-stack");
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-stack",
-                           function(response, targetFront, threadClient) {
-                             gThreadClient = threadClient;
-                             test_throw_eval();
-                           });
-  });
-  do_test_pending();
-}
-
-function test_throw_eval() {
-  gThreadClient.addOneTimeListener("paused", function(event, packet) {
-    gThreadClient.eval(null, "throw 'failure'", function(response) {
-      Assert.equal(response.type, "resumed");
-      // Expect a pause notification immediately.
-      gThreadClient.addOneTimeListener("paused", function(event, packet) {
-        // Check the return value...
-        Assert.equal(packet.type, "paused");
-        Assert.equal(packet.why.type, "clientEvaluated");
-        Assert.equal(packet.why.frameFinished.throw, "failure");
-        gThreadClient.resume(function() {
-          finishClient(gClient);
-        });
-      });
-    });
-  });
-
-  /* eslint-disable */
-  gDebuggee.eval("(" + function () {
-    function stopMe(arg1) { debugger; }
-    stopMe({obj: true});
-  } + ")()");
-  /* eslint-enable */
-}
deleted file mode 100644
--- a/devtools/server/tests/unit/test_eval-03.js
+++ /dev/null
@@ -1,58 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-/* eslint-disable no-shadow, max-nested-callbacks */
-
-"use strict";
-
-/**
- * Check syntax errors in an eval.
- */
-
-var gDebuggee;
-var gClient;
-var gThreadClient;
-
-function run_test() {
-  Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
-  registerCleanupFunction(() => {
-    Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
-  });
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-stack");
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then().then(function() {
-    attachTestTabAndResume(gClient, "test-stack",
-                           function(response, targetFront, threadClient) {
-                             gThreadClient = threadClient;
-                             test_syntax_error_eval();
-                           });
-  });
-  do_test_pending();
-}
-
-function test_syntax_error_eval() {
-  gThreadClient.addOneTimeListener("paused", function(event, packet) {
-    gThreadClient.eval(null, "%$@!@#", function(response) {
-      Assert.equal(response.type, "resumed");
-      // Expect a pause notification immediately.
-      gThreadClient.addOneTimeListener("paused", function(event, packet) {
-        // Check the return value...
-        Assert.equal(packet.type, "paused");
-        Assert.equal(packet.why.type, "clientEvaluated");
-        Assert.equal(packet.why.frameFinished.throw.type, "object");
-        Assert.equal(packet.why.frameFinished.throw.class, "Error");
-
-        gThreadClient.resume(function() {
-          finishClient(gClient);
-        });
-      });
-    });
-  });
-
-  /* eslint-disable */
-  gDebuggee.eval("(" + function () {
-    function stopMe(arg1) { debugger; }
-    stopMe({obj: true});
-  } + ")()");
-  /* eslint-enable */
-}
deleted file mode 100644
--- a/devtools/server/tests/unit/test_eval-04.js
+++ /dev/null
@@ -1,74 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-/* eslint-disable no-shadow, max-nested-callbacks */
-
-"use strict";
-
-/**
- * Check evals against different frames.
- */
-
-var gDebuggee;
-var gClient;
-var gThreadClient;
-
-function run_test() {
-  Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
-  registerCleanupFunction(() => {
-    Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
-  });
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-stack");
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-stack",
-                           function(response, targetFront, threadClient) {
-                             gThreadClient = threadClient;
-                             test_different_frames_eval();
-                           });
-  });
-  do_test_pending();
-}
-
-function test_different_frames_eval() {
-  gThreadClient.addOneTimeListener("paused", function(event, packet) {
-    gThreadClient.getFrames(0, 2, function(response) {
-      const frame0 = response.frames[0];
-      const frame1 = response.frames[1];
-
-      // Eval against the top frame...
-      gThreadClient.eval(frame0.actor, "arg", function(response) {
-        Assert.equal(response.type, "resumed");
-        gThreadClient.addOneTimeListener("paused", function(event, packet) {
-          // 'arg' should have been evaluated in frame0
-          Assert.equal(packet.type, "paused");
-          Assert.equal(packet.why.type, "clientEvaluated");
-          Assert.equal(packet.why.frameFinished.return, "arg0");
-
-          // Now eval against the second frame.
-          gThreadClient.eval(frame1.actor, "arg", function(response) {
-            gThreadClient.addOneTimeListener("paused", function(event, packet) {
-              // 'arg' should have been evaluated in frame1
-              Assert.equal(packet.type, "paused");
-              Assert.equal(packet.why.frameFinished.return, "arg1");
-
-              gThreadClient.resume(function() {
-                finishClient(gClient);
-              });
-            });
-          });
-        });
-      });
-    });
-  });
-
-  gDebuggee.eval("(" + function() {
-    function frame0(arg) {
-      debugger;
-    }
-    function frame1(arg) {
-      frame0("arg0");
-    }
-    frame1("arg1");
-  } + ")()");
-}
deleted file mode 100644
--- a/devtools/server/tests/unit/test_eval-05.js
+++ /dev/null
@@ -1,60 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-/* eslint-disable no-shadow, max-nested-callbacks */
-
-"use strict";
-
-/**
- * Check pauses within evals.
- */
-
-var gDebuggee;
-var gClient;
-var gThreadClient;
-
-function run_test() {
-  Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
-  registerCleanupFunction(() => {
-    Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
-  });
-  initTestDebuggerServer();
-  gDebuggee = addTestGlobal("test-stack");
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function() {
-    attachTestTabAndResume(gClient, "test-stack",
-                           function(response, targetFront, threadClient) {
-                             gThreadClient = threadClient;
-                             test_pauses_eval();
-                           });
-  });
-  do_test_pending();
-}
-
-function test_pauses_eval() {
-  gThreadClient.addOneTimeListener("paused", function(event, packet) {
-    gThreadClient.eval(null, "debugger", function(response) {
-      // Expect a resume then a debuggerStatement pause.
-      Assert.equal(response.type, "resumed");
-      gThreadClient.addOneTimeListener("paused", function(event, packet) {
-        Assert.equal(packet.why.type, "debuggerStatement");
-        // Resume from the debugger statement should immediately re-pause
-        // with a clientEvaluated reason.
-        gThreadClient.resume(function(packet) {
-          Assert.equal(packet.type, "resumed");
-          gThreadClient.addOneTimeListener("paused", function(event, packet) {
-            Assert.equal(packet.why.type, "clientEvaluated");
-            gThreadClient.resume(function() {
-              finishClient(gClient);
-            });
-          });
-        });
-      });
-    });
-  });
-  gDebuggee.eval("(" + function() {
-    function stopMe(arg) {
-      debugger;
-    }
-    stopMe();
-  } + ")()");
-}
--- a/devtools/server/tests/unit/test_new_source-02.js
+++ b/devtools/server/tests/unit/test_new_source-02.js
@@ -4,55 +4,59 @@
 "use strict";
 
 /**
  * Check that sourceURL has the correct effect when using gThreadClient.eval.
  */
 
 var gDebuggee;
 var gClient;
+var gTargetFront;
 var gThreadClient;
 
 function run_test() {
   Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
   registerCleanupFunction(() => {
     Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
   });
   initTestDebuggerServer();
   gDebuggee = addTestGlobal("test-stack");
   gClient = new DebuggerClient(DebuggerServer.connectPipe());
   gClient.connect().then(function() {
     attachTestTabAndResume(gClient, "test-stack",
                            function(response, targetFront, threadClient) {
                              gThreadClient = threadClient;
+                             gTargetFront = targetFront;
                              test_simple_new_source();
                            });
   });
   do_test_pending();
 }
 
 function test_simple_new_source() {
   gThreadClient.addOneTimeListener("paused", function() {
-    gThreadClient.addOneTimeListener("newSource", function(event2, packet2) {
+    gThreadClient.addOneTimeListener("newSource", async function(event2, packet2) {
       // The "stopMe" eval source is emitted first.
       Assert.equal(event2, "newSource");
       Assert.equal(packet2.type, "newSource");
       Assert.ok(!!packet2.source);
       Assert.ok(packet2.source.introductionType, "eval");
 
       gThreadClient.addOneTimeListener("newSource", function(event, packet) {
         Assert.equal(event, "newSource");
         Assert.equal(packet.type, "newSource");
         dump(JSON.stringify(packet, null, 2));
         Assert.ok(!!packet.source);
         Assert.ok(!!packet.source.url.match(/example\.com/));
 
         finishClient(gClient);
       });
-      gThreadClient.eval(null, "function f() { }\n//# sourceURL=http://example.com/code.js");
+
+      const consoleFront = await gTargetFront.getFront("console");
+      consoleFront.evaluateJSAsync("function f() { }\n//# sourceURL=http://example.com/code.js");
     });
   });
 
   /* eslint-disable */
   gDebuggee.eval("(" + function () {
     function stopMe(arg1) { debugger; }
     stopMe({obj: true});
   } + ")()");
--- a/devtools/server/tests/unit/testactors.js
+++ b/devtools/server/tests/unit/testactors.js
@@ -68,16 +68,21 @@ function TestTabList(connection) {
 TestTabList.prototype = {
   constructor: TestTabList,
   getList: function() {
     return Promise.resolve([...this._targetActors]);
   },
 };
 
 exports.createRootActor = function createRootActor(connection) {
+  ActorRegistry.registerModule("devtools/server/actors/webconsole", {
+    prefix: "console",
+    constructor: "WebConsoleActor",
+    type: { target: true },
+  });
   const root = new RootActor(connection, {
     tabList: new TestTabList(connection),
     globalActorFactories: ActorRegistry.globalActorFactories,
   });
 
   root.applicationType = "xpcshell-tests";
   return root;
 };
--- a/devtools/server/tests/unit/xpcshell.ini
+++ b/devtools/server/tests/unit/xpcshell.ini
@@ -74,21 +74,16 @@ support-files =
 [test_pauselifetime-04.js]
 [test_threadlifetime-01.js]
 [test_threadlifetime-02.js]
 [test_threadlifetime-04.js]
 [test_functiongrips-01.js]
 [test_front_destroy.js]
 [test_nativewrappers.js]
 [test_nodelistactor.js]
-[test_eval-01.js]
-[test_eval-02.js]
-[test_eval-03.js]
-[test_eval-04.js]
-[test_eval-05.js]
 [test_format_command.js]
 [test_promises_actor_attach.js]
 [test_promises_actor_list_promises.js]
 skip-if = coverage # bug 1336670
 [test_promises_actor_onnewpromise.js]
 [test_promises_actor_onpromisesettled.js]
 [test_promises_client_getdependentpromises.js]
 [test_promises_object_creationtimestamp.js]
--- a/devtools/shared/client/thread-client.js
+++ b/devtools/shared/client/thread-client.js
@@ -264,49 +264,16 @@ ThreadClient.prototype = {
    */
   pauseOnExceptions: DebuggerClient.requester({
     type: "pauseOnExceptions",
     pauseOnExceptions: arg(0),
     ignoreCaughtExceptions: arg(1),
   }),
 
   /**
-   * Send a clientEvaluate packet to the debuggee. Response
-   * will be a resume packet.
-   *
-   * @param string frame
-   *        The actor ID of the frame where the evaluation should take place.
-   * @param string expression
-   *        The expression that will be evaluated in the scope of the frame
-   *        above.
-   * @param function onResponse
-   *        Called with the response packet.
-   */
-  eval: DebuggerClient.requester({
-    type: "clientEvaluate",
-    frame: arg(0),
-    expression: arg(1),
-  }, {
-    before: function(packet) {
-      this._assertPaused("eval");
-      // Put the client in a tentative "resuming" state so we can prevent
-      // further requests that should only be sent in the paused state.
-      this._state = "resuming";
-      return packet;
-    },
-    after: function(response) {
-      if (response.error) {
-        // There was an error resuming, back to paused state.
-        this._state = "paused";
-      }
-      return response;
-    },
-  }),
-
-  /**
    * Detach from the thread actor.
    *
    * @param function onResponse
    *        Called with the response packet.
    */
   detach: DebuggerClient.requester({
     type: "detach",
   }, {
--- a/docshell/base/BrowsingContext.cpp
+++ b/docshell/base/BrowsingContext.cpp
@@ -53,30 +53,16 @@ static StaticAutoPtr<BrowsingContextMap<
 
 static void Register(BrowsingContext* aBrowsingContext) {
   MOZ_ALWAYS_TRUE(
       sBrowsingContexts->putNew(aBrowsingContext->Id(), aBrowsingContext));
 
   aBrowsingContext->Group()->Register(aBrowsingContext);
 }
 
-static void Sync(BrowsingContext* aBrowsingContext) {
-  if (!XRE_IsContentProcess()) {
-    return;
-  }
-
-  auto cc = ContentChild::GetSingleton();
-  MOZ_DIAGNOSTIC_ASSERT(cc);
-  RefPtr<BrowsingContext> parent = aBrowsingContext->GetParent();
-  BrowsingContext* opener = aBrowsingContext->GetOpener();
-  cc->SendAttachBrowsingContext(parent, opener,
-                                BrowsingContextId(aBrowsingContext->Id()),
-                                aBrowsingContext->Name());
-}
-
 BrowsingContext* BrowsingContext::TopLevelBrowsingContext() {
   BrowsingContext* bc = this;
   while (bc->mParent) {
     bc = bc->mParent;
   }
   return bc;
 }
 
@@ -116,197 +102,205 @@ already_AddRefed<BrowsingContext> Browsi
   MOZ_DIAGNOSTIC_ASSERT(!aParent || aParent->mType == aType);
 
   uint64_t id = nsContentUtils::GenerateBrowsingContextId();
 
   MOZ_LOG(GetLog(), LogLevel::Debug,
           ("Creating 0x%08" PRIx64 " in %s", id,
            XRE_IsParentProcess() ? "Parent" : "Child"));
 
+  // Determine which BrowsingContextGroup this context should be created in.
+  RefPtr<BrowsingContextGroup> group =
+      BrowsingContextGroup::Select(aParent, aOpener);
+
   RefPtr<BrowsingContext> context;
   if (XRE_IsParentProcess()) {
-    context = new CanonicalBrowsingContext(aParent, aOpener, aName, id,
+    context = new CanonicalBrowsingContext(aParent, group, id,
                                            /* aProcessId */ 0, aType);
   } else {
-    context = new BrowsingContext(aParent, aOpener, aName, id, aType);
+    context = new BrowsingContext(aParent, group, id, aType);
+  }
+
+  // The name and opener fields need to be explicitly initialized. Don't bother
+  // using transactions to set them, as we haven't been attached yet.
+  context->mName = aName;
+  context->mOpenerId = aOpener ? aOpener->Id() : 0;
+
+  if (aParent) {
+    context->mCrossOriginPolicy = aParent->mCrossOriginPolicy;
+  } else if (aOpener) {
+    context->mCrossOriginPolicy = aOpener->mCrossOriginPolicy;
+  } else {
+    context->mCrossOriginPolicy = nsILoadInfo::CROSS_ORIGIN_POLICY_NULL;
   }
 
   Register(context);
 
   // Attach the browsing context to the tree.
   context->Attach();
 
   return context.forget();
 }
 
 /* static */
 already_AddRefed<BrowsingContext> BrowsingContext::CreateFromIPC(
-    BrowsingContext* aParent, BrowsingContext* aOpener, const nsAString& aName,
-    uint64_t aId, ContentParent* aOriginProcess) {
-  MOZ_DIAGNOSTIC_ASSERT(aOriginProcess || XRE_IsContentProcess(),
-                        "Parent Process IPC contexts need a Content Process.");
-  MOZ_DIAGNOSTIC_ASSERT(!aParent || aParent->IsContent());
+    BrowsingContext::IPCInitializer&& aInit, BrowsingContextGroup* aGroup,
+    ContentParent* aOriginProcess) {
+  MOZ_DIAGNOSTIC_ASSERT(aOriginProcess || XRE_IsContentProcess());
+  MOZ_DIAGNOSTIC_ASSERT(aGroup);
+
+  uint64_t originId = aOriginProcess ? aOriginProcess->ChildID() : 0;
 
   MOZ_LOG(GetLog(), LogLevel::Debug,
-          ("Creating 0x%08" PRIx64 " from IPC (origin=0x%08" PRIx64 ")", aId,
-           aOriginProcess ? uint64_t(aOriginProcess->ChildID()) : 0));
+          ("Creating 0x%08" PRIx64 " from IPC (origin=0x%08" PRIx64 ")",
+           aInit.mId, originId));
+
+  RefPtr<BrowsingContext> parent = aInit.GetParent();
 
   RefPtr<BrowsingContext> context;
   if (XRE_IsParentProcess()) {
-    context = new CanonicalBrowsingContext(
-        aParent, aOpener, aName, aId, aOriginProcess->ChildID(), Type::Content);
-
-    context->Group()->Subscribe(aOriginProcess);
+    context = new CanonicalBrowsingContext(parent, aGroup, aInit.mId, originId,
+                                           Type::Content);
   } else {
-    context = new BrowsingContext(aParent, aOpener, aName, aId, Type::Content);
+    context = new BrowsingContext(parent, aGroup, aInit.mId, Type::Content);
   }
 
   Register(context);
 
-  context->Attach();
+  // Initialize all of our fields from IPC. We don't have to worry about
+  // mOpenerId, as we won't try to dereference it immediately.
+#define MOZ_BC_FIELD(name, ...) context->m##name = aInit.m##name;
+#include "mozilla/dom/BrowsingContextFieldList.h"
+
+  // Caller handles attaching us to the tree.
 
   return context.forget();
 }
 
 BrowsingContext::BrowsingContext(BrowsingContext* aParent,
-                                 BrowsingContext* aOpener,
-                                 const nsAString& aName,
+                                 BrowsingContextGroup* aGroup,
                                  uint64_t aBrowsingContextId, Type aType)
-    : mName(aName),
-      mClosed(false),
-      mType(aType),
+    : mType(aType),
       mBrowsingContextId(aBrowsingContextId),
+      mGroup(aGroup),
       mParent(aParent),
-      mOpener(aOpener),
-      mIsActivatedByUserGesture(false) {
-  mCrossOriginPolicy = nsILoadInfo::CROSS_ORIGIN_POLICY_NULL;
-  // Specify our group in our constructor. We will explicitly join the group
-  // when we are registered, as doing so will take a reference.
-  if (mParent) {
-    mGroup = mParent->Group();
-    mCrossOriginPolicy = mParent->CrossOriginPolicy();
-  } else if (mOpener) {
-    mGroup = mOpener->Group();
-    mCrossOriginPolicy = mOpener->CrossOriginPolicy();
-  } else {
-    // To ensure the group has a unique ID, we will use our ID, as the founder
-    // of this BrowsingContextGroup.
-    mGroup = new BrowsingContextGroup();
-  }
+      mIsInProcess(false) {
+  MOZ_RELEASE_ASSERT(!mParent || mParent->Group() == mGroup);
+  MOZ_RELEASE_ASSERT(mBrowsingContextId != 0);
+  MOZ_RELEASE_ASSERT(mGroup);
 }
 
 void BrowsingContext::SetDocShell(nsIDocShell* aDocShell) {
   // XXX(nika): We should communicate that we are now an active BrowsingContext
   // process to the parent & do other validation here.
   MOZ_RELEASE_ASSERT(nsDocShell::Cast(aDocShell)->GetBrowsingContext() == this);
   mDocShell = aDocShell;
+  mIsInProcess = true;
 }
 
-void BrowsingContext::Attach() {
+void BrowsingContext::Attach(bool aFromIPC) {
   MOZ_LOG(GetLog(), LogLevel::Debug,
           ("%s: %s 0x%08" PRIx64 " to 0x%08" PRIx64,
            XRE_IsParentProcess() ? "Parent" : "Child",
            sCachedBrowsingContexts->has(Id()) ? "Re-connecting" : "Connecting",
            Id(), mParent ? mParent->Id() : 0));
 
   sCachedBrowsingContexts->remove(Id());
 
   auto* children = mParent ? &mParent->mChildren : &mGroup->Toplevels();
   MOZ_DIAGNOSTIC_ASSERT(!children->Contains(this));
 
   children->AppendElement(this);
 
-  Sync(this);
+  // Send attach to our parent if we need to.
+  if (!aFromIPC && XRE_IsContentProcess()) {
+    ContentChild::GetSingleton()->SendAttachBrowsingContext(
+        GetIPCInitializer());
+  }
 }
 
-void BrowsingContext::Detach() {
+void BrowsingContext::Detach(bool aFromIPC) {
   MOZ_LOG(GetLog(), LogLevel::Debug,
           ("%s: Detaching 0x%08" PRIx64 " from 0x%08" PRIx64,
            XRE_IsParentProcess() ? "Parent" : "Child", Id(),
            mParent ? mParent->Id() : 0));
 
   RefPtr<BrowsingContext> kungFuDeathGrip(this);
 
   BrowsingContextMap<RefPtr>::Ptr p;
   if (sCachedBrowsingContexts && (p = sCachedBrowsingContexts->lookup(Id()))) {
     MOZ_DIAGNOSTIC_ASSERT(!mParent || !mParent->mChildren.Contains(this));
     MOZ_DIAGNOSTIC_ASSERT(!mGroup || !mGroup->Toplevels().Contains(this));
     sCachedBrowsingContexts->remove(p);
   } else {
-    Children& children = mParent ? mParent->mChildren : mGroup->Toplevels();
+    Children* children = nullptr;
+    if (mParent) {
+      children = &mParent->mChildren;
+    } else if (mGroup) {
+      children = &mGroup->Toplevels();
+    }
 
-    // TODO(farre): This assert looks extremely fishy, I know, but
-    // what we're actually saying is this: if we're detaching, but our
-    // parent doesn't have any children, it is because we're being
-    // detached by the cycle collector destroying docshells out of
-    // order.
-    MOZ_DIAGNOSTIC_ASSERT(children.IsEmpty() || children.Contains(this));
+    if (children) {
+      // TODO(farre): This assert looks extremely fishy, I know, but
+      // what we're actually saying is this: if we're detaching, but our
+      // parent doesn't have any children, it is because we're being
+      // detached by the cycle collector destroying docshells out of
+      // order.
+      MOZ_DIAGNOSTIC_ASSERT(children->IsEmpty() || children->Contains(this));
 
-    children.RemoveElement(this);
+      children->RemoveElement(this);
+    }
   }
 
-  Group()->Unregister(this);
-
-  if (!XRE_IsContentProcess()) {
-    return;
+  if (mGroup) {
+    mGroup->Unregister(this);
   }
 
-  auto cc = ContentChild::GetSingleton();
-  MOZ_DIAGNOSTIC_ASSERT(cc);
-  cc->SendDetachBrowsingContext(this, false /* aMoveToBFCache */);
+  // As our nsDocShell is going away, this should implicitly mark us as closed.
+  // We directly set our member, rather than using a transaction as we're going
+  // to send a `Detach` message to other processes either way.
+  mClosed = true;
+
+  if (!aFromIPC && XRE_IsContentProcess()) {
+    auto cc = ContentChild::GetSingleton();
+    MOZ_DIAGNOSTIC_ASSERT(cc);
+    cc->SendDetachBrowsingContext(this, false /* aMoveToBFCache */);
+  }
 }
 
-void BrowsingContext::CacheChildren() {
+void BrowsingContext::CacheChildren(bool aFromIPC) {
   if (mChildren.IsEmpty()) {
     return;
   }
 
   MOZ_LOG(GetLog(), LogLevel::Debug,
           ("%s: Caching children of 0x%08" PRIx64 "",
            XRE_IsParentProcess() ? "Parent" : "Child", Id()));
 
   MOZ_ALWAYS_TRUE(sCachedBrowsingContexts->reserve(mChildren.Length()));
 
   for (BrowsingContext* child : mChildren) {
     MOZ_ALWAYS_TRUE(sCachedBrowsingContexts->putNew(child->Id(), child));
   }
   mChildren.Clear();
 
-  if (!XRE_IsContentProcess()) {
-    return;
+  if (!aFromIPC && XRE_IsContentProcess()) {
+    auto cc = ContentChild::GetSingleton();
+    MOZ_DIAGNOSTIC_ASSERT(cc);
+    cc->SendDetachBrowsingContext(this, true /* aMoveToBFCache */);
   }
-
-  auto cc = ContentChild::GetSingleton();
-  MOZ_DIAGNOSTIC_ASSERT(cc);
-  cc->SendDetachBrowsingContext(this, true /* aMoveToBFCache */);
 }
 
 bool BrowsingContext::IsCached() { return sCachedBrowsingContexts->has(Id()); }
 
 void BrowsingContext::GetChildren(
     nsTArray<RefPtr<BrowsingContext>>& aChildren) {
   MOZ_ALWAYS_TRUE(aChildren.AppendElements(mChildren));
 }
 
-void BrowsingContext::SetOpener(BrowsingContext* aOpener) {
-  if (mOpener == aOpener) {
-    return;
-  }
-
-  mOpener = aOpener;
-
-  if (!XRE_IsContentProcess()) {
-    return;
-  }
-
-  auto cc = ContentChild::GetSingleton();
-  MOZ_DIAGNOSTIC_ASSERT(cc);
-  cc->SendSetOpenerBrowsingContext(this, aOpener);
-}
-
 // FindWithName follows the rules for choosing a browsing context,
 // with the exception of sandboxing for iframes. The implementation
 // for arbitrarily choosing between two browsing contexts with the
 // same name is as follows:
 //
 // 1) The start browsing context, i.e. 'this'
 // 2) Descendants in insertion order
 // 3) The parent
@@ -478,60 +472,32 @@ JSObject* BrowsingContext::WrapObject(JS
 
 void BrowsingContext::NotifyUserGestureActivation() {
   // We would set the user gesture activation flag on the top level browsing
   // context, which would automatically be sync to other top level browsing
   // contexts which are in the different process.
   RefPtr<BrowsingContext> topLevelBC = TopLevelBrowsingContext();
   USER_ACTIVATION_LOG("Get top level browsing context 0x%08" PRIx64,
                       topLevelBC->Id());
-  topLevelBC->SetUserGestureActivation();
-
-  if (!XRE_IsContentProcess()) {
-    return;
-  }
-  auto cc = ContentChild::GetSingleton();
-  MOZ_ASSERT(cc);
-  cc->SendSetUserGestureActivation(topLevelBC, true);
+  topLevelBC->SetIsActivatedByUserGesture(true);
 }
 
 void BrowsingContext::NotifyResetUserGestureActivation() {
   // We would reset the user gesture activation flag on the top level browsing
   // context, which would automatically be sync to other top level browsing
   // contexts which are in the different process.
   RefPtr<BrowsingContext> topLevelBC = TopLevelBrowsingContext();
   USER_ACTIVATION_LOG("Get top level browsing context 0x%08" PRIx64,
                       topLevelBC->Id());
-  topLevelBC->ResetUserGestureActivation();
-
-  if (!XRE_IsContentProcess()) {
-    return;
-  }
-  auto cc = ContentChild::GetSingleton();
-  MOZ_ASSERT(cc);
-  cc->SendSetUserGestureActivation(topLevelBC, false);
-}
-
-void BrowsingContext::SetUserGestureActivation() {
-  MOZ_ASSERT(!mParent, "Set user activation flag on non top-level context!");
-  USER_ACTIVATION_LOG(
-      "Set user gesture activation for browsing context 0x%08" PRIx64, Id());
-  mIsActivatedByUserGesture = true;
+  topLevelBC->SetIsActivatedByUserGesture(false);
 }
 
 bool BrowsingContext::GetUserGestureActivation() {
   RefPtr<BrowsingContext> topLevelBC = TopLevelBrowsingContext();
-  return topLevelBC->mIsActivatedByUserGesture;
-}
-
-void BrowsingContext::ResetUserGestureActivation() {
-  MOZ_ASSERT(!mParent, "Clear user activation flag on non top-level context!");
-  USER_ACTIVATION_LOG(
-      "Reset user gesture activation for browsing context 0x%08" PRIx64, Id());
-  mIsActivatedByUserGesture = false;
+  return topLevelBC->GetIsActivatedByUserGesture();
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(BrowsingContext)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(BrowsingContext)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocShell, mChildren, mParent, mGroup)
   if (XRE_IsParentProcess()) {
     CanonicalBrowsingContext::Cast(tmp)->Unlink();
@@ -589,40 +555,49 @@ void BrowsingContext::Location(JSContext
   }
 }
 
 void BrowsingContext::Close(CallerType aCallerType, ErrorResult& aError) {
   // FIXME We need to set mClosed, but only once we're sending the
   //       DOMWindowClose event (which happens in the process where the
   //       document for this browsing context is loaded).
   //       See https://bugzilla.mozilla.org/show_bug.cgi?id=1516343.
-  ContentChild* cc = ContentChild::GetSingleton();
-  cc->SendWindowClose(this, aCallerType == CallerType::System);
+  if (ContentChild* cc = ContentChild::GetSingleton()) {
+    cc->SendWindowClose(this, aCallerType == CallerType::System);
+  } else if (ContentParent* cp = Canonical()->GetContentParent()) {
+    Unused << cp->SendWindowClose(this, aCallerType == CallerType::System);
+  }
 }
 
 void BrowsingContext::Focus(ErrorResult& aError) {
-  ContentChild* cc = ContentChild::GetSingleton();
-  cc->SendWindowFocus(this);
+  if (ContentChild* cc = ContentChild::GetSingleton()) {
+    cc->SendWindowFocus(this);
+  } else if (ContentParent* cp = Canonical()->GetContentParent()) {
+    Unused << cp->SendWindowFocus(this);
+  }
 }
 
 void BrowsingContext::Blur(ErrorResult& aError) {
-  ContentChild* cc = ContentChild::GetSingleton();
-  cc->SendWindowBlur(this);
+  if (ContentChild* cc = ContentChild::GetSingleton()) {
+    cc->SendWindowBlur(this);
+  } else if (ContentParent* cp = Canonical()->GetContentParent()) {
+    Unused << cp->SendWindowBlur(this);
+  }
 }
 
 Nullable<WindowProxyHolder> BrowsingContext::GetTop(ErrorResult& aError) {
   // We never return null or throw an error, but the implementation in
   // nsGlobalWindow does and we need to use the same signature.
   return WindowProxyHolder(TopLevelBrowsingContext());
 }
 
 void BrowsingContext::GetOpener(JSContext* aCx,
                                 JS::MutableHandle<JS::Value> aOpener,
                                 ErrorResult& aError) const {
-  auto* opener = GetOpener();
+  RefPtr<BrowsingContext> opener = GetOpener();
   if (!opener) {
     aOpener.setNull();
     return;
   }
 
   if (!ToJSValue(aCx, WindowProxyHolder(opener), aOpener)) {
     aError.NoteJSContextException(aCx);
   }
@@ -670,24 +645,32 @@ void BrowsingContext::PostMessageMoz(JSC
   }
 
   ipc::StructuredCloneData message;
   message.Write(aCx, aMessage, transferArray, aError);
   if (NS_WARN_IF(aError.Failed())) {
     return;
   }
 
-  ContentChild* cc = ContentChild::GetSingleton();
   ClonedMessageData messageData;
-  if (!message.BuildClonedMessageDataForChild(cc, messageData)) {
-    aError.Throw(NS_ERROR_FAILURE);
-    return;
+  if (ContentChild* cc = ContentChild::GetSingleton()) {
+    if (!message.BuildClonedMessageDataForChild(cc, messageData)) {
+      aError.Throw(NS_ERROR_FAILURE);
+      return;
+    }
+
+    cc->SendWindowPostMessage(this, messageData, data);
+  } else if (ContentParent* cp = Canonical()->GetContentParent()) {
+    if (!message.BuildClonedMessageDataForParent(cp, messageData)) {
+      aError.Throw(NS_ERROR_FAILURE);
+      return;
+    }
+
+    Unused << cp->SendWindowPostMessage(this, messageData, data);
   }
-
-  cc->SendWindowPostMessage(this, messageData, data);
 }
 
 void BrowsingContext::PostMessageMoz(JSContext* aCx,
                                      JS::Handle<JS::Value> aMessage,
                                      const WindowPostMessageOptions& aOptions,
                                      nsIPrincipal& aSubjectPrincipal,
                                      ErrorResult& aError) {
   PostMessageMoz(aCx, aMessage, aOptions.mTargetOrigin, aOptions.mTransfer,
@@ -703,17 +686,47 @@ void BrowsingContext::Transaction::Commi
     for (auto iter = aBrowsingContext->Group()->ContentParentsIter();
          !iter.Done(); iter.Next()) {
       nsRefPtrHashKey<ContentParent>* entry = iter.Get();
       Unused << entry->GetKey()->SendCommitBrowsingContextTransaction(
           aBrowsingContext, *this);
     }
   }
 
-  Apply(aBrowsingContext);
+  Apply(aBrowsingContext, nullptr);
+}
+
+void BrowsingContext::Transaction::Apply(BrowsingContext* aBrowsingContext,
+                                         ContentParent* aSource) {
+#define MOZ_BC_FIELD(name, ...)                         \
+  if (m##name) {                                        \
+    aBrowsingContext->WillSet##name(*m##name, aSource); \
+    aBrowsingContext->m##name = std::move(*m##name);    \
+    aBrowsingContext->DidSet##name(aSource);            \
+    m##name.reset();                                    \
+  }
+#include "mozilla/dom/BrowsingContextFieldList.h"
+}
+
+already_AddRefed<BrowsingContext> BrowsingContext::IPCInitializer::GetParent() {
+  RefPtr<BrowsingContext> parent;
+  if (mParentId != 0) {
+    parent = BrowsingContext::Get(mParentId);
+    MOZ_RELEASE_ASSERT(parent);
+  }
+  return parent.forget();
+}
+
+already_AddRefed<BrowsingContext> BrowsingContext::IPCInitializer::GetOpener() {
+  RefPtr<BrowsingContext> opener;
+  if (mOpenerId != 0) {
+    opener = BrowsingContext::Get(mOpenerId);
+    MOZ_RELEASE_ASSERT(opener);
+  }
+  return opener.forget();
 }
 
 void BrowsingContext::LocationProxy::SetHref(const nsAString& aHref,
                                              nsIPrincipal& aSubjectPrincipal,
                                              ErrorResult& aError) {
   nsPIDOMWindowOuter* win = GetBrowsingContext()->GetDOMWindow();
   if (!win || !win->GetLocation()) {
     aError.Throw(NS_ERROR_FAILURE);
@@ -773,28 +786,58 @@ bool IPDLParamTraits<dom::BrowsingContex
   }
 
   return aResult != nullptr;
 }
 
 void IPDLParamTraits<dom::BrowsingContext::Transaction>::Write(
     IPC::Message* aMessage, IProtocol* aActor,
     const dom::BrowsingContext::Transaction& aTransaction) {
-  void_t sentinel;
-  const dom::BrowsingContext::Transaction* transaction = &aTransaction;
-  auto tuple = mozilla::Tie(
-      MOZ_FOR_EACH_SYNCED_BC_FIELD(MOZ_SYNCED_BC_FIELD_ARGUMENT, sentinel));
-
-  WriteIPDLParam(aMessage, aActor, tuple);
+#define MOZ_BC_FIELD(name, ...) \
+  WriteIPDLParam(aMessage, aActor, aTransaction.m##name);
+#include "mozilla/dom/BrowsingContextFieldList.h"
 }
 
 bool IPDLParamTraits<dom::BrowsingContext::Transaction>::Read(
     const IPC::Message* aMessage, PickleIterator* aIterator, IProtocol* aActor,
     dom::BrowsingContext::Transaction* aTransaction) {
-  void_t sentinel;
-  dom::BrowsingContext::Transaction* transaction = aTransaction;
-  auto tuple = mozilla::Tie(
-      MOZ_FOR_EACH_SYNCED_BC_FIELD(MOZ_SYNCED_BC_FIELD_ARGUMENT, sentinel));
-  return ReadIPDLParam(aMessage, aIterator, aActor, &tuple);
+#define MOZ_BC_FIELD(name, ...)                                              \
+  if (!ReadIPDLParam(aMessage, aIterator, aActor, &aTransaction->m##name)) { \
+    return false;                                                            \
+  }
+#include "mozilla/dom/BrowsingContextFieldList.h"
+
+  return true;
+}
+
+void IPDLParamTraits<dom::BrowsingContext::IPCInitializer>::Write(
+    IPC::Message* aMessage, IProtocol* aActor,
+    const dom::BrowsingContext::IPCInitializer& aInit) {
+  // Write actor ID parameters.
+  WriteIPDLParam(aMessage, aActor, aInit.mId);
+  WriteIPDLParam(aMessage, aActor, aInit.mParentId);
+
+  // Write other synchronized fields.
+#define MOZ_BC_FIELD(name, ...) WriteIPDLParam(aMessage, aActor, aInit.m##name);
+#include "mozilla/dom/BrowsingContextFieldList.h"
+}
+
+bool IPDLParamTraits<dom::BrowsingContext::IPCInitializer>::Read(
+    const IPC::Message* aMessage, PickleIterator* aIterator, IProtocol* aActor,
+    dom::BrowsingContext::IPCInitializer* aInit) {
+  // Read actor ID parameters.
+  if (!ReadIPDLParam(aMessage, aIterator, aActor, &aInit->mId) ||
+      !ReadIPDLParam(aMessage, aIterator, aActor, &aInit->mParentId)) {
+    return false;
+  }
+
+  // Read other synchronized fields.
+#define MOZ_BC_FIELD(name, ...)                                       \
+  if (!ReadIPDLParam(aMessage, aIterator, aActor, &aInit->m##name)) { \
+    return false;                                                     \
+  }
+#include "mozilla/dom/BrowsingContextFieldList.h"
+
+  return true;
 }
 
 }  // namespace ipc
 }  // namespace mozilla
--- a/docshell/base/BrowsingContext.h
+++ b/docshell/base/BrowsingContext.h
@@ -37,89 +37,44 @@ class LogModule;
 namespace ipc {
 class IProtocol;
 
 template <typename T>
 struct IPDLParamTraits;
 }  // namespace ipc
 
 namespace dom {
+class BrowsingContent;
 class BrowsingContextGroup;
 class CanonicalBrowsingContext;
 class ContentParent;
 template <typename>
 struct Nullable;
 template <typename T>
 class Sequence;
 struct WindowPostMessageOptions;
 class WindowProxyHolder;
 
-// MOZ_FOR_EACH_SYNCED_FIELD declares BrowsingContext fields that need
-// to be synced to the synced versions of BrowsingContext in parent
-// and child processes. To add a new field for syncing add a line:
-//
-// declare(name of new field, storage type, parameter type)
-//
-// before __VA_ARGS__. This will declare a private member with the
-// supplied name prepended with 'm'. If the field needs to be
-// initialized in the constructor, then that will have to be done
-// manually, and of course keeping the same order as below.
-//
-// At all times the last line below should be __VA_ARGS__, since that
-// acts as a sentinel for callers of MOZ_FOR_EACH_SYNCED_FIELD.
-
-// clang-format off
-#define MOZ_FOR_EACH_SYNCED_BC_FIELD(declare, ...)        \
-  declare(Name, nsString, nsAString)                   \
-  declare(Closed, bool, bool)                          \
-  declare(CrossOriginPolicy, nsILoadInfo::CrossOriginPolicy, nsILoadInfo::CrossOriginPolicy) \
-  __VA_ARGS__
-// clang-format on
+class BrowsingContextBase {
+ protected:
+  BrowsingContextBase() {
+    // default-construct each field.
+#define MOZ_BC_FIELD(name, type) m##name = type();
+#include "mozilla/dom/BrowsingContextFieldList.h"
+  }
+  ~BrowsingContextBase() = default;
 
-#define MOZ_SYNCED_BC_FIELD_NAME(name, ...) m##name
-#define MOZ_SYNCED_BC_FIELD_ARGUMENT(name, type, atype) \
-  transaction->MOZ_SYNCED_BC_FIELD_NAME(name),
-#define MOZ_SYNCED_BC_FIELD_GETTER(name, type, atype) \
-  const type& Get##name() const { return MOZ_SYNCED_BC_FIELD_NAME(name); }
-#define MOZ_SYNCED_BC_FIELD_SETTER(name, type, atype) \
-  void Set##name(const atype& aValue) {               \
-    Transaction t;                                    \
-    t.MOZ_SYNCED_BC_FIELD_NAME(name).emplace(aValue); \
-    t.Commit(this);                                   \
-  }
-#define MOZ_SYNCED_BC_FIELD_MEMBER(name, type, atype) \
-  type MOZ_SYNCED_BC_FIELD_NAME(name);
-#define MOZ_SYNCED_BC_FIELD_MAYBE_MEMBER(name, type, atype) \
-  mozilla::Maybe<type> MOZ_SYNCED_BC_FIELD_NAME(name);
-#define MOZ_SYNCED_BC_FIELD_APPLIER(name, type, atype) \
-  if (MOZ_SYNCED_BC_FIELD_NAME(name)) {                \
-    aOwner->MOZ_SYNCED_BC_FIELD_NAME(name) =           \
-        std::move(*MOZ_SYNCED_BC_FIELD_NAME(name));    \
-    MOZ_SYNCED_BC_FIELD_NAME(name).reset();            \
-  }
-
-#define MOZ_SYNCED_BC_FIELDS                                              \
- public:                                                                  \
-  MOZ_FOR_EACH_SYNCED_BC_FIELD(MOZ_SYNCED_BC_FIELD_GETTER)                \
-  MOZ_FOR_EACH_SYNCED_BC_FIELD(MOZ_SYNCED_BC_FIELD_SETTER)                \
-  class Transaction {                                                     \
-   public:                                                                \
-    void Commit(BrowsingContext* aOwner);                                 \
-    void Apply(BrowsingContext* aOwner) {                                 \
-      MOZ_FOR_EACH_SYNCED_BC_FIELD(MOZ_SYNCED_BC_FIELD_APPLIER)           \
-      return; /* without this return clang-format messes up formatting */ \
-    }                                                                     \
-                                                                          \
-    MOZ_FOR_EACH_SYNCED_BC_FIELD(MOZ_SYNCED_BC_FIELD_MAYBE_MEMBER)        \
-   private:                                                               \
-    friend struct mozilla::ipc::IPDLParamTraits<Transaction>;             \
-  };                                                                      \
-                                                                          \
- private:                                                                 \
-  MOZ_FOR_EACH_SYNCED_BC_FIELD(MOZ_SYNCED_BC_FIELD_MEMBER)
+#define MOZ_BC_FIELD(name, type)                                    \
+  type m##name;                                                     \
+                                                                    \
+  /* shadow to validate fields. aSource is setter process or null*/ \
+  void WillSet##name(type const& aValue, ContentParent* aSource) {} \
+  void DidSet##name(ContentParent* aSource) {}
+#include "mozilla/dom/BrowsingContextFieldList.h"
+};
 
 // BrowsingContext, in this context, is the cross process replicated
 // environment in which information about documents is stored. In
 // particular the tree structure of nested browsing contexts is
 // represented by the tree of BrowsingContexts.
 //
 // The tree of BrowsingContexts is created in step with its
 // corresponding nsDocShell, and when nsDocShells are connected
@@ -129,20 +84,18 @@ class WindowProxyHolder;
 // BrowsingContext tree for a tab, in both the parent and the child
 // process.
 //
 // Trees of BrowsingContexts should only ever contain nodes of the
 // same BrowsingContext::Type. This is enforced by asserts in the
 // BrowsingContext::Create* methods.
 class BrowsingContext : public nsWrapperCache,
                         public SupportsWeakPtr<BrowsingContext>,
-                        public LinkedListElement<RefPtr<BrowsingContext>> {
-  // Do not declare members above MOZ_SYNCED_BC_FIELDS
-  MOZ_SYNCED_BC_FIELDS
-
+                        public LinkedListElement<RefPtr<BrowsingContext>>,
+                        public BrowsingContextBase {
  public:
   enum class Type { Chrome, Content };
 
   static void Init();
   static LogModule* GetLog();
   static void CleanupContexts(uint64_t aProcessId);
 
   // Look up a BrowsingContext in the current process by ID.
@@ -152,69 +105,70 @@ class BrowsingContext : public nsWrapper
   }
 
   // Create a brand-new BrowsingContext object.
   static already_AddRefed<BrowsingContext> Create(BrowsingContext* aParent,
                                                   BrowsingContext* aOpener,
                                                   const nsAString& aName,
                                                   Type aType);
 
-  // Create a BrowsingContext object from over IPC.
-  static already_AddRefed<BrowsingContext> CreateFromIPC(
-      BrowsingContext* aParent, BrowsingContext* aOpener,
-      const nsAString& aName, uint64_t aId, ContentParent* aOriginProcess);
-
   // Cast this object to a canonical browsing context, and return it.
   CanonicalBrowsingContext* Canonical();
 
+  // Is the most recent Document in this BrowsingContext loaded within this
+  // process? This may be true with a null mDocShell after the Window has been
+  // closed.
+  bool IsInProcess() const { return mIsInProcess; }
+
   // Get the DocShell for this BrowsingContext if it is in-process, or
   // null if it's not.
   nsIDocShell* GetDocShell() { return mDocShell; }
   void SetDocShell(nsIDocShell* aDocShell);
 
   // Get the outer window object for this BrowsingContext if it is in-process
   // and still has a docshell, or null otherwise.
   nsPIDOMWindowOuter* GetDOMWindow() const {
     return mDocShell ? mDocShell->GetWindow() : nullptr;
   }
 
   // Attach the current BrowsingContext to its parent, in both the child and the
   // parent process. BrowsingContext objects are created attached by default, so
   // this method need only be called when restoring cached BrowsingContext
   // objects.
-  void Attach();
+  void Attach(bool aFromIPC = false);
 
   // Detach the current BrowsingContext from its parent, in both the
   // child and the parent process.
-  void Detach();
+  void Detach(bool aFromIPC = false);
 
   // Remove all children from the current BrowsingContext and cache
   // them to allow them to be attached again.
-  void CacheChildren();
+  void CacheChildren(bool aFromIPC = false);
 
   // Determine if the current BrowsingContext was 'cached' by the logic in
   // CacheChildren.
   bool IsCached();
 
   const nsString& Name() const { return mName; }
   void GetName(nsAString& aName) { aName = mName; }
   bool NameEquals(const nsAString& aName) { return mName.Equals(aName); }
 
   bool IsContent() const { return mType == Type::Content; }
 
   uint64_t Id() const { return mBrowsingContextId; }
 
-  BrowsingContext* GetParent() { return mParent; }
+  BrowsingContext* GetParent() const { return mParent; }
+
+  already_AddRefed<BrowsingContext> GetOpener() const { return Get(mOpenerId); }
+  void SetOpener(BrowsingContext* aOpener) {
+    SetOpenerId(aOpener ? aOpener->Id() : 0);
+  }
 
   void GetChildren(nsTArray<RefPtr<BrowsingContext>>& aChildren);
 
-  BrowsingContext* GetOpener() const { return mOpener; }
-
-  void SetOpener(BrowsingContext* aOpener);
-
   BrowsingContextGroup* Group() { return mGroup; }
 
   // Using the rules for choosing a browsing context we try to find
   // the browsing context with the given name in the set of
   // transitively reachable browsing contexts. Performs access control
   // with regards to this.
   // See
   // https://html.spec.whatwg.org/multipage/browsers.html#the-rules-for-choosing-a-browsing-context-given-a-browsing-context-name.
@@ -238,21 +192,16 @@ class BrowsingContext : public nsWrapper
   // by user gesture, and we would set the flag in the top level browsing
   // context.
   void NotifyUserGestureActivation();
 
   // This function would be called when we want to reset the user gesture
   // activation flag of the top level browsing context.
   void NotifyResetUserGestureActivation();
 
-  // These functions would only be called in the top level browsing context.
-  // They would set/reset the user gesture activation flag.
-  void SetUserGestureActivation();
-  void ResetUserGestureActivation();
-
   // Return true if it corresponding document is activated by user gesture.
   bool GetUserGestureActivation();
 
   // Return the window proxy object that corresponds to this browsing context.
   inline JSObject* GetWindowProxy() const { return mWindowProxy; }
   // Set the window proxy object that corresponds to this browsing context.
   void SetWindowProxy(JS::Handle<JSObject*> aWindowProxy) {
     mWindowProxy = aWindowProxy;
@@ -260,16 +209,25 @@ class BrowsingContext : public nsWrapper
 
   MOZ_DECLARE_WEAKREFERENCE_TYPENAME(BrowsingContext)
   NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(BrowsingContext)
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(BrowsingContext)
 
   using Children = nsTArray<RefPtr<BrowsingContext>>;
   const Children& GetChildren() { return mChildren; }
 
+  // Perform a pre-order walk of this BrowsingContext subtree.
+  template <typename Func>
+  void PreOrderWalk(Func&& aCallback) {
+    aCallback(this);
+    for (auto& child : GetChildren()) {
+      child->PreOrderWalk(aCallback);
+    }
+  }
+
   // Window APIs that are cross-origin-accessible (from the HTML spec).
   BrowsingContext* Window() { return Self(); }
   BrowsingContext* Self() { return this; }
   void Location(JSContext* aCx, JS::MutableHandle<JSObject*> aLocation,
                 ErrorResult& aError);
   void Close(CallerType aCallerType, ErrorResult& aError);
   bool GetClosed(ErrorResult&) { return mClosed; }
   void Focus(ErrorResult& aError);
@@ -285,25 +243,94 @@ class BrowsingContext : public nsWrapper
                       const Sequence<JSObject*>& aTransfer,
                       nsIPrincipal& aSubjectPrincipal, ErrorResult& aError);
   void PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
                       const WindowPostMessageOptions& aOptions,
                       nsIPrincipal& aSubjectPrincipal, ErrorResult& aError);
 
   JSObject* WrapObject(JSContext* aCx);
 
-  nsILoadInfo::CrossOriginPolicy CrossOriginPolicy() {
-    return mCrossOriginPolicy;
+  /**
+   * Transaction object. This object is used to specify and then commit
+   * modifications to synchronized fields in BrowsingContexts.
+   */
+  class Transaction {
+   public:
+    // Apply the changes from this transaction to the specified BrowsingContext
+    // in all processes. This method will call the correct `WillSet` and
+    // `DidSet` methods, as well as move the value.
+    //
+    // NOTE: This method mutates `this`, resetting all members to `Nothing()`
+    void Commit(BrowsingContext* aOwner);
+
+    // You probably don't want to directly call this method - instead call
+    // `Commit`, which will perform the necessary synchronization.
+    //
+    // |aSource| is the ContentParent which is performing the mutation in the
+    // parent process.
+    void Apply(BrowsingContext* aOwner, ContentParent* aSource);
+
+#define MOZ_BC_FIELD(name, type) mozilla::Maybe<type> m##name;
+#include "mozilla/dom/BrowsingContextFieldList.h"
+
+   private:
+    friend struct mozilla::ipc::IPDLParamTraits<Transaction>;
+  };
+
+#define MOZ_BC_FIELD(name, type)                        \
+  template <typename... Args>                           \
+  void Set##name(Args&&... aValue) {                    \
+    Transaction txn;                                    \
+    txn.m##name.emplace(std::forward<Args>(aValue)...); \
+    txn.Commit(this);                                   \
+  }                                                     \
+                                                        \
+  type const& Get##name() const { return m##name; }
+#include "mozilla/dom/BrowsingContextFieldList.h"
+
+  /**
+   * Information required to initialize a BrowsingContext in another process.
+   * This object may be serialized over IPC.
+   */
+  struct IPCInitializer {
+    uint64_t mId;
+
+    // IDs are used for Parent and Opener to allow for this object to be
+    // deserialized before other BrowsingContext in the BrowsingContextGroup
+    // have been initialized.
+    uint64_t mParentId;
+    already_AddRefed<BrowsingContext> GetParent();
+    already_AddRefed<BrowsingContext> GetOpener();
+
+    // Include each field, skipping mOpener, as we want to handle it
+    // separately.
+#define MOZ_BC_FIELD(name, type) type m##name;
+#include "mozilla/dom/BrowsingContextFieldList.h"
+  };
+
+  // Create an IPCInitializer object for this BrowsingContext.
+  IPCInitializer GetIPCInitializer() {
+    IPCInitializer init;
+    init.mId = Id();
+    init.mParentId = mParent ? mParent->Id() : 0;
+
+#define MOZ_BC_FIELD(name, type) init.m##name = m##name;
+#include "mozilla/dom/BrowsingContextFieldList.h"
+    return init;
   }
 
+  // Create a BrowsingContext object from over IPC.
+  static already_AddRefed<BrowsingContext> CreateFromIPC(
+      IPCInitializer&& aInitializer, BrowsingContextGroup* aGroup,
+      ContentParent* aOriginProcess);
+
  protected:
   virtual ~BrowsingContext();
-  BrowsingContext(BrowsingContext* aParent, BrowsingContext* aOpener,
-                  const nsAString& aName, uint64_t aBrowsingContextId,
-                  Type aType);
+  BrowsingContext(BrowsingContext* aParent, BrowsingContextGroup* aGroup,
+                  uint64_t aBrowsingContextId, Type aType);
 
  private:
   // Find the special browsing context if aName is '_self', '_parent',
   // '_top', but not '_blank'. The latter is handled in FindWithName
   BrowsingContext* FindWithSpecialName(const nsAString& aName);
 
   // Find a browsing context in the subtree rooted at 'this' Doesn't
   // consider the special names, '_self', '_parent', '_top', or
@@ -358,52 +385,62 @@ class BrowsingContext : public nsWrapper
    private:
     friend class RemoteLocationProxy;
     BrowsingContext* GetBrowsingContext() {
       return reinterpret_cast<BrowsingContext*>(
           uintptr_t(this) - offsetof(BrowsingContext, mLocation));
     }
   };
 
+  // Ensure that opener is in the same BrowsingContextGroup.
+  void WillSetOpener(const uint64_t& aValue, ContentParent* aSource) {
+    if (aValue != 0) {
+      RefPtr<BrowsingContext> opener = Get(aValue);
+      MOZ_RELEASE_ASSERT(opener && opener->Group() == Group());
+    }
+  }
+
   // Type of BrowsingContent
   const Type mType;
 
   // Unique id identifying BrowsingContext
   const uint64_t mBrowsingContextId;
 
   RefPtr<BrowsingContextGroup> mGroup;
   RefPtr<BrowsingContext> mParent;
   Children mChildren;
-  WeakPtr<BrowsingContext> mOpener;
   nsCOMPtr<nsIDocShell> mDocShell;
+
   // This is not a strong reference, but using a JS::Heap for that should be
   // fine. The JSObject stored in here should be a proxy with a
   // nsOuterWindowProxy handler, which will update the pointer from its
   // objectMoved hook and clear it from its finalize hook.
   JS::Heap<JSObject*> mWindowProxy;
   LocationProxy mLocation;
 
-  // This flag is only valid in the top level browsing context, it indicates
-  // whether the corresponding document has been activated by user gesture.
-  bool mIsActivatedByUserGesture;
+  // Is the most recent Document in this BrowsingContext loaded within this
+  // process? This may be true with a null mDocShell after the Window has been
+  // closed.
+  bool mIsInProcess : 1;
 };
 
 /**
  * Gets a WindowProxy object for a BrowsingContext that lives in a different
  * process (creating the object if it doesn't already exist). The WindowProxy
  * object will be in the compartment that aCx is currently in. This should only
  * be called if aContext doesn't hold a docshell, otherwise the BrowsingContext
  * lives in this process, and a same-process WindowProxy should be used (see
  * nsGlobalWindowOuter). This should only be called by bindings code, ToJSValue
  * is the right API to get a WindowProxy for a BrowsingContext.
  */
 extern bool GetRemoteOuterWindowProxy(JSContext* aCx, BrowsingContext* aContext,
                                       JS::MutableHandle<JSObject*> aRetVal);
 
 typedef BrowsingContext::Transaction BrowsingContextTransaction;
+typedef BrowsingContext::IPCInitializer BrowsingContextInitializer;
 
 }  // namespace dom
 
 // Allow sending BrowsingContext objects over IPC.
 namespace ipc {
 template <>
 struct IPDLParamTraits<dom::BrowsingContext> {
   static void Write(IPC::Message* aMsg, IProtocol* aActor,
@@ -417,12 +454,22 @@ struct IPDLParamTraits<dom::BrowsingCont
   static void Write(IPC::Message* aMessage, IProtocol* aActor,
                     const dom::BrowsingContext::Transaction& aTransaction);
 
   static bool Read(const IPC::Message* aMessage, PickleIterator* aIterator,
                    IProtocol* aActor,
                    dom::BrowsingContext::Transaction* aTransaction);
 };
 
+template <>
+struct IPDLParamTraits<dom::BrowsingContext::IPCInitializer> {
+  static void Write(IPC::Message* aMessage, IProtocol* aActor,
+                    const dom::BrowsingContext::IPCInitializer& aInitializer);
+
+  static bool Read(const IPC::Message* aMessage, PickleIterator* aIterator,
+                   IProtocol* aActor,
+                   dom::BrowsingContext::IPCInitializer* aInitializer);
+};
+
 }  // namespace ipc
 }  // namespace mozilla
 
 #endif  // !defined(mozilla_dom_BrowsingContext_h)
new file mode 100644
--- /dev/null
+++ b/docshell/base/BrowsingContextFieldList.h
@@ -0,0 +1,20 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+MOZ_BC_FIELD(Name, nsString)
+MOZ_BC_FIELD(Closed, bool)
+MOZ_BC_FIELD(CrossOriginPolicy, nsILoadInfo::CrossOriginPolicy)
+
+// The current opener for this BrowsingContext. This is a weak reference, and
+// stored as the opener ID.
+MOZ_BC_FIELD(OpenerId, uint64_t)
+
+// Toplevel browsing contexts only. This field controls whether the browsing
+// context is currently considered to be activated by a gesture.
+MOZ_BC_FIELD(IsActivatedByUserGesture, bool)
+
+#undef MOZ_BC_FIELD
+#undef MOZ_BC_FIELD_SKIP_OPENER
--- a/docshell/base/BrowsingContextGroup.cpp
+++ b/docshell/base/BrowsingContextGroup.cpp
@@ -32,16 +32,50 @@ void BrowsingContextGroup::Subscribe(Con
 }
 
 void BrowsingContextGroup::Unsubscribe(ContentParent* aOriginProcess) {
   MOZ_DIAGNOSTIC_ASSERT(aOriginProcess);
   mSubscribers.RemoveEntry(aOriginProcess);
   aOriginProcess->OnBrowsingContextGroupUnsubscribe(this);
 }
 
+void BrowsingContextGroup::EnsureSubscribed(ContentParent* aProcess) {
+  MOZ_DIAGNOSTIC_ASSERT(aProcess);
+  if (mSubscribers.Contains(aProcess)) {
+    return;
+  }
+
+  // Subscribe to the BrowsingContext, and send down initial state!
+  Subscribe(aProcess);
+
+  // Iterate over each of our browsing contexts, locating those which are not in
+  // their parent's children list. We can then use those as starting points to
+  // get a pre-order walk of each tree.
+  nsTArray<BrowsingContext::IPCInitializer> inits(mContexts.Count());
+  for (auto iter = mContexts.Iter(); !iter.Done(); iter.Next()) {
+    auto* context = iter.Get()->GetKey();
+
+    // If we have a parent, and are in our parent's `Children` list, skip
+    // ourselves as we'll be found in the pre-order traversal of our parent.
+    if (context->GetParent() &&
+        context->GetParent()->GetChildren().IndexOf(context) !=
+            BrowsingContext::Children::NoIndex) {
+      continue;
+    }
+
+    // Add all elements to the list in pre-order.
+    context->PreOrderWalk([&](BrowsingContext* aContext) {
+      inits.AppendElement(aContext->GetIPCInitializer());
+    });
+  }
+
+  // Send all of our contexts to the target content process.
+  Unused << aProcess->SendRegisterBrowsingContextGroup(inits);
+}
+
 BrowsingContextGroup::~BrowsingContextGroup() {
   for (auto iter = mSubscribers.Iter(); !iter.Done(); iter.Next()) {
     nsRefPtrHashKey<ContentParent>* entry = iter.Get();
     entry->GetKey()->OnBrowsingContextGroupUnsubscribe(this);
   }
 }
 
 nsISupports* BrowsingContextGroup::GetParentObject() const {
--- a/docshell/base/BrowsingContextGroup.h
+++ b/docshell/base/BrowsingContextGroup.h
@@ -38,31 +38,56 @@ class BrowsingContextGroup final : publi
   bool Contains(BrowsingContext* aContext);
   void Register(BrowsingContext* aContext);
   void Unregister(BrowsingContext* aContext);
 
   // Interact with the list of ContentParents
   void Subscribe(ContentParent* aOriginProcess);
   void Unsubscribe(ContentParent* aOriginProcess);
 
+  // Force the given ContentParent to subscribe to our BrowsingContextGroup.
+  void EnsureSubscribed(ContentParent* aProcess);
+
   ContentParents::Iterator ContentParentsIter() { return mSubscribers.Iter(); }
 
   // Get a reference to the list of toplevel contexts in this
   // BrowsingContextGroup.
   BrowsingContext::Children& Toplevels() { return mToplevels; }
   void GetToplevels(BrowsingContext::Children& aToplevels) {
     aToplevels.AppendElements(mToplevels);
   }
 
   nsISupports* GetParentObject() const;
   JSObject* WrapObject(JSContext* aCx,
                        JS::Handle<JSObject*> aGivenProto) override;
 
   BrowsingContextGroup() = default;
 
+  static already_AddRefed<BrowsingContextGroup> Select(
+      BrowsingContext* aParent, BrowsingContext* aOpener) {
+    if (aParent) {
+      return do_AddRef(aParent->Group());
+    }
+    if (aOpener) {
+      return do_AddRef(aOpener->Group());
+    }
+    return MakeAndAddRef<BrowsingContextGroup>();
+  }
+
+  static already_AddRefed<BrowsingContextGroup> Select(uint64_t aParentId,
+                                                       uint64_t aOpenerId) {
+    RefPtr<BrowsingContext> parent = BrowsingContext::Get(aParentId);
+    MOZ_RELEASE_ASSERT(parent || aParentId == 0);
+
+    RefPtr<BrowsingContext> opener = BrowsingContext::Get(aOpenerId);
+    MOZ_RELEASE_ASSERT(opener || aOpenerId == 0);
+
+    return Select(parent, opener);
+  }
+
  private:
   friend class CanonicalBrowsingContext;
 
   ~BrowsingContextGroup();
 
   // A BrowsingContextGroup contains a series of BrowsingContext objects. They
   // are addressed using a hashtable to avoid linear lookup when adding or
   // removing elements from the set.
--- a/docshell/base/CanonicalBrowsingContext.cpp
+++ b/docshell/base/CanonicalBrowsingContext.cpp
@@ -3,32 +3,32 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/CanonicalBrowsingContext.h"
 
 #include "mozilla/dom/BrowsingContextGroup.h"
 #include "mozilla/dom/WindowGlobalParent.h"
+#include "mozilla/dom/ContentProcessManager.h"
 
 namespace mozilla {
 namespace dom {
 
 extern mozilla::LazyLogModule gUserInteractionPRLog;
 
 #define USER_ACTIVATION_LOG(msg, ...) \
   MOZ_LOG(gUserInteractionPRLog, LogLevel::Debug, (msg, ##__VA_ARGS__))
 
 CanonicalBrowsingContext::CanonicalBrowsingContext(BrowsingContext* aParent,
-                                                   BrowsingContext* aOpener,
-                                                   const nsAString& aName,
+                                                   BrowsingContextGroup* aGroup,
                                                    uint64_t aBrowsingContextId,
                                                    uint64_t aProcessId,
                                                    BrowsingContext::Type aType)
-    : BrowsingContext(aParent, aOpener, aName, aBrowsingContextId, aType),
+    : BrowsingContext(aParent, aGroup, aBrowsingContextId, aType),
       mProcessId(aProcessId) {
   // You are only ever allowed to create CanonicalBrowsingContexts in the
   // parent process.
   MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
 }
 
 /* static */
 already_AddRefed<CanonicalBrowsingContext> CanonicalBrowsingContext::Get(
@@ -39,22 +39,32 @@ already_AddRefed<CanonicalBrowsingContex
 
 /* static */
 CanonicalBrowsingContext* CanonicalBrowsingContext::Cast(
     BrowsingContext* aContext) {
   MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
   return static_cast<CanonicalBrowsingContext*>(aContext);
 }
 
-/* static */ const CanonicalBrowsingContext* CanonicalBrowsingContext::Cast(
+/* static */
+const CanonicalBrowsingContext* CanonicalBrowsingContext::Cast(
     const BrowsingContext* aContext) {
   MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
   return static_cast<const CanonicalBrowsingContext*>(aContext);
 }
 
+ContentParent* CanonicalBrowsingContext::GetContentParent() const {
+  if (mProcessId == 0) {
+    return nullptr;
+  }
+
+  ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
+  return cpm->GetContentProcessById(ContentParentId(mProcessId));
+}
+
 void CanonicalBrowsingContext::GetWindowGlobals(
     nsTArray<RefPtr<WindowGlobalParent>>& aWindows) {
   aWindows.SetCapacity(mWindowGlobals.Count());
   for (auto iter = mWindowGlobals.Iter(); !iter.Done(); iter.Next()) {
     aWindows.AppendElement(iter.Get()->GetKey());
   }
 }
 
@@ -79,42 +89,30 @@ void CanonicalBrowsingContext::Unregiste
 void CanonicalBrowsingContext::SetCurrentWindowGlobal(
     WindowGlobalParent* aGlobal) {
   MOZ_ASSERT(mWindowGlobals.Contains(aGlobal), "Global not registered!");
 
   // TODO: This should probably assert that the processes match.
   mCurrentWindowGlobal = aGlobal;
 }
 
+bool CanonicalBrowsingContext::ValidateTransaction(
+    const Transaction& aTransaction, ContentParent* aProcess) {
+  if (NS_WARN_IF(aProcess && mProcessId != aProcess->ChildID())) {
+    return false;
+  }
+
+  return true;
+}
+
 JSObject* CanonicalBrowsingContext::WrapObject(
     JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
   return CanonicalBrowsingContext_Binding::Wrap(aCx, this, aGivenProto);
 }
 
-void CanonicalBrowsingContext::NotifySetUserGestureActivationFromIPC(
-    bool aIsUserGestureActivation) {
-  if (!mCurrentWindowGlobal) {
-    return;
-  }
-
-  if (aIsUserGestureActivation) {
-    SetUserGestureActivation();
-  } else {
-    ResetUserGestureActivation();
-  }
-
-  USER_ACTIVATION_LOG("Chrome browsing context 0x%08" PRIx64
-                      " would notify other browsing contexts for updating "
-                      "user gesture activation flag.",
-                      Id());
-  // XXX(alwu) : we need to sync the flag to other browsing contexts which are
-  // not in the same child process where the flag was set. Will implement that
-  // in bug1519229.
-}
-
 void CanonicalBrowsingContext::Traverse(
     nsCycleCollectionTraversalCallback& cb) {
   CanonicalBrowsingContext* tmp = this;
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindowGlobals);
 }
 
 void CanonicalBrowsingContext::Unlink() {
   CanonicalBrowsingContext* tmp = this;
--- a/docshell/base/CanonicalBrowsingContext.h
+++ b/docshell/base/CanonicalBrowsingContext.h
@@ -29,16 +29,19 @@ class CanonicalBrowsingContext final : p
   static already_AddRefed<CanonicalBrowsingContext> Get(uint64_t aId);
   static CanonicalBrowsingContext* Cast(BrowsingContext* aContext);
   static const CanonicalBrowsingContext* Cast(const BrowsingContext* aContext);
 
   bool IsOwnedByProcess(uint64_t aProcessId) const {
     return mProcessId == aProcessId;
   }
   uint64_t OwnerProcessId() const { return mProcessId; }
+  ContentParent* GetContentParent() const;
+
+  void SetOwnerProcessId(uint64_t aProcessId) { mProcessId = aProcessId; }
 
   void GetWindowGlobals(nsTArray<RefPtr<WindowGlobalParent>>& aWindows);
 
   // Called by WindowGlobalParent to register and unregister window globals.
   void RegisterWindowGlobal(WindowGlobalParent* aGlobal);
   void UnregisterWindowGlobal(WindowGlobalParent* aGlobal);
 
   // The current active WindowGlobal.
@@ -52,24 +55,30 @@ class CanonicalBrowsingContext final : p
 
   // This functions would set/reset its user gesture activation flag and then
   // notify other browsing contexts which are not the one related with the
   // current window global to set/reset the flag. (the corresponding browsing
   // context of the current global window has been set/reset before calling this
   // function)
   void NotifySetUserGestureActivationFromIPC(bool aIsUserGestureActivation);
 
+  // Validate that the given process is allowed to perform the given
+  // transaction. aSource is |nullptr| if set in the parent process.
+  bool ValidateTransaction(const Transaction& aTransaction,
+                           ContentParent* aSource);
+
  protected:
   void Traverse(nsCycleCollectionTraversalCallback& cb);
   void Unlink();
 
   using Type = BrowsingContext::Type;
-  CanonicalBrowsingContext(BrowsingContext* aParent, BrowsingContext* aOpener,
-                           const nsAString& aName, uint64_t aBrowsingContextId,
-                           uint64_t aProcessId, Type aType = Type::Chrome);
+  CanonicalBrowsingContext(BrowsingContext* aParent,
+                           BrowsingContextGroup* aGroup,
+                           uint64_t aBrowsingContextId, uint64_t aProcessId,
+                           Type aType);
 
  private:
   friend class BrowsingContext;
 
   // XXX(farre): Store a ContentParent pointer here rather than mProcessId?
   // Indicates which process owns the docshell.
   uint64_t mProcessId;
 
--- a/docshell/base/moz.build
+++ b/docshell/base/moz.build
@@ -71,16 +71,17 @@ EXPORTS += [
 
 EXPORTS.mozilla += [
     'IHistory.h',
     'LoadContext.h',
 ]
 
 EXPORTS.mozilla.dom += [
     'BrowsingContext.h',
+    'BrowsingContextFieldList.h',
     'BrowsingContextGroup.h',
     'CanonicalBrowsingContext.h',
     'ChildProcessChannelListener.h',
 ]
 
 UNIFIED_SOURCES += [
     'BrowsingContext.cpp',
     'BrowsingContextGroup.cpp',
--- a/docshell/base/nsDocShellLoadState.cpp
+++ b/docshell/base/nsDocShellLoadState.cpp
@@ -49,19 +49,17 @@ nsDocShellLoadState::nsDocShellLoadState
   mLoadType = aLoadState.LoadType();
   mTarget = aLoadState.Target();
   mLoadFlags = aLoadState.LoadFlags();
   mFirstParty = aLoadState.FirstParty();
   mTypeHint = aLoadState.TypeHint();
   mFileName = aLoadState.FileName();
   mIsFromProcessingFrameAttributes =
       aLoadState.IsFromProcessingFrameAttributes();
-  mReferrerInfo =
-      new ReferrerInfo(aLoadState.Referrer(), aLoadState.ReferrerPolicy(),
-                       aLoadState.SendReferrer());
+  mReferrerInfo = aLoadState.ReferrerInfo();
   mURI = aLoadState.URI();
   mOriginalURI = aLoadState.OriginalURI();
   mBaseURI = aLoadState.BaseURI();
   mTriggeringPrincipal = aLoadState.TriggeringPrincipal();
   mPrincipalToInherit = aLoadState.PrincipalToInherit();
   mCsp = aLoadState.Csp();
 }
 
@@ -462,13 +460,11 @@ DocShellLoadStateInit nsDocShellLoadStat
   loadState.IsFromProcessingFrameAttributes() =
       mIsFromProcessingFrameAttributes;
   loadState.URI() = mURI;
   loadState.OriginalURI() = mOriginalURI;
   loadState.BaseURI() = mBaseURI;
   loadState.TriggeringPrincipal() = mTriggeringPrincipal;
   loadState.PrincipalToInherit() = mPrincipalToInherit;
   loadState.Csp() = mCsp;
-  loadState.Referrer() = mReferrerInfo->GetOriginalReferrer();
-  loadState.SendReferrer() = mReferrerInfo->GetSendReferrer();
-  loadState.ReferrerPolicy() = mReferrerInfo->GetReferrerPolicy();
+  loadState.ReferrerInfo() = mReferrerInfo;
   return loadState;
 }
--- a/docshell/base/nsPingListener.cpp
+++ b/docshell/base/nsPingListener.cpp
@@ -322,31 +322,27 @@ nsresult nsPingListener::StartTimeout(Do
 
   return NS_NewTimerWithFuncCallback(
       getter_AddRefs(mTimer), OnPingTimeout, mLoadGroup, PING_TIMEOUT,
       nsITimer::TYPE_ONE_SHOT, "nsPingListener::StartTimeout",
       aDocGroup->EventTargetFor(TaskCategory::Network));
 }
 
 NS_IMETHODIMP
-nsPingListener::OnStartRequest(nsIRequest* aRequest) {
-  return NS_OK;
-}
+nsPingListener::OnStartRequest(nsIRequest* aRequest) { return NS_OK; }
 
 NS_IMETHODIMP
-nsPingListener::OnDataAvailable(nsIRequest* aRequest,
-                                nsIInputStream* aStream, uint64_t aOffset,
-                                uint32_t aCount) {
+nsPingListener::OnDataAvailable(nsIRequest* aRequest, nsIInputStream* aStream,
+                                uint64_t aOffset, uint32_t aCount) {
   uint32_t result;
   return aStream->ReadSegments(NS_DiscardSegment, nullptr, aCount, &result);
 }
 
 NS_IMETHODIMP
-nsPingListener::OnStopRequest(nsIRequest* aRequest,
-                              nsresult aStatus) {
+nsPingListener::OnStopRequest(nsIRequest* aRequest, nsresult aStatus) {
   mLoadGroup = nullptr;
 
   if (mTimer) {
     mTimer->Cancel();
     mTimer = nullptr;
   }
 
   return NS_OK;
--- a/dom/animation/EffectCompositor.cpp
+++ b/dom/animation/EffectCompositor.cpp
@@ -291,26 +291,26 @@ void EffectCompositor::PostRestyleForAni
     return;
   }
 
   dom::Element* element = GetElementToRestyle(aElement, aPseudoType);
   if (!element) {
     return;
   }
 
-  nsRestyleHint hint = aCascadeLevel == CascadeLevel::Transitions
-                           ? eRestyle_CSSTransitions
-                           : eRestyle_CSSAnimations;
+  RestyleHint hint = aCascadeLevel == CascadeLevel::Transitions
+                         ? StyleRestyleHint_RESTYLE_CSS_TRANSITIONS
+                         : StyleRestyleHint_RESTYLE_CSS_ANIMATIONS;
 
   MOZ_ASSERT(NS_IsMainThread(),
              "Restyle request during restyling should be requested only on "
              "the main-thread. e.g. after the parallel traversal");
   if (ServoStyleSet::IsInServoTraversal() || mIsInPreTraverse) {
-    MOZ_ASSERT(hint == eRestyle_CSSAnimations ||
-               hint == eRestyle_CSSTransitions);
+    MOZ_ASSERT(hint == StyleRestyleHint_RESTYLE_CSS_ANIMATIONS ||
+               hint == StyleRestyleHint_RESTYLE_CSS_TRANSITIONS);
 
     // We can't call Servo_NoteExplicitHints here since AtomicRefCell does not
     // allow us mutate ElementData of the |aElement| in SequentialTask.
     // Instead we call Servo_NoteExplicitHints for the element in PreTraverse()
     // which will be called right before the second traversal that we do for
     // updating CSS animations.
     // In that case PreTraverse() will return true so that we know to do the
     // second traversal so we don't need to post any restyle requests to the
@@ -825,18 +825,19 @@ bool EffectCompositor::PreTraverseInSubt
       }
 
       // We need to post restyle hints even if the target is not in EffectSet to
       // ensure the final restyling for removed animations.
       // We can't call PostRestyleEvent directly here since we are still in the
       // middle of the servo traversal.
       mPresContext->RestyleManager()->PostRestyleEventForAnimations(
           target.mElement, target.mPseudoType,
-          cascadeLevel == CascadeLevel::Transitions ? eRestyle_CSSTransitions
-                                                    : eRestyle_CSSAnimations);
+          cascadeLevel == CascadeLevel::Transitions
+              ? StyleRestyleHint_RESTYLE_CSS_TRANSITIONS
+              : StyleRestyleHint_RESTYLE_CSS_ANIMATIONS);
 
       foundElementsNeedingRestyle = true;
 
       EffectSet* effects =
           EffectSet::GetEffectSet(target.mElement, target.mPseudoType);
       if (!effects) {
         // Drop EffectSets that have been destroyed.
         iter.Remove();
@@ -903,21 +904,21 @@ bool EffectCompositor::PreTraverse(dom::
     bool hasUnthrottledRestyle = false;
     if (!elementSet.Get(key, &hasUnthrottledRestyle) ||
         (!flushThrottledRestyles && !hasUnthrottledRestyle)) {
       continue;
     }
 
     mPresContext->RestyleManager()->PostRestyleEventForAnimations(
         aElement, aPseudoType,
-        cascadeLevel == CascadeLevel::Transitions ? eRestyle_CSSTransitions
-                                                  : eRestyle_CSSAnimations);
+        cascadeLevel == CascadeLevel::Transitions
+            ? StyleRestyleHint_RESTYLE_CSS_TRANSITIONS
+            : StyleRestyleHint_RESTYLE_CSS_ANIMATIONS);
 
-    EffectSet* effects = EffectSet::GetEffectSet(aElement, aPseudoType);
-    if (effects) {
+    if (EffectSet* effects = EffectSet::GetEffectSet(aElement, aPseudoType)) {
       MaybeUpdateCascadeResults(aElement, aPseudoType);
 
       for (KeyframeEffect* effect : *effects) {
         effect->GetAnimation()->WillComposeStyle();
       }
     }
 
     elementSet.Remove(key);
--- a/dom/base/DOMParser.cpp
+++ b/dom/base/DOMParser.cpp
@@ -198,18 +198,17 @@ already_AddRefed<Document> DOMParser::Pa
   // Now start pumping data to the listener
   nsresult status;
 
   rv = listener->OnStartRequest(parserChannel);
   if (NS_FAILED(rv)) parserChannel->Cancel(rv);
   parserChannel->GetStatus(&status);
 
   if (NS_SUCCEEDED(rv) && NS_SUCCEEDED(status)) {
-    rv = listener->OnDataAvailable(parserChannel, stream, 0,
-                                   aContentLength);
+    rv = listener->OnDataAvailable(parserChannel, stream, 0, aContentLength);
     if (NS_FAILED(rv)) parserChannel->Cancel(rv);
     parserChannel->GetStatus(&status);
   }
 
   rv = listener->OnStopRequest(parserChannel, status);
   // Failure returned from OnStopRequest does not affect the final status of
   // the channel, so we do not need to call Cancel(rv) as we do above.
 
--- a/dom/base/Document.cpp
+++ b/dom/base/Document.cpp
@@ -987,18 +987,17 @@ ExternalResourceMap::PendingLoad::OnData
                                                   nsIInputStream* aStream,
                                                   uint64_t aOffset,
                                                   uint32_t aCount) {
   // mTargetListener might be null if SetupViewer or AddExternalResource failed.
   NS_ENSURE_TRUE(mTargetListener, NS_ERROR_FAILURE);
   if (mDisplayDocument->ExternalResourceMap().HaveShutDown()) {
     return NS_BINDING_ABORTED;
   }
-  return mTargetListener->OnDataAvailable(aRequest, aStream, aOffset,
-                                          aCount);
+  return mTargetListener->OnDataAvailable(aRequest, aStream, aOffset, aCount);
 }
 
 NS_IMETHODIMP
 ExternalResourceMap::PendingLoad::OnStopRequest(nsIRequest* aRequest,
                                                 nsresult aStatus) {
   // mTargetListener might be null if SetupViewer or AddExternalResource failed
   if (mTargetListener) {
     nsCOMPtr<nsIStreamListener> listener;
@@ -2540,24 +2539,21 @@ nsresult Document::StartDocumentLoad(con
       mUpgradeInsecureRequests = doc->GetUpgradeInsecureRequests(false);
       // if the parent document makes use of upgrade-insecure-requests
       // then subdocument preloads should always be upgraded.
       mUpgradeInsecurePreloads =
           mUpgradeInsecureRequests || doc->GetUpgradeInsecureRequests(true);
     }
   }
 
-  // If this is not a data document, set CSP.
-  if (!mLoadedAsData) {
-    nsresult rv = InitCSP(aChannel);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
+  nsresult rv = InitCSP(aChannel);
+  NS_ENSURE_SUCCESS(rv, rv);
 
   // Initialize FeaturePolicy
-  nsresult rv = InitFeaturePolicy(aChannel);
+  rv = InitFeaturePolicy(aChannel);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // XFO needs to be checked after CSP because it is ignored if
   // the CSP defines frame-ancestors.
   if (!FramingChecker::CheckFrameOptions(aChannel, docShell, NodePrincipal())) {
     MOZ_LOG(gCspPRLog, LogLevel::Debug,
             ("XFO doesn't like frame's ancestry, not loading."));
     // stop!  ERROR page!
@@ -2645,16 +2641,21 @@ nsresult Document::InitCSP(nsIChannel* a
   MOZ_ASSERT(!mScriptGlobalObject,
              "CSP must be initialized before mScriptGlobalObject is set!");
   if (!StaticPrefs::security_csp_enable()) {
     MOZ_LOG(gCspPRLog, LogLevel::Debug,
             ("CSP is disabled, skipping CSP init for document %p", this));
     return NS_OK;
   }
 
+  // If this is a data document - no need to set CSP.
+  if (mLoadedAsData) {
+    return NS_OK;
+  }
+
   nsAutoCString tCspHeaderValue, tCspROHeaderValue;
 
   nsCOMPtr<nsIHttpChannel> httpChannel;
   nsresult rv = GetHttpChannelHelper(aChannel, getter_AddRefs(httpChannel));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
@@ -2668,16 +2669,30 @@ nsresult Document::InitCSP(nsIChannel* a
   }
   NS_ConvertASCIItoUTF16 cspHeaderValue(tCspHeaderValue);
   NS_ConvertASCIItoUTF16 cspROHeaderValue(tCspROHeaderValue);
 
   // Check if this is a document from a WebExtension.
   nsCOMPtr<nsIPrincipal> principal = NodePrincipal();
   auto addonPolicy = BasePrincipal::Cast(principal)->AddonPolicy();
 
+  // Unless the NodePrincipal is a SystemPrincipal, which currently can not
+  // hold a CSP, we always call EnsureCSP() on the Principal. We do this
+  // so that a Meta CSP does not have to create a new CSP object. This is
+  // important because history entries hold a reference to the CSP object,
+  // which then gets dynamically updated in case a meta CSP is present.
+  // Note that after Bug 965637 we can remove that carve out, because the
+  // CSP will hang off the Client/Document and not the Principal anymore.
+  if (principal->IsSystemPrincipal()) {
+    return NS_OK;
+  }
+  nsCOMPtr<nsIContentSecurityPolicy> csp;
+  rv = principal->EnsureCSP(this, getter_AddRefs(csp));
+  NS_ENSURE_SUCCESS(rv, rv);
+
   // Check if this is a signed content to apply default CSP.
   bool applySignedContentCSP = false;
   nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
   if (loadInfo->GetVerifySignedContent()) {
     applySignedContentCSP = true;
   }
 
   // If there's no CSP to apply, go ahead and return early
@@ -2693,20 +2708,16 @@ nsresult Document::InitCSP(nsIChannel* a
     }
 
     return NS_OK;
   }
 
   MOZ_LOG(gCspPRLog, LogLevel::Debug,
           ("Document is an add-on or CSP header specified %p", this));
 
-  nsCOMPtr<nsIContentSecurityPolicy> csp;
-  rv = principal->EnsureCSP(this, getter_AddRefs(csp));
-  NS_ENSURE_SUCCESS(rv, rv);
-
   // ----- if the doc is an addon, apply its CSP.
   if (addonPolicy) {
     nsCOMPtr<nsIAddonPolicyService> aps =
         do_GetService("@mozilla.org/addons/policy-service;1");
 
     nsAutoString addonCSP;
     Unused << ExtensionPolicyService::GetSingleton().GetBaseCSP(addonCSP);
     csp->AppendPolicy(addonCSP, false, false);
@@ -6498,16 +6509,18 @@ nsINode* Document::AdoptNode(nsINode& aA
   }
 
   NS_ASSERTION(adoptedNode->OwnerDoc() == this,
                "Should still be in the document we just got adopted into");
 
   return adoptedNode;
 }
 
+bool Document::UseWidthDeviceWidthFallbackViewport() const { return false; }
+
 void Document::ParseWidthAndHeightInMetaViewport(
     const nsAString& aWidthString, const nsAString& aHeightString,
     const nsAString& aScaleString) {
   // The width and height properties
   // https://drafts.csswg.org/css-device-adapt/#width-and-height-properties
   //
   // The width and height viewport <META> properties are translated into width
   // and height descriptors, setting the min-width/min-height value to
@@ -6537,16 +6550,19 @@ void Document::ParseWidthAndHeightInMeta
       }
     }
     // FIXME: Check the scale is not 'auto' once we support auto value for it.
   } else if (!aScaleString.IsEmpty()) {
     if (aHeightString.IsEmpty()) {
       mMinWidth = nsViewportInfo::ExtendToZoom;
       mMaxWidth = nsViewportInfo::ExtendToZoom;
     }
+  } else if (aHeightString.IsEmpty() && UseWidthDeviceWidthFallbackViewport()) {
+    mMinWidth = nsViewportInfo::ExtendToZoom;
+    mMaxWidth = nsViewportInfo::DeviceSize;
   }
 
   mMinHeight = nsViewportInfo::Auto;
   mMaxHeight = nsViewportInfo::Auto;
   if (!aHeightString.IsEmpty()) {
     mMinHeight = nsViewportInfo::ExtendToZoom;
     if (aHeightString.EqualsLiteral("device-height")) {
       mMaxHeight = nsViewportInfo::DeviceSize;
--- a/dom/base/Document.h
+++ b/dom/base/Document.h
@@ -444,18 +444,17 @@ class Document : public nsINode,
  protected:
   explicit Document(const char* aContentType);
   virtual ~Document();
 
   Document(const Document&) = delete;
   Document& operator=(const Document&) = delete;
 
  public:
-  typedef mozilla::dom::ExternalResourceMap::ExternalResourceLoad
-      ExternalResourceLoad;
+  typedef dom::ExternalResourceMap::ExternalResourceLoad ExternalResourceLoad;
   typedef net::ReferrerPolicy ReferrerPolicyEnum;
 
   MOZ_DECLARE_WEAKREFERENCE_TYPENAME(Document)
 
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_IDOCUMENT_IID)
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
 
@@ -480,38 +479,36 @@ class Document : public nsINode,
 
   // nsIRadioGroupContainer
   NS_IMETHOD WalkRadioGroup(const nsAString& aName, nsIRadioVisitor* aVisitor,
                             bool aFlushContent) final {
     return DocumentOrShadowRoot::WalkRadioGroup(aName, aVisitor, aFlushContent);
   }
 
   void SetCurrentRadioButton(const nsAString& aName,
-                             mozilla::dom::HTMLInputElement* aRadio) final {
+                             HTMLInputElement* aRadio) final {
     DocumentOrShadowRoot::SetCurrentRadioButton(aName, aRadio);
   }
 
-  mozilla::dom::HTMLInputElement* GetCurrentRadioButton(
-      const nsAString& aName) final {
+  HTMLInputElement* GetCurrentRadioButton(const nsAString& aName) final {
     return DocumentOrShadowRoot::GetCurrentRadioButton(aName);
   }
 
   NS_IMETHOD
   GetNextRadioButton(const nsAString& aName, const bool aPrevious,
-                     mozilla::dom::HTMLInputElement* aFocusedRadio,
-                     mozilla::dom::HTMLInputElement** aRadioOut) final {
+                     HTMLInputElement* aFocusedRadio,
+                     HTMLInputElement** aRadioOut) final {
     return DocumentOrShadowRoot::GetNextRadioButton(aName, aPrevious,
                                                     aFocusedRadio, aRadioOut);
   }
-  void AddToRadioGroup(const nsAString& aName,
-                       mozilla::dom::HTMLInputElement* aRadio) final {
+  void AddToRadioGroup(const nsAString& aName, HTMLInputElement* aRadio) final {
     DocumentOrShadowRoot::AddToRadioGroup(aName, aRadio);
   }
   void RemoveFromRadioGroup(const nsAString& aName,
-                            mozilla::dom::HTMLInputElement* aRadio) final {
+                            HTMLInputElement* aRadio) final {
     DocumentOrShadowRoot::RemoveFromRadioGroup(aName, aRadio);
   }
   uint32_t GetRequiredRadioCount(const nsAString& aName) const final {
     return DocumentOrShadowRoot::GetRequiredRadioCount(aName);
   }
   void RadioRequiredWillChange(const nsAString& aName,
                                bool aRequiredAdded) final {
     DocumentOrShadowRoot::RadioRequiredWillChange(aName, aRequiredAdded);
@@ -522,19 +519,19 @@ class Document : public nsINode,
   void SetValueMissingState(const nsAString& aName, bool aValue) final {
     return DocumentOrShadowRoot::SetValueMissingState(aName, aValue);
   }
 
   // nsIScriptObjectPrincipal
   nsIPrincipal* GetPrincipal() final { return NodePrincipal(); }
 
   // EventTarget
-  void GetEventTargetParent(mozilla::EventChainPreVisitor& aVisitor) override;
-  mozilla::EventListenerManager* GetOrCreateListenerManager() override;
-  mozilla::EventListenerManager* GetExistingListenerManager() const override;
+  void GetEventTargetParent(EventChainPreVisitor& aVisitor) override;
+  EventListenerManager* GetOrCreateListenerManager() override;
+  EventListenerManager* GetExistingListenerManager() const override;
 
   // This helper class must be set when we dispatch beforeunload and unload
   // events in order to avoid unterminate sync XHRs.
   class MOZ_RAII PageUnloadingEventTimeStamp {
     RefPtr<Document> mDocument;
     bool mSet;
 
    public:
@@ -604,18 +601,17 @@ class Document : public nsINode,
   virtual void SetSuppressParserErrorConsoleMessages(bool aSuppress) {}
   virtual bool SuppressParserErrorConsoleMessages() { return false; }
 
   // nsINode
   bool IsNodeOfType(uint32_t aFlags) const final;
   nsresult InsertChildBefore(nsIContent* aKid, nsIContent* aBeforeThis,
                              bool aNotify) override;
   void RemoveChildNode(nsIContent* aKid, bool aNotify) final;
-  nsresult Clone(mozilla::dom::NodeInfo* aNodeInfo,
-                 nsINode** aResult) const override {
+  nsresult Clone(dom::NodeInfo* aNodeInfo, nsINode** aResult) const override {
     return NS_ERROR_NOT_IMPLEMENTED;
   }
   nsresult CloneDocHelper(Document* clone) const;
 
   Document* GetLatestStaticClone() const { return mLatestStaticClone; }
 
   /**
    * Signal that the document title may have changed
@@ -809,17 +805,17 @@ class Document : public nsINode,
 
   /**
    * Return the URL data which style system needs for resolving url value.
    * This method attempts to use the cached object in mCachedURLData, but
    * if the base URI, document URI, or principal has changed since last
    * call to this function, or the function is called the first time for
    * the document, a new one is created.
    */
-  mozilla::URLExtraData* DefaultStyleAttrURLData();
+  URLExtraData* DefaultStyleAttrURLData();
 
   /**
    * Get/Set the base target of a link in a document.
    */
   void GetBaseTarget(nsAString& aBaseTarget) const {
     aBaseTarget = mBaseTarget;
   }
 
@@ -999,19 +995,17 @@ class Document : public nsINode,
    */
   void SetHasUnsafeEvalCSP(bool aHasUnsafeEvalCSP) {
     mHasUnsafeEvalCSP = aHasUnsafeEvalCSP;
   }
 
   /**
    * Get the content blocking log.
    */
-  mozilla::dom::ContentBlockingLog* GetContentBlockingLog() {
-    return &mContentBlockingLog;
-  }
+  ContentBlockingLog* GetContentBlockingLog() { return &mContentBlockingLog; }
 
   /**
    * Get tracking content blocked flag for this document.
    */
   bool GetHasTrackingContentBlocked() {
     return mContentBlockingLog.HasBlockedAnyOfType(
         nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT);
   }
@@ -1321,29 +1315,27 @@ class Document : public nsINode,
   /**
    * Find the content node for which aDocument is a sub document.
    */
   Element* FindContentForSubDocument(Document* aDocument) const;
 
   /**
    * Return the doctype for this document.
    */
-  mozilla::dom::DocumentType* GetDoctype() const;
+  DocumentType* GetDoctype() const;
 
   /**
    * Return the root element for this document.
    */
   Element* GetRootElement() const;
 
-  mozilla::dom::Selection* GetSelection(mozilla::ErrorResult& aRv);
-
-  already_AddRefed<mozilla::dom::Promise> HasStorageAccess(
-      mozilla::ErrorResult& aRv);
-  already_AddRefed<mozilla::dom::Promise> RequestStorageAccess(
-      mozilla::ErrorResult& aRv);
+  Selection* GetSelection(ErrorResult& aRv);
+
+  already_AddRefed<Promise> HasStorageAccess(ErrorResult& aRv);
+  already_AddRefed<Promise> RequestStorageAccess(ErrorResult& aRv);
 
   /**
    * Gets the event target to dispatch key events to if there is no focused
    * content in the document.
    */
   virtual Element* GetUnfocusedKeyEventTarget();
 
   /**
@@ -1354,17 +1346,17 @@ class Document : public nsINode,
    * of returning it directly.
    *
    * @param aDisplaySize size of the on-screen display area for this
    * document, in device pixels.
    *
    * NOTE: If the site is optimized for mobile (via the doctype), this
    * will return viewport information that specifies default information.
    */
-  nsViewportInfo GetViewportInfo(const mozilla::ScreenIntSize& aDisplaySize);
+  nsViewportInfo GetViewportInfo(const ScreenIntSize& aDisplaySize);
 
   /**
    * It updates the viewport overflow type with the given two widths
    * and the viewport setting of the document.
    * This should only be called when there is out-of-reach overflow
    * happens on the viewport, i.e. the viewport should be using
    * `overflow: hidden`. And it should only be called on a top level
    * content document.
@@ -1393,37 +1385,36 @@ class Document : public nsINode,
 
   /*
    * Gets the srcdoc string from within the channel (assuming both exist).
    * Returns a void string if this isn't a srcdoc document or if
    * the channel has not been set.
    */
   nsresult GetSrcdocData(nsAString& aSrcdocData);
 
-  already_AddRefed<mozilla::dom::AnonymousContent> InsertAnonymousContent(
-      mozilla::dom::Element& aElement, mozilla::ErrorResult& aError);
-  void RemoveAnonymousContent(mozilla::dom::AnonymousContent& aContent,
-                              mozilla::ErrorResult& aError);
+  already_AddRefed<AnonymousContent> InsertAnonymousContent(
+      Element& aElement, ErrorResult& aError);
+  void RemoveAnonymousContent(AnonymousContent& aContent, ErrorResult& aError);
   /**
    * If aNode is a descendant of anonymous content inserted by
    * InsertAnonymousContent, this method returns the root element of the
    * inserted anonymous content (in other words, the clone of the aElement
    * that was passed to InsertAnonymousContent).
    */
   Element* GetAnonRootIfInAnonymousContentContainer(nsINode* aNode) const;
-  nsTArray<RefPtr<mozilla::dom::AnonymousContent>>& GetAnonymousContents() {
+  nsTArray<RefPtr<AnonymousContent>>& GetAnonymousContents() {
     return mAnonymousContents;
   }
 
-  mozilla::TimeStamp GetPageUnloadingEventTimeStamp() const {
+  TimeStamp GetPageUnloadingEventTimeStamp() const {
     if (!mParentDocument) {
       return mPageUnloadingEventTimeStamp;
     }
 
-    mozilla::TimeStamp parentTimeStamp(
+    TimeStamp parentTimeStamp(
         mParentDocument->GetPageUnloadingEventTimeStamp());
     if (parentTimeStamp.IsNull()) {
       return mPageUnloadingEventTimeStamp;
     }
 
     if (!mPageUnloadingEventTimeStamp ||
         parentTimeStamp < mPageUnloadingEventTimeStamp) {
       return parentTimeStamp;
@@ -1449,19 +1440,19 @@ class Document : public nsINode,
   // when it is destroyed)
   void UnscheduleSVGForPresAttrEvaluation(SVGElement* aSVG) {
     mLazySVGPresElements.RemoveEntry(aSVG);
   }
 
   // Resolve all SVG pres attrs scheduled in ScheduleSVGForPresAttrEvaluation
   void ResolveScheduledSVGPresAttrs();
 
-  mozilla::Maybe<mozilla::dom::ClientInfo> GetClientInfo() const;
-  mozilla::Maybe<mozilla::dom::ClientState> GetClientState() const;
-  mozilla::Maybe<mozilla::dom::ServiceWorkerDescriptor> GetController() const;
+  Maybe<ClientInfo> GetClientInfo() const;
+  Maybe<ClientState> GetClientState() const;
+  Maybe<ServiceWorkerDescriptor> GetController() const;
 
   // Returns the size of the mBlockedNodesByClassifier array.
   //
   // This array contains nodes that have been blocked to prevent user tracking,
   // fingerprinting, cryptomining, etc. They most likely have had their
   // nsIChannel canceled by the URL classifier (Safebrowsing).
   //
   // A script can subsequently use GetBlockedNodesByClassifier()
@@ -1526,35 +1517,35 @@ class Document : public nsINode,
   void RetrieveRelevantHeaders(nsIChannel* aChannel);
 
   void TryChannelCharset(nsIChannel* aChannel, int32_t& aCharsetSource,
                          NotNull<const Encoding*>& aEncoding,
                          nsHtml5TreeOpExecutor* aExecutor);
 
   void DispatchContentLoadedEvents();
 
-  void DispatchPageTransition(mozilla::dom::EventTarget* aDispatchTarget,
+  void DispatchPageTransition(EventTarget* aDispatchTarget,
                               const nsAString& aType, bool aPersisted,
                               bool aOnlySystemGroup = false);
 
   // Call this before the document does something that will unbind all content.
   // That will stop us from doing a lot of work as each element is removed.
   void DestroyElementMaps();
 
   Element* GetRootElementInternal() const;
   void DoNotifyPossibleTitleChange();
 
   void SetPageUnloadingEventTimeStamp() {
     MOZ_ASSERT(!mPageUnloadingEventTimeStamp);
-    mPageUnloadingEventTimeStamp = mozilla::TimeStamp::NowLoRes();
+    mPageUnloadingEventTimeStamp = TimeStamp::NowLoRes();
   }
 
   void CleanUnloadEventsTimeStamp() {
     MOZ_ASSERT(mPageUnloadingEventTimeStamp);
-    mPageUnloadingEventTimeStamp = mozilla::TimeStamp();
+    mPageUnloadingEventTimeStamp = TimeStamp();
   }
 
   /**
    * Clears any Servo element data stored on Elements in the document.
    */
   void ClearStaleServoData();
 
   /**
@@ -1614,128 +1605,125 @@ class Document : public nsINode,
     ~SelectorCache();
 
    private:
     nsDataHashtable<nsStringHashKey, SelectorList> mTable;
   };
 
   SelectorCache& GetSelectorCache() {
     if (!mSelectorCache) {
-      mSelectorCache = mozilla::MakeUnique<SelectorCache>(
-          EventTargetFor(mozilla::TaskCategory::Other));
+      mSelectorCache =
+          MakeUnique<SelectorCache>(EventTargetFor(TaskCategory::Other));
     }
     return *mSelectorCache;
   }
   // Get the root <html> element, or return null if there isn't one (e.g.
   // if the root isn't <html>)
   Element* GetHtmlElement() const;
   // Returns the first child of GetHtmlContent which has the given tag,
   // or nullptr if that doesn't exist.
   Element* GetHtmlChildElement(nsAtom* aTag);
   // Get the canonical <body> element, or return null if there isn't one (e.g.
   // if the root isn't <html> or if the <body> isn't there)
-  mozilla::dom::HTMLBodyElement* GetBodyElement();
+  HTMLBodyElement* GetBodyElement();
   // Get the canonical <head> element, or return null if there isn't one (e.g.
   // if the root isn't <html> or if the <head> isn't there)
   Element* GetHeadElement() { return GetHtmlChildElement(nsGkAtoms::head); }
   // Get the "body" in the sense of document.body: The first <body> or
   // <frameset> that's a child of a root <html>
   nsGenericHTMLElement* GetBody();
   // Set the "body" in the sense of document.body.
-  void SetBody(nsGenericHTMLElement* aBody, mozilla::ErrorResult& rv);
+  void SetBody(nsGenericHTMLElement* aBody, ErrorResult& rv);
   // Get the "head" element in the sense of document.head.
-  mozilla::dom::HTMLSharedElement* GetHead();
+  HTMLSharedElement* GetHead();
 
   /**
    * Accessors to the collection of stylesheets owned by this document.
    * Style sheets are ordered, most significant last.
    */
 
-  mozilla::dom::StyleSheetList* StyleSheets() {
+  StyleSheetList* StyleSheets() {
     return &DocumentOrShadowRoot::EnsureDOMStyleSheets();
   }
 
-  void InsertSheetAt(size_t aIndex, mozilla::StyleSheet&);
+  void InsertSheetAt(size_t aIndex, StyleSheet&);
 
   /**
    * Replace the stylesheets in aOldSheets with the stylesheets in
    * aNewSheets. The two lists must have equal length, and the sheet
    * at positon J in the first list will be replaced by the sheet at
    * position J in the second list.  Some sheets in the second list
    * may be null; if so the corresponding sheets in the first list
    * will simply be removed.
    */
-  void UpdateStyleSheets(nsTArray<RefPtr<mozilla::StyleSheet>>& aOldSheets,
-                         nsTArray<RefPtr<mozilla::StyleSheet>>& aNewSheets);
+  void UpdateStyleSheets(nsTArray<RefPtr<StyleSheet>>& aOldSheets,
+                         nsTArray<RefPtr<StyleSheet>>& aNewSheets);
 
   /**
    * Add a stylesheet to the document
    *
    * TODO(emilio): This is only used by parts of editor that are no longer in
    * use by m-c or c-c, so remove.
    */
-  void AddStyleSheet(mozilla::StyleSheet* aSheet) {
+  void AddStyleSheet(StyleSheet* aSheet) {
     MOZ_ASSERT(aSheet);
     InsertSheetAt(SheetCount(), *aSheet);
   }
 
   /**
    * Remove a stylesheet from the document
    */
-  void RemoveStyleSheet(mozilla::StyleSheet* aSheet);
+  void RemoveStyleSheet(StyleSheet* aSheet);
 
   /**
    * Notify the document that the applicable state of the sheet changed
    * and that observers should be notified and style sets updated
    */
-  void SetStyleSheetApplicableState(mozilla::StyleSheet* aSheet,
-                                    bool aApplicable);
+  void SetStyleSheetApplicableState(StyleSheet* aSheet, bool aApplicable);
 
   enum additionalSheetType {
     eAgentSheet,
     eUserSheet,
     eAuthorSheet,
     AdditionalSheetTypeCount
   };
 
   nsresult LoadAdditionalStyleSheet(additionalSheetType aType,
                                     nsIURI* aSheetURI);
   nsresult AddAdditionalStyleSheet(additionalSheetType aType,
-                                   mozilla::StyleSheet* aSheet);
+                                   StyleSheet* aSheet);
   void RemoveAdditionalStyleSheet(additionalSheetType aType, nsIURI* sheetURI);
 
-  mozilla::StyleSheet* GetFirstAdditionalAuthorSheet() {
+  StyleSheet* GetFirstAdditionalAuthorSheet() {
     return mAdditionalSheets[eAuthorSheet].SafeElementAt(0);
   }
 
   /**
    * Assuming that aDocSheets is an array of document-level style
    * sheets for this document, returns the index that aSheet should
    * be inserted at to maintain document ordering.
    *
    * Type T has to cast to StyleSheet*.
    *
    * Defined in DocumentInlines.h.
    */
   template <typename T>
   size_t FindDocStyleSheetInsertionPoint(const nsTArray<T>& aDocSheets,
-                                         const mozilla::StyleSheet& aSheet);
+                                         const StyleSheet& aSheet);
 
   /**
    * Get this document's CSSLoader.  This is guaranteed to not return null.
    */
-  mozilla::css::Loader* CSSLoader() const { return mCSSLoader; }
+  css::Loader* CSSLoader() const { return mCSSLoader; }
 
   /**
    * Get this document's StyleImageLoader.  This is guaranteed to not return
    * null.
    */
-  mozilla::css::ImageLoader* StyleImageLoader() const {
-    return mStyleImageLoader;
-  }
+  css::ImageLoader* StyleImageLoader() const { return mStyleImageLoader; }
 
   /**
    * Get the channel that was passed to StartDocumentLoad or Reset for this
    * document.  Note that this may be null in some cases (eg if
    * StartDocumentLoad or Reset were never called)
    */
   nsIChannel* GetChannel() const { return mChannel; }
 
@@ -1819,17 +1807,17 @@ class Document : public nsINode,
     return window ? window->WindowID() : 0;
   }
 
   bool IsTopLevelWindowInactive() const;
 
   /**
    * Get the script loader for this document
    */
-  mozilla::dom::ScriptLoader* ScriptLoader() { return mScriptLoader; }
+  dom::ScriptLoader* ScriptLoader() { return mScriptLoader; }
 
   /**
    * Add/Remove an element to the document's id and name hashes
    */
   void AddToIdTable(Element* aElement, nsAtom* aId);
   void RemoveFromIdTable(Element* aElement, nsAtom* aId);
   void AddToNameTable(Element* aElement, nsAtom* aName);
   void RemoveFromNameTable(Element* aElement, nsAtom* aName);
@@ -1876,17 +1864,17 @@ class Document : public nsINode,
   void FullscreenStackPop();
 
   /**
    * Called when a frame in a child process has entered fullscreen or when a
    * fullscreen frame in a child process changes to another origin.
    * aFrameElement is the frame element which contains the child-process
    * fullscreen document.
    */
-  nsresult RemoteFrameFullscreenChanged(mozilla::dom::Element* aFrameElement);
+  nsresult RemoteFrameFullscreenChanged(Element* aFrameElement);
 
   /**
    * Called when a frame in a remote child document has rolled back fullscreen
    * so that all its fullscreen element stacks are empty; we must continue the
    * rollback in this parent process' doc tree branch which is fullscreen.
    * Note that only one branch of the document tree can have its documents in
    * fullscreen state at one time. We're in inconsistent state if a
    * fullscreen document has a parent and that parent isn't fullscreen. We
@@ -1954,28 +1942,27 @@ class Document : public nsINode,
 
   void RequestPointerLock(Element* aElement, CallerType);
   bool SetPointerLock(Element* aElement, StyleCursorKind);
 
   static void UnlockPointer(Document* aDoc = nullptr);
 
   // ScreenOrientation related APIs
 
-  void SetCurrentOrientation(mozilla::dom::OrientationType aType,
-                             uint16_t aAngle) {
+  void SetCurrentOrientation(OrientationType aType, uint16_t aAngle) {
     mCurrentOrientationType = aType;
     mCurrentOrientationAngle = aAngle;
   }
 
   uint16_t CurrentOrientationAngle() const { return mCurrentOrientationAngle; }
-  mozilla::dom::OrientationType CurrentOrientationType() const {
+  OrientationType CurrentOrientationType() const {
     return mCurrentOrientationType;
   }
-  void SetOrientationPendingPromise(mozilla::dom::Promise* aPromise);
-  mozilla::dom::Promise* GetOrientationPendingPromise() const {
+  void SetOrientationPendingPromise(Promise* aPromise);
+  Promise* GetOrientationPendingPromise() const {
     return mOrientationPendingPromise;
   }
 
   //----------------------------------------------------------------------
 
   // Document notification API's
 
   /**
@@ -2019,54 +2006,50 @@ class Document : public nsINode,
 
   void SetAncestorLoading(bool aAncestorIsLoading);
   void NotifyLoading(const bool& aCurrentParentIsLoading,
                      bool aNewParentIsLoading, const ReadyState& aCurrentState,
                      ReadyState aNewState);
 
   // notify that a content node changed state.  This must happen under
   // a scriptblocker but NOT within a begin/end update.
-  void ContentStateChanged(nsIContent* aContent,
-                           mozilla::EventStates aStateMask);
+  void ContentStateChanged(nsIContent* aContent, EventStates aStateMask);
 
   // Notify that a document state has changed.
   // This should only be called by callers whose state is also reflected in the
   // implementation of Document::GetDocumentState.
-  void DocumentStatesChanged(mozilla::EventStates aStateMask);
+  void DocumentStatesChanged(EventStates aStateMask);
 
   // Observation hooks for style data to propagate notifications
   // to document observers
-  void StyleRuleChanged(mozilla::StyleSheet* aStyleSheet,
-                        mozilla::css::Rule* aStyleRule);
-  void StyleRuleAdded(mozilla::StyleSheet* aStyleSheet,
-                      mozilla::css::Rule* aStyleRule);
-  void StyleRuleRemoved(mozilla::StyleSheet* aStyleSheet,
-                        mozilla::css::Rule* aStyleRule);
+  void StyleRuleChanged(StyleSheet* aStyleSheet, css::Rule* aStyleRule);
+  void StyleRuleAdded(StyleSheet* aStyleSheet, css::Rule* aStyleRule);
+  void StyleRuleRemoved(StyleSheet* aStyleSheet, css::Rule* aStyleRule);
 
   /**
    * Flush notifications for this document and its parent documents
    * (since those may affect the layout of this one).
    */
-  void FlushPendingNotifications(mozilla::FlushType aType);
+  void FlushPendingNotifications(FlushType aType);
 
   /**
    * Another variant of the above FlushPendingNotifications.  This function
    * takes a ChangesToFlush to specify whether throttled animations are flushed
    * or not.
    * If in doublt, use the above FlushPendingNotifications.
    */
-  void FlushPendingNotifications(mozilla::ChangesToFlush aFlush);
+  void FlushPendingNotifications(ChangesToFlush aFlush);
 
   /**
    * Calls FlushPendingNotifications on any external resources this document
    * has. If this document has no external resources or is an external resource
    * itself this does nothing. This should only be called with
    * aType >= FlushType::Style.
    */
-  void FlushExternalResources(mozilla::FlushType aType);
+  void FlushExternalResources(FlushType aType);
 
   // Triggers an update of <svg:use> element shadow trees.
   void UpdateSVGUseElementShadowTrees() {
     if (mSVGUseElementsNeedingShadowTreeUpdate.IsEmpty()) {
       return;
     }
     DoUpdateSVGUseElementShadowTrees();
   }
@@ -2316,64 +2299,62 @@ class Document : public nsINode,
    * PageTransitionEvent.webidl for a description of the |aPersisted|
    * parameter. If aDispatchStartTarget is null, the pageshow event is
    * dispatched on the ScriptGlobalObject for this document, otherwise it's
    * dispatched on aDispatchStartTarget. If |aOnlySystemGroup| is true, the
    * event is only dispatched to listeners in the system group.
    * Note: if aDispatchStartTarget isn't null, the showing state of the
    * document won't be altered.
    */
-  virtual void OnPageShow(bool aPersisted,
-                          mozilla::dom::EventTarget* aDispatchStartTarget,
+  virtual void OnPageShow(bool aPersisted, EventTarget* aDispatchStartTarget,
                           bool aOnlySystemGroup = false);
 
   /**
    * Notification that the page has been hidden, for documents which are loaded
    * into a DOM window.  This corresponds to the unloading of the document, or
    * to the document's presentation being saved but removed from an existing
    * DOM window.  This notification fires applicable DOM events to the content
    * window.  See PageTransitionEvent.webidl for a description of the
    * |aPersisted| parameter. If aDispatchStartTarget is null, the pagehide
    * event is dispatched on the ScriptGlobalObject for this document,
    * otherwise it's dispatched on aDispatchStartTarget. If |aOnlySystemGroup| is
    * true, the event is only dispatched to listeners in the system group.
    * Note: if aDispatchStartTarget isn't null, the showing state of the
    * document won't be altered.
    */
-  void OnPageHide(bool aPersisted,
-                  mozilla::dom::EventTarget* aDispatchStartTarget,
+  void OnPageHide(bool aPersisted, EventTarget* aDispatchStartTarget,
                   bool aOnlySystemGroup = false);
 
   /*
    * We record the set of links in the document that are relevant to
    * style.
    */
   /**
    * Notification that an element is a link that is relevant to style.
    */
-  void AddStyleRelevantLink(mozilla::dom::Link* aLink) {
+  void AddStyleRelevantLink(Link* aLink) {
     NS_ASSERTION(aLink, "Passing in a null link.  Expect crashes RSN!");
 #ifdef DEBUG
-    nsPtrHashKey<mozilla::dom::Link>* entry = mStyledLinks.GetEntry(aLink);
+    nsPtrHashKey<Link>* entry = mStyledLinks.GetEntry(aLink);
     NS_ASSERTION(!entry, "Document already knows about this Link!");
     mStyledLinksCleared = false;
 #endif
     mStyledLinks.PutEntry(aLink);
   }
 
   /**
    * Notification that an element is a link and its URI might have been
    * changed or the element removed. If the element is still a link relevant
    * to style, then someone must ensure that AddStyleRelevantLink is
    * (eventually) called on it again.
    */
-  void ForgetLink(mozilla::dom::Link* aLink) {
+  void ForgetLink(Link* aLink) {
     NS_ASSERTION(aLink, "Passing in a null link.  Expect crashes RSN!");
 #ifdef DEBUG
-    nsPtrHashKey<mozilla::dom::Link>* entry = mStyledLinks.GetEntry(aLink);
+    nsPtrHashKey<Link>* entry = mStyledLinks.GetEntry(aLink);
     NS_ASSERTION(entry || mStyledLinksCleared,
                  "Document knows nothing about this Link!");
 #endif
     mStyledLinks.RemoveEntry(aLink);
   }
 
   // Refreshes the hrefs of all the links in the document.
   void RefreshLinkHrefs();
@@ -2384,29 +2365,27 @@ class Document : public nsINode,
    * @param aElement canonical nsIContent pointer of the box object's element
    */
   void ClearBoxObjectFor(nsIContent* aContent);
 
   /**
    * Get the box object for an element. This is not exposed through a
    * scriptable interface except for XUL documents.
    */
-  already_AddRefed<mozilla::dom::BoxObject> GetBoxObjectFor(
-      mozilla::dom::Element* aElement, mozilla::ErrorResult& aRv);
+  already_AddRefed<BoxObject> GetBoxObjectFor(Element* aElement,
+                                              ErrorResult& aRv);
 
   /**
    * Support for window.matchMedia()
    */
 
-  already_AddRefed<mozilla::dom::MediaQueryList> MatchMedia(
-      const nsAString& aMediaQueryList, mozilla::dom::CallerType aCallerType);
-
-  mozilla::LinkedList<mozilla::dom::MediaQueryList>& MediaQueryLists() {
-    return mDOMMediaQueryLists;
-  }
+  already_AddRefed<MediaQueryList> MatchMedia(const nsAString& aMediaQueryList,
+                                              CallerType aCallerType);
+
+  LinkedList<MediaQueryList>& MediaQueryLists() { return mDOMMediaQueryLists; }
 
   /**
    * Get the compatibility mode for this document
    */
   nsCompatibility GetCompatibilityMode() const { return mCompatMode; }
 
   /**
    * Check whether we've ever fired a DOMTitleChanged event for this
@@ -2571,17 +2550,17 @@ class Document : public nsINode,
 
   /**
    * Enumerate the external resource documents associated with this document.
    * The enumerator callback should return true to continue enumerating, or
    * false to stop.  This callback will never get passed a null aDocument.
    */
   void EnumerateExternalResources(SubDocEnumFunc aCallback, void* aData);
 
-  mozilla::dom::ExternalResourceMap& ExternalResourceMap() {
+  dom::ExternalResourceMap& ExternalResourceMap() {
     return mExternalResourceMap;
   }
 
   /**
    * Return whether the document is currently showing (in the sense of
    * OnPageShow() having been called already and OnPageHide() not having been
    * called yet.
    */
@@ -2651,30 +2630,30 @@ class Document : public nsINode,
   // will have a non-null return value.
   bool HasAnimationController() { return !!mAnimationController; }
 
   // Getter for this document's SMIL Animation Controller. Performs lazy
   // initialization, if this document supports animation and if
   // mAnimationController isn't yet initialized.
   //
   // If HasAnimationController is true, this is guaranteed to return non-null.
-  mozilla::SMILAnimationController* GetAnimationController();
+  SMILAnimationController* GetAnimationController();
 
   // Gets the tracker for animations that are waiting to start.
   // Returns nullptr if there is no pending animation tracker for this document
   // which will be the case if there have never been any CSS animations or
   // transitions on elements in the document.
-  mozilla::PendingAnimationTracker* GetPendingAnimationTracker() {
+  PendingAnimationTracker* GetPendingAnimationTracker() {
     return mPendingAnimationTracker;
   }
 
   // Gets the tracker for animations that are waiting to start and
   // creates it if it doesn't already exist. As a result, the return value
   // will never be nullptr.
-  mozilla::PendingAnimationTracker* GetOrCreatePendingAnimationTracker();
+  PendingAnimationTracker* GetOrCreatePendingAnimationTracker();
 
   /**
    * Prevents user initiated events from being dispatched to the document and
    * subdocuments.
    */
   void SuppressEventHandling(uint32_t aIncrease = 1);
 
   /**
@@ -2697,17 +2676,17 @@ class Document : public nsINode,
   }
 
   /**
    * Note a ChannelEventQueue which has been suspended on the document's behalf
    * to prevent XHRs from running content scripts while event handling is
    * suppressed. The document is responsible for resuming the queue after
    * event handling is unsuppressed.
    */
-  void AddSuspendedChannelEventQueue(mozilla::net::ChannelEventQueue* aQueue);
+  void AddSuspendedChannelEventQueue(net::ChannelEventQueue* aQueue);
 
   void SetHasDelayedRefreshEvent() { mHasDelayedRefreshEvent = true; }
 
   /**
    * Flag whether we're about to fire the window's load event for this document.
    */
   void SetLoadEventFiring(bool aFiring) { mLoadEventFiring = aFiring; }
 
@@ -2855,45 +2834,45 @@ class Document : public nsINode,
    */
   void ForgetImagePreload(nsIURI* aURI);
 
   /**
    * Called by nsParser to preload style sheets.  Can also be merged into the
    * parser if and when the parser is merged with libgklayout.  aCrossOriginAttr
    * should be a void string if the attr is not present.
    */
-  void PreloadStyle(nsIURI* aURI, const mozilla::Encoding* aEncoding,
+  void PreloadStyle(nsIURI* aURI, const Encoding* aEncoding,
                     const nsAString& aCrossOriginAttr,
                     ReferrerPolicyEnum aReferrerPolicy,
                     const nsAString& aIntegrity);
 
   /**
    * Called by the chrome registry to load style sheets.  Can be put
    * back there if and when when that module is merged with libgklayout.
    *
    * This always does a synchronous load.  If aIsAgentSheet is true,
    * it also uses the system principal and enables unsafe rules.
    * DO NOT USE FOR UNTRUSTED CONTENT.
    */
   nsresult LoadChromeSheetSync(nsIURI* aURI, bool aIsAgentSheet,
-                               RefPtr<mozilla::StyleSheet>* aSheet);
+                               RefPtr<StyleSheet>* aSheet);
 
   /**
    * Returns true if the locale used for the document specifies a direction of
    * right to left. For chrome documents, this comes from the chrome registry.
    * This is used to determine the current state for the :-moz-locale-dir
    * pseudoclass so once can know whether a document is expected to be rendered
    * left-to-right or right-to-left.
    */
   virtual bool IsDocumentRightToLeft() { return false; }
 
   /**
    * Called by Parser for link rel=preconnect
    */
-  void MaybePreconnect(nsIURI* uri, mozilla::CORSMode aCORSMode);
+  void MaybePreconnect(nsIURI* uri, CORSMode aCORSMode);
 
   enum DocumentTheme {
     Doc_Theme_Uninitialized,  // not determined yet
     Doc_Theme_None,
     Doc_Theme_Neutral,
     Doc_Theme_Dark,
     Doc_Theme_Bright
   };
@@ -2926,17 +2905,17 @@ class Document : public nsINode,
     return MediaDocumentKind::NotMedia;
   }
 
   /**
    * Returns the document state.
    * Document state bits have the form NS_DOCUMENT_STATE_* and are declared in
    * Document.h.
    */
-  mozilla::EventStates GetDocumentState() const { return mDocumentState; }
+  EventStates GetDocumentState() const { return mDocumentState; }
 
   nsISupports* GetCurrentContentSink();
 
   void SetAutoFocusElement(Element* aAutoFocusElement);
   void TriggerAutoFocus();
 
   void SetScrollToRef(nsIURI* aDocumentURI);
   void ScrollToRef();
@@ -2947,17 +2926,17 @@ class Document : public nsINode,
   }
 
   using DocumentOrShadowRoot::GetElementById;
   using DocumentOrShadowRoot::GetElementsByClassName;
   using DocumentOrShadowRoot::GetElementsByTagName;
   using DocumentOrShadowRoot::GetElementsByTagNameNS;
 
   DocumentTimeline* Timeline();
-  mozilla::LinkedList<DocumentTimeline>& Timelines() { return mTimelines; }
+  LinkedList<DocumentTimeline>& Timelines() { return mTimelines; }
 
   void GetAnimations(nsTArray<RefPtr<Animation>>& aAnimations);
 
   SVGSVGElement* GetSVGRootElement() const;
 
   nsresult ScheduleFrameRequestCallback(FrameRequestCallback& aCallback,
                                         int32_t* aHandle);
   void CancelFrameRequestCallback(int32_t aHandle);
@@ -2974,17 +2953,17 @@ class Document : public nsINode,
    * throttled. We throttle requestAnimationFrame for documents which aren't
    * visible (e.g. scrolled out of the viewport).
    */
   bool ShouldThrottleFrameRequests();
 
   // This returns true when the document tree is being teared down.
   bool InUnlinkOrDeletion() { return mInUnlinkOrDeletion; }
 
-  mozilla::dom::ImageTracker* ImageTracker();
+  dom::ImageTracker* ImageTracker();
 
   // AddPlugin adds a plugin-related element to mPlugins when the element is
   // added to the tree.
   void AddPlugin(nsIObjectLoadingContent* aPlugin) {
     MOZ_ASSERT(aPlugin);
     mPlugins.PutEntry(aPlugin);
   }
 
@@ -3142,18 +3121,18 @@ class Document : public nsINode,
    * Creates a new element in the HTML namespace with a local name given by
    * aTag.
    */
   already_AddRefed<Element> CreateHTMLElement(nsAtom* aTag);
 
   // WebIDL API
   nsIGlobalObject* GetParentObject() const { return GetScopeObject(); }
   static already_AddRefed<Document> Constructor(const GlobalObject& aGlobal,
-                                                mozilla::ErrorResult& rv);
-  DOMImplementation* GetImplementation(mozilla::ErrorResult& rv);
+