Merge mozilla-central to mozilla-inbound. a=merge on a CLOSED TREE
authorDaniel Varga <dvarga@mozilla.com>
Fri, 11 Jan 2019 06:23:02 +0200
changeset 510501 9cc1cf173fcee8c559488a0c5b8beda7d32a6734
parent 510500 b62455787e3f0de267d2bd99764e633ac470df72 (current diff)
parent 510482 6317367156ddbc537c700dbd9a8ebdf1e371ae8a (diff)
child 510502 340d5146c4052a47c5aa4f70817dc3ee9fd4e7da
push id10547
push userffxbld-merge
push dateMon, 21 Jan 2019 13:03:58 +0000
treeherdermozilla-beta@24ec1916bffe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone66.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to mozilla-inbound. a=merge on a CLOSED TREE
build/moz.configure/pgo.configure
devtools/server/actors/thread.js
devtools/server/actors/worker/worker-list.js
layout/base/PresShell.cpp
layout/base/nsIPresShell.h
layout/base/nsLayoutUtils.cpp
testing/marionette/harness/marionette_harness/www/cssTransform.html
testing/marionette/harness/marionette_harness/www/cssTransform2.html
testing/web-platform/meta/FileAPI/file/__dir__.ini
testing/web-platform/meta/html/semantics/forms/form-submission-0/__dir__.ini
testing/web-platform/meta/referrer-policy/no-referrer-when-downgrade/attr-referrer/same-origin/http-http/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/meta-referrer/same-origin/http-http/iframe-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/strict-origin/http-rp/cross-origin/http-https/iframe-tag/__dir__.ini
--- a/.clang-format-ignore
+++ b/.clang-format-ignore
@@ -156,21 +156,17 @@ security/nss/.*
 security/sandbox/chromium/.*
 security/sandbox/chromium-shim/.*
 testing/gtest/gmock/.*
 testing/gtest/gtest/.*
 testing/talos/talos/tests/dromaeo/.*
 testing/talos/talos/tests/kraken/.*
 testing/talos/talos/tests/v8_7/.*
 testing/web-platform/tests/resources/webidl2/.*
-third_party/aom/.*
-third_party/msgpack/.*
-third_party/prio/.*
-third_party/python/.*
-third_party/rust/.*
+third_party/.*
 toolkit/components/jsoncpp/.*
 toolkit/components/protobuf/.*
 toolkit/components/url-classifier/chromium/.*
 toolkit/components/url-classifier/protobuf/.*
 toolkit/crashreporter/breakpad-client/.*
 toolkit/crashreporter/google-breakpad/.*
 toolkit/recordreplay/udis86/.*
 tools/fuzzing/libfuzzer/.*
--- a/browser/base/content/browser-contentblocking.js
+++ b/browser/base/content/browser-contentblocking.js
@@ -647,41 +647,41 @@ var ContentBlocking = {
     let baseURL = Services.urlFormatter.formatURLPref("app.support.baseURL");
     this.reportBreakageLearnMore.href = baseURL + "blocking-breakage";
 
     this.updateAnimationsEnabled = () => {
       this.iconBox.toggleAttribute("animationsenabled",
         Services.prefs.getBoolPref(this.PREF_ANIMATIONS_ENABLED, false));
     };
 
-    for (let blocker of this.blockers) {
-      if (blocker.init) {
-        blocker.init();
-      }
-    }
-
-    this.updateAnimationsEnabled();
-
-    Services.prefs.addObserver(this.PREF_ANIMATIONS_ENABLED, this.updateAnimationsEnabled);
-
     XPCOMUtils.defineLazyPreferenceGetter(this, "showBlockedLabels",
       this.PREF_SHOW_BLOCKED_LABELS, false, () => {
         for (let blocker of this.blockers) {
           blocker.updateCategoryLabel();
         }
     });
     XPCOMUtils.defineLazyPreferenceGetter(this, "showAllowedLabels",
       this.PREF_SHOW_ALLOWED_LABELS, false, () => {
         for (let blocker of this.blockers) {
           blocker.updateCategoryLabel();
         }
     });
     XPCOMUtils.defineLazyPreferenceGetter(this, "reportBreakageEnabled",
       this.PREF_REPORT_BREAKAGE_ENABLED, false);
 
+    for (let blocker of this.blockers) {
+      if (blocker.init) {
+        blocker.init();
+      }
+    }
+
+    this.updateAnimationsEnabled();
+
+    Services.prefs.addObserver(this.PREF_ANIMATIONS_ENABLED, this.updateAnimationsEnabled);
+
     this.appMenuLabel.setAttribute("value", this.strings.appMenuTitle);
     this.appMenuLabel.setAttribute("tooltiptext", this.strings.appMenuTooltip);
 
     this.updateCBCategoryLabel = this.updateCBCategoryLabel.bind(this);
     this.updateCBCategoryLabel();
     Services.prefs.addObserver(this.PREF_CB_CATEGORY, this.updateCBCategoryLabel);
   },
 
--- a/browser/branding/aurora/branding.nsi
+++ b/browser/branding/aurora/branding.nsi
@@ -10,18 +10,19 @@
 # instead of BrandFullName and typically should not be modified.
 !define BrandFullNameInternal "Firefox Developer Edition"
 !define BrandShortName        "Firefox Developer Edition"
 !define BrandFullName         "Firefox Developer Edition"
 !define CompanyName           "mozilla.org"
 !define URLInfoAbout          "https://www.mozilla.org"
 !define HelpLink              "https://support.mozilla.org"
 
-!define URLStubDownload32 "https://download.mozilla.org/?os=win&lang=${AB_CD}&product=firefox-devedition-latest"
-!define URLStubDownload64 "https://download.mozilla.org/?os=win64&lang=${AB_CD}&product=firefox-devedition-latest"
+!define URLStubDownloadX86 "https://download.mozilla.org/?os=win&lang=${AB_CD}&product=firefox-devedition-latest"
+!define URLStubDownloadAMD64 "https://download.mozilla.org/?os=win64&lang=${AB_CD}&product=firefox-devedition-latest"
+!define URLStubDownloadAArch64 "https://download.mozilla.org/?os=win64-aarch64&lang=${AB_CD}&product=firefox-devedition-latest"
 !define URLManualDownload "https://www.mozilla.org/${AB_CD}/firefox/installer-help/?channel=aurora&installer_lang=${AB_CD}"
 !define URLSystemRequirements "https://www.mozilla.org/firefox/system-requirements/"
 !define Channel "aurora"
 
 # The installer's certificate name and issuer expected by the stub installer
 !define CertNameDownload   "Mozilla Corporation"
 !define CertIssuerDownload "DigiCert SHA2 Assured ID Code Signing CA"
 
--- a/browser/branding/nightly/branding.nsi
+++ b/browser/branding/nightly/branding.nsi
@@ -9,18 +9,19 @@
 # BrandFullNameInternal is used for some registry and file system values
 # instead of BrandFullName and typically should not be modified.
 !define BrandFullNameInternal "Nightly"
 !define BrandFullName         "Firefox Nightly"
 !define CompanyName           "mozilla.org"
 !define URLInfoAbout          "https://www.mozilla.org"
 !define HelpLink              "https://support.mozilla.org"
 
-!define URLStubDownload32 "https://download.mozilla.org/?os=win&lang=${AB_CD}&product=firefox-nightly-latest"
-!define URLStubDownload64 "https://download.mozilla.org/?os=win64&lang=${AB_CD}&product=firefox-nightly-latest"
+!define URLStubDownloadX86 "https://download.mozilla.org/?os=win&lang=${AB_CD}&product=firefox-nightly-latest"
+!define URLStubDownloadAMD64 "https://download.mozilla.org/?os=win64&lang=${AB_CD}&product=firefox-nightly-latest"
+!define URLStubDownloadAArch64 "https://download.mozilla.org/?os=win64-aarch64&lang=${AB_CD}&product=firefox-nightly-latest"
 !define URLManualDownload "https://www.mozilla.org/${AB_CD}/firefox/installer-help/?channel=nightly&installer_lang=${AB_CD}"
 !define URLSystemRequirements "https://www.mozilla.org/firefox/system-requirements/"
 !define Channel "nightly"
 
 # The installer's certificate name and issuer expected by the stub installer
 !define CertNameDownload   "Mozilla Corporation"
 !define CertIssuerDownload "DigiCert SHA2 Assured ID Code Signing CA"
 
--- a/browser/branding/official/branding.nsi
+++ b/browser/branding/official/branding.nsi
@@ -14,18 +14,19 @@
 !define URLInfoAbout          "https://www.mozilla.org"
 !define URLUpdateInfo         "https://www.mozilla.org/firefox/${AppVersion}/releasenotes"
 !define HelpLink              "https://support.mozilla.org"
 
 ; The OFFICIAL define is a workaround to support different urls for Release and
 ; Beta since they share the same branding when building with other branches that
 ; set the update channel to beta.
 !define OFFICIAL
-!define URLStubDownload32 "https://download.mozilla.org/?os=win&lang=${AB_CD}&product=firefox-latest"
-!define URLStubDownload64 "https://download.mozilla.org/?os=win64&lang=${AB_CD}&product=firefox-latest"
+!define URLStubDownloadX86 "https://download.mozilla.org/?os=win&lang=${AB_CD}&product=firefox-latest"
+!define URLStubDownloadAMD64 "https://download.mozilla.org/?os=win64&lang=${AB_CD}&product=firefox-latest"
+!define URLStubDownloadAArch64 "https://download.mozilla.org/?os=win64-aarch64&lang=${AB_CD}&product=firefox-latest"
 !define URLManualDownload "https://www.mozilla.org/${AB_CD}/firefox/installer-help/?channel=release&installer_lang=${AB_CD}"
 !define URLSystemRequirements "https://www.mozilla.org/firefox/system-requirements/"
 !define Channel "release"
 
 # The installer's certificate name and issuer expected by the stub installer
 !define CertNameDownload   "Mozilla Corporation"
 !define CertIssuerDownload "DigiCert SHA2 Assured ID Code Signing CA"
 
--- a/browser/branding/unofficial/branding.nsi
+++ b/browser/branding/unofficial/branding.nsi
@@ -9,18 +9,19 @@
 # BrandFullNameInternal is used for some registry and file system values
 # instead of BrandFullName and typically should not be modified.
 !define BrandFullNameInternal "Mozilla Developer Preview"
 !define BrandFullName         "Mozilla Developer Preview"
 !define CompanyName           "mozilla.org"
 !define URLInfoAbout          "https://www.mozilla.org"
 !define HelpLink              "https://support.mozilla.org"
 
-!define URLStubDownload32 "https://download.mozilla.org/?os=win&lang=${AB_CD}&product=firefox-latest"
-!define URLStubDownload64 "https://download.mozilla.org/?os=win64&lang=${AB_CD}&product=firefox-latest"
+!define URLStubDownloadX86 "https://download.mozilla.org/?os=win&lang=${AB_CD}&product=firefox-latest"
+!define URLStubDownloadAMD64 "https://download.mozilla.org/?os=win64&lang=${AB_CD}&product=firefox-latest"
+!define URLStubDownloadAArch64 "https://download.mozilla.org/?os=win64-aarch64&lang=${AB_CD}&product=firefox-latest"
 !define URLManualDownload "https://www.mozilla.org/${AB_CD}/firefox/installer-help/?channel=release&installer_lang=${AB_CD}"
 !define URLSystemRequirements "https://www.mozilla.org/firefox/system-requirements/"
 !define Channel "unofficial"
 
 # The installer's certificate name and issuer expected by the stub installer
 !define CertNameDownload   "Mozilla Corporation"
 !define CertIssuerDownload "DigiCert SHA2 Assured ID Code Signing CA"
 
--- a/browser/components/extensions/test/browser/browser_ext_optionsPage_popups.js
+++ b/browser/components/extensions/test/browser/browser_ext_optionsPage_popups.js
@@ -83,19 +83,24 @@ add_task(async function test_tab_options
   const optionsBrowser = gBrowser.selectedBrowser.contentDocument.getElementById("addon-options");
 
   const contentAreaContextMenu = await openContextMenuInOptionsPage(optionsBrowser);
 
   let contextMenuItemIds = [
     "context-openlinkintab",
     "context-openlinkprivate",
     "context-copylink",
-    "context-openlinkinusercontext-menu",
   ];
 
+  // Test that the "open link in container" menu is available if the containers are enabled
+  // (which is the default on Nightly, but not on Beta).
+  if (Services.prefs.getBoolPref("privacy.userContext.enabled")) {
+    contextMenuItemIds.push("context-openlinkinusercontext-menu");
+  }
+
   for (const itemID of contextMenuItemIds) {
     const item = contentAreaContextMenu.querySelector(`#${itemID}`);
 
     ok(!item.hidden, `${itemID} should not be hidden`);
     ok(!item.disabled, `${itemID} should not be disabled`);
   }
 
   const menuDetails = await extension.awaitMessage("extension-menus-onShown");
--- a/browser/components/payments/res/components/currency-amount.js
+++ b/browser/components/payments/res/components/currency-amount.js
@@ -1,14 +1,12 @@
 /* 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";
-
 /**
  * <currency-amount value="7.5" currency="USD" display-code></currency-amount>
  */
 
 import ObservedPropertiesMixin from "../mixins/ObservedPropertiesMixin.js";
 
 export default class CurrencyAmount extends ObservedPropertiesMixin(HTMLElement) {
   static get observedAttributes() {
--- a/browser/components/preferences/in-content/tests/browser_contentblocking.js
+++ b/browser/components/preferences/in-content/tests/browser_contentblocking.js
@@ -247,16 +247,20 @@ add_task(async function testContentBlock
   default:
     ok(false, "Unexpected default value found for " + NCB_PREF + ": " + defaultNCB);
     break;
   }
   Services.prefs.setIntPref(NCB_PREF, nonDefaultNCB);
   await TestUtils.waitForCondition(() => Services.prefs.prefHasUserValue(NCB_PREF));
   is(Services.prefs.getStringPref(CAT_PREF), "custom", `${CAT_PREF} has been set to custom`);
 
+  for (let pref of prefs) {
+    SpecialPowers.clearUserPref(pref);
+  }
+
   gBrowser.removeCurrentTab();
 });
 
 function checkControlState(doc, controls, enabled) {
   for (let selector of controls) {
     for (let control of doc.querySelectorAll(selector)) {
       if (enabled) {
         ok(!control.hasAttribute("disabled"), `${selector} is enabled.`);
--- a/browser/components/sessionstore/test/browser_456342_sample.xhtml
+++ b/browser/components/sessionstore/test/browser_456342_sample.xhtml
@@ -22,18 +22,25 @@
 <input type="button" name="button"/>
 <input type="password" name="password"/>
 <input type="PassWord" name="password2"/>
 <input type="PASSWORD" name="password3"/>
 <input autocomplete="off" name="auto1"/>
 <input type="text" autocomplete="OFF" name="auto2"/>
 <input type="text" autocomplete="   OFF   " name="auto5"/>
 <input autocomplete="   off   " name="auto6"/>
+<input autocomplete=" cc-CSC " name="auto7"/>
+<input autocomplete=" NEW-password " name="auto8"/>
 <textarea autocomplete="off" name="auto3"/>
 <select autocomplete="off" name="auto4">
   <option value="1" selected="true"/>
   <option value="2"/>
   <option value="3"/>
 </select>
+<select autocomplete="cc-CSC" name="CSC">
+  <option value="123" selected="true"/>
+  <option value="234"/>
+  <option value="345"/>
+</select>
 </form>
 
 </body>
 </html>
--- a/browser/installer/windows/nsis/stub.nsi
+++ b/browser/installer/windows/nsis/stub.nsi
@@ -26,17 +26,16 @@ ManifestDPIAware true
 
 Var Dialog
 Var Progressbar
 Var ProgressbarMarqueeIntervalMS
 Var CheckboxSetAsDefault
 Var CheckboxShortcuts
 Var CheckboxSendPing
 Var CheckboxInstallMaintSvc
-Var DroplistArch
 Var LabelBlurb
 Var BgBitmapImage
 Var HwndBgBitmapControl
 Var CurrentBlurbIdx
 Var CheckboxCleanupProfile
 
 Var FontInstalling
 Var FontBlurb
@@ -82,22 +81,26 @@ Var ExistingProfile
 Var ExistingVersion
 Var ExistingBuildID
 Var DownloadedBytes
 Var DownloadRetryCount
 Var OpenedDownloadPage
 Var DownloadServerIP
 Var PostSigningData
 Var PreviousInstallDir
-Var PreviousInstallArch
 Var ProfileCleanupPromptType
 Var ProfileCleanupHeaderString
 Var ProfileCleanupButtonString
 Var AppLaunchWaitTickCount
 
+!define ARCH_X86 1
+!define ARCH_AMD64 2
+!define ARCH_AARCH64 3
+Var ArchToInstall
+
 ; Uncomment the following to prevent pinging the metrics server when testing
 ; the stub installer
 ;!define STUB_DEBUG
 
 !define StubURLVersion "v8"
 
 ; Successful install exit code
 !define ERR_SUCCESS 0
@@ -236,20 +239,22 @@ Var AppLaunchWaitTickCount
 ; Must be included after defines.nsi
 !include "locale-fonts.nsh"
 
 ; The OFFICIAL define is a workaround to support different urls for Release and
 ; Beta since they share the same branding when building with other branches that
 ; set the update channel to beta.
 !ifdef OFFICIAL
 !ifdef BETA_UPDATE_CHANNEL
-!undef URLStubDownload32
-!undef URLStubDownload64
-!define URLStubDownload32 "https://download.mozilla.org/?os=win&lang=${AB_CD}&product=firefox-beta-latest"
-!define URLStubDownload64 "https://download.mozilla.org/?os=win64&lang=${AB_CD}&product=firefox-beta-latest"
+!undef URLStubDownloadX86
+!undef URLStubDownloadAMD64
+!undef URLStubDownloadAArch64
+!define URLStubDownloadX86 "https://download.mozilla.org/?os=win&lang=${AB_CD}&product=firefox-beta-latest"
+!define URLStubDownloadAMD64 "https://download.mozilla.org/?os=win64&lang=${AB_CD}&product=firefox-beta-latest"
+!define URLStubDownloadAArch64 "https://download.mozilla.org/?os=win64-aarch64&lang=${AB_CD}&product=firefox-beta-latest"
 !undef URLManualDownload
 !define URLManualDownload "https://www.mozilla.org/${AB_CD}/firefox/installer-help/?channel=beta&installer_lang=${AB_CD}"
 !undef Channel
 !define Channel "beta"
 !endif
 !endif
 
 !undef INSTALL_BLURB_TEXT_COLOR
@@ -321,45 +326,47 @@ Function .onInit
 
   ; SSE2 CPU support
   ${If} "$R7" == "0"
     MessageBox MB_OKCANCEL|MB_ICONSTOP "$(WARN_MIN_SUPPORTED_CPU_MSG)" IDCANCEL +2
     ExecShell "open" "${URLSystemRequirements}"
     Quit
   ${EndIf}
 
-  Call ShouldInstall64Bit
-  ${If} $0 == 1
-    StrCpy $DroplistArch "$(VERSION_64BIT)"
+  Call GetArchToInstall
+  ${If} $ArchToInstall == ${ARCH_AARCH64}
+  ${OrIf} $ArchToInstall == ${ARCH_AMD64}
     StrCpy $INSTDIR "${DefaultInstDir64bit}"
   ${Else}
-    StrCpy $DroplistArch "$(VERSION_32BIT)"
     StrCpy $INSTDIR "${DefaultInstDir32bit}"
   ${EndIf}
 
   ; Require elevation if the user can elevate
   ${ElevateUAC}
 
   ; If we have any existing installation, use its location as the default
   ; path for this install, even if it's not the same architecture.
   SetRegView 32
   SetShellVarContext all ; Set SHCTX to HKLM
   ${GetSingleInstallPath} "Software\Mozilla\${BrandFullNameInternal}" $R9
 
   ${If} "$R9" == "false"
-  ${AndIf} ${RunningX64}
-    SetRegView 64
-    ${GetSingleInstallPath} "Software\Mozilla\${BrandFullNameInternal}" $R9
+    ${If} ${IsNativeAMD64}
+    ${OrIf} ${IsNativeARM64}
+      SetRegView 64
+      ${GetSingleInstallPath} "Software\Mozilla\${BrandFullNameInternal}" $R9
+    ${EndIf}
   ${EndIf}
 
   ${If} "$R9" == "false"
     SetShellVarContext current ; Set SHCTX to HKCU
     ${GetSingleInstallPath} "Software\Mozilla\${BrandFullNameInternal}" $R9
 
-    ${If} ${RunningX64}
+    ${If} ${IsNativeAMD64}
+    ${OrIf} ${IsNativeARM64}
       ; In HKCU there is no WOW64 redirection, which means we may have gotten
       ; the path to a 32-bit install even though we're 64-bit.
       ; In that case, just use the default path instead of offering an upgrade.
       ; But only do that override if the existing install is in Program Files,
       ; because that's the only place we can be sure is specific
       ; to either 32 or 64 bit applications.
       ; The WordFind syntax below searches for the first occurence of the
       ; "delimiter" (the Program Files path) in the install path and returns
@@ -368,35 +375,33 @@ Function .onInit
       ${WordFind} $R9 $PROGRAMFILES32 "+1{" $0
       ${If} $0 == ""
         StrCpy $R9 "false"
       ${EndIf}
     ${EndIf}
   ${EndIf}
 
   StrCpy $PreviousInstallDir ""
-  StrCpy $PreviousInstallArch ""
   ${If} "$R9" != "false"
     ; Don't override the default install path with an existing installation
     ; of a different architecture.
-    System::Call "*(i)p.r0"
-    StrCpy $1 "$R9\${FileMainEXE}"
-    System::Call "Kernel32::GetBinaryTypeW(w r1, p r0)i"
-    System::Call "*$0(i.r2)"
-    System::Free $0
+    StrCpy $0 $R9
+    Call GetExistingInstallArch
 
-    ${If} $2 == "6" ; 6 == SCS_64BIT_BINARY
-    ${AndIf} ${RunningX64}
+    ${If} $0 == ${ARCH_X86}
+    ${AndIf} $ArchToInstall == ${ARCH_X86}
       StrCpy $PreviousInstallDir "$R9"
-      StrCpy $PreviousInstallArch "64"
       StrCpy $INSTDIR "$PreviousInstallDir"
-    ${ElseIf} $2 == "0" ; 0 == SCS_32BIT_BINARY
-    ${AndIfNot} ${RunningX64}
+    ${ElseIf} $0 == ${ARCH_AMD64}
+    ${AndIf} $ArchToInstall == ${ARCH_AMD64}
       StrCpy $PreviousInstallDir "$R9"
-      StrCpy $PreviousInstallArch "32"
+      StrCpy $INSTDIR "$PreviousInstallDir"
+    ${ElseIf} $0 == ${ARCH_AARCH64}
+    ${AndIf} $ArchToInstall == ${ARCH_AARCH64}
+      StrCpy $PreviousInstallDir "$R9"
       StrCpy $INSTDIR "$PreviousInstallDir"
     ${EndIf}
   ${EndIf}
 
   ; Used to determine if the default installation directory was used.
   StrCpy $InitialInstallDir "$INSTDIR"
 
   ClearErrors
@@ -897,22 +902,26 @@ Function createInstall
   LockWindow off
   nsDialogs::Show
 
   ${NSD_FreeImage} $BgBitmapImage
 FunctionEnd
 
 Function StartDownload
   ${NSD_KillTimer} StartDownload
-  ${If} $DroplistArch == "$(VERSION_64BIT)"
-    InetBgDL::Get "${URLStubDownload64}${URLStubDownloadAppend}" \
+  ${If} $ArchToInstall == ${ARCH_AMD64}
+    InetBgDL::Get "${URLStubDownloadAMD64}${URLStubDownloadAppend}" \
+                  "$PLUGINSDIR\download.exe" \
+                  /CONNECTTIMEOUT 120 /RECEIVETIMEOUT 120 /END
+  ${ElseIf} $ArchToInstall == ${ARCH_AARCH64}
+    InetBgDL::Get "${URLStubDownloadAArch64}${URLStubDownloadAppend}" \
                   "$PLUGINSDIR\download.exe" \
                   /CONNECTTIMEOUT 120 /RECEIVETIMEOUT 120 /END
   ${Else}
-    InetBgDL::Get "${URLStubDownload32}${URLStubDownloadAppend}" \
+    InetBgDL::Get "${URLStubDownloadX86}${URLStubDownloadAppend}" \
                   "$PLUGINSDIR\download.exe" \
                   /CONNECTTIMEOUT 120 /RECEIVETIMEOUT 120 /END
   ${EndIf}
   StrCpy $4 ""
   ${NSD_CreateTimer} OnDownload ${DownloadIntervalMS}
   ${If} ${FileExists} "$INSTDIR\${TO_BE_DELETED}"
     RmDir /r "$INSTDIR\${TO_BE_DELETED}"
   ${EndIf}
@@ -1288,23 +1297,25 @@ Function SendPing
     ; Get the seconds elapsed from the end of the pre-installation check phase
     ; to the completion of the installation phase.
     ${GetSecondsElapsed} "$EndPreInstallPhaseTickCount" "$EndInstallPhaseTickCount" $3
 
     ; Get the seconds elapsed from the end of the installation phase to the
     ; completion of all phases.
     ${GetSecondsElapsed} "$EndInstallPhaseTickCount" "$EndFinishPhaseTickCount" $4
 
-    ${If} $DroplistArch == "$(VERSION_64BIT)"
+    ${If} $ArchToInstall == ${ARCH_AMD64}
+    ${OrIf} $ArchToInstall == ${ARCH_AARCH64}
       StrCpy $R0 "1"
     ${Else}
       StrCpy $R0 "0"
     ${EndIf}
 
-    ${If} ${RunningX64}
+    ${If} ${IsNativeAMD64}
+    ${OrIf} ${IsNativeARM64}
       StrCpy $R1 "1"
     ${Else}
       StrCpy $R1 "0"
     ${EndIf}
 
     ; Though these values are sometimes incorrect due to bug 444664 it happens
     ; so rarely it isn't worth working around it by reading the registry values.
     ${WinVerGetMajor} $5
@@ -1754,17 +1765,18 @@ Function OpenManualDownloadURL
   ExecShell "open" "${URLManualDownload}${URLManualDownloadAppend}"
 FunctionEnd
 
 Function ShouldPromptForProfileCleanup
   ; This will be our return value.
   StrCpy $ProfileCleanupPromptType 0
 
   ; Only consider installations of the same architecture we're installing.
-  ${If} $DroplistArch == "$(VERSION_64BIT)"
+  ${If} $ArchToInstall == ${ARCH_AMD64}
+  ${OrIf} $ArchToInstall == ${ARCH_AARCH64}
     SetRegView 64
   ${Else}
     SetRegView 32
   ${EndIf}
 
   ; Make sure $APPDATA is the user's AppData and not ProgramData.
   ; We'll set this back to all at the end of the function.
   SetShellVarContext current
@@ -1905,26 +1917,69 @@ Function GetLatestReleasedVersion
   ${EndSelect}
   nsJSON::Get "Output" $1 /end
   IfErrors end
   Pop $1
 
   end:
 FunctionEnd
 
-; Returns 1 in $0 if we should install the 64-bit build, or 0 if not.
-; The requirements for selecting the 64-bit build to install are:
+Function GetExistingInstallArch
+  StrCpy $0 "unknown"
+
+  ClearErrors
+  FileOpen $R1 "$0\install.log" r
+  ${If} ${Errors}
+    Return
+  ${EndIf}
+
+  ${Do}
+    ClearErrors
+    FileReadUTF16LE $R1 $R2
+    ${If} ${Errors}
+      ${Break}
+    ${EndIf}
+
+    ClearErrors
+    ${WordFind} "$R2" "Target CPU : " "E+1}" $R3
+    ${If} ${Errors}
+      ${Continue}
+    ${EndIf}
+
+    ${TrimNewLines} "$R3" $R3
+    ${If} $R3 == "x86"
+      StrCpy $0 ${ARCH_X86}
+    ${ElseIf} $R3 == "x64"
+      StrCpy $0 ${ARCH_AMD64}
+    ${ElseIf} $R3 == "AArch64"
+      StrCpy $0 ${ARCH_AARCH64}
+    ${EndIf}
+    ${Break}
+  ${Loop}
+
+  FileClose $R1
+FunctionEnd
+
+; Determine which architecture build we should download and install.
+; AArch64 is always selected if it's the native architecture of the machine.
+; Otherwise, we check a few things to determine if AMD64 is appropriate:
 ; 1) Running a 64-bit OS (we've already checked the OS version).
 ; 2) An amount of RAM strictly greater than RAM_NEEDED_FOR_64BIT
 ; 3) No third-party products installed that cause issues with the 64-bit build.
 ;    Currently this includes Lenovo OneKey Theater and Lenovo Energy Management.
-Function ShouldInstall64Bit
-  StrCpy $0 0
+; If any of those checks fail, the 32-bit x86 build is selected.
+Function GetArchToInstall
+  StrCpy $ArchToInstall ${ARCH_X86}
 
-  ${IfNot} ${RunningX64}
+  ${If} ${IsNativeARM64}
+    StrCpy $ArchToInstall ${ARCH_AARCH64}
+    Return
+  ${EndIf}
+
+  ${IfNot} ${IsNativeAMD64}
     Return
   ${EndIf}
 
   System::Call "*(i 64, i, l 0, l, l, l, l, l, l)p.r1"
   System::Call "Kernel32::GlobalMemoryStatusEx(p r1)"
   System::Call "*$1(i, i, l.r2, l, l, l, l, l, l)"
   System::Free $1
   ${If} $2 L<= ${RAM_NEEDED_FOR_64BIT}
@@ -1936,13 +1991,13 @@ Function ShouldInstall64Bit
   ${If} ${FileExists} "$PROGRAMFILES32\Lenovo\Onekey Theater\windowsapihookdll64.dll"
     Return
   ${EndIf}
 
   ${If} ${FileExists} "$PROGRAMFILES32\Lenovo\Energy Management\Energy Management.exe"
     Return
   ${EndIf}
 
-  StrCpy $0 1
+  StrCpy $ArchToInstall ${ARCH_AMD64}
 FunctionEnd
 
 Section
 SectionEnd
--- a/browser/locales/en-US/chrome/browser/aboutPrivateBrowsing.dtd
+++ b/browser/locales/en-US/chrome/browser/aboutPrivateBrowsing.dtd
@@ -26,8 +26,15 @@
 <!ENTITY aboutPrivateBrowsing.learnMore3.before          "Learn more about ">
 <!ENTITY aboutPrivateBrowsing.learnMore3.title           "Private Browsing">
 <!ENTITY aboutPrivateBrowsing.learnMore3.after           ".">
 
 <!ENTITY trackingProtection.startTour1                   "See how it works">
 
 <!ENTITY contentBlocking.title                           "Content Blocking">
 <!ENTITY contentBlocking.description                     "Some websites use trackers that can monitor your activity across the Internet. In private windows, Firefox Content Blocking automatically blocks many trackers that can collect information about your browsing behavior.">
+
+<!-- Strings for new Private Browsing with Search -->
+<!ENTITY aboutPrivateBrowsing.info.title                 "You’re in a Private Window">
+<!ENTITY aboutPrivateBrowsing.info.description           "&brandShortName; clears your search and browsing history when you quit the app or close all Private Browsing tabs and windows. While this doesn’t make you anonymous to websites or your internet service provider, it makes it easier to keep what you do online private from anyone else who uses this computer.">
+<!-- LOCALIZATION NOTE (aboutPrivateBrowsing.info.myths): This is a link to a SUMO article
+                       about private browsing myths.   -->
+<!ENTITY aboutPrivateBrowsing.info.myths                 "Common myths about private browsing">
--- a/browser/locales/en-US/installer/nsisstrings.properties
+++ b/browser/locales/en-US/installer/nsisstrings.properties
@@ -42,11 +42,8 @@ WARN_DISK_SPACE_QUIT=You don't have suff
 WARN_MANUALLY_CLOSE_APP_LAUNCH=$BrandShortName is already running.\n\nPlease close $BrandShortName prior to launching the version you have just installed.
 
 ERROR_DOWNLOAD_CONT=Hmm. For some reason, we could not install $BrandShortName.\nChoose OK to start over.
 
 STUB_CANCEL_PROMPT_HEADING=Do you want to install $BrandShortName?
 STUB_CANCEL_PROMPT_MESSAGE=If you cancel, $BrandShortName will not be installed.
 STUB_CANCEL_PROMPT_BUTTON_CONTINUE=Install $BrandShortName
 STUB_CANCEL_PROMPT_BUTTON_EXIT=Cancel
-
-VERSION_32BIT=32-bit $BrandShortName
-VERSION_64BIT=64-bit $BrandShortName
--- a/build/moz.configure/init.configure
+++ b/build/moz.configure/init.configure
@@ -846,38 +846,29 @@ def target_variables(target):
     elif target.kernel == 'Darwin' or (target.kernel == 'Linux' and
                                        target.os == 'GNU'):
         os_target = target.kernel
         os_arch = target.kernel
     else:
         os_target = target.os
         os_arch = target.kernel
 
-    if target.kernel == 'Darwin' and target.cpu == 'x86':
-        os_test = 'i386'
-    else:
-        os_test = target.raw_cpu
-
     return namespace(
         OS_TARGET=os_target,
         OS_ARCH=os_arch,
-        OS_TEST=os_test,
         INTEL_ARCHITECTURE=target.cpu in ('x86', 'x86_64') or None,
     )
 
 
 set_config('OS_TARGET', target_variables.OS_TARGET)
 add_old_configure_assignment('OS_TARGET',
                              target_variables.OS_TARGET)
 set_config('OS_ARCH', target_variables.OS_ARCH)
 add_old_configure_assignment('OS_ARCH',
                              target_variables.OS_ARCH)
-set_config('OS_TEST', target_variables.OS_TEST)
-add_old_configure_assignment('OS_TEST',
-                             target_variables.OS_TEST)
 set_config('CPU_ARCH', target.cpu)
 add_old_configure_assignment('CPU_ARCH', target.cpu)
 set_config('INTEL_ARCHITECTURE', target_variables.INTEL_ARCHITECTURE)
 set_config('TARGET_CPU', target.raw_cpu)
 set_config('TARGET_OS', target.raw_os)
 set_config('TARGET_ENDIANNESS', target.endianness)
 
 
@@ -1137,17 +1128,16 @@ def milestone(build_env, build_project, 
                      is_release_or_beta=is_release_or_beta,
                      app_version=app_version,
                      app_version_display=app_version_display)
 
 
 set_config('GRE_MILESTONE', milestone.version)
 set_config('NIGHTLY_BUILD', milestone.is_nightly)
 set_define('NIGHTLY_BUILD', milestone.is_nightly)
-add_old_configure_assignment('NIGHTLY_BUILD', milestone.is_nightly)
 set_config('RELEASE_OR_BETA', milestone.is_release_or_beta)
 set_define('RELEASE_OR_BETA', milestone.is_release_or_beta)
 add_old_configure_assignment('RELEASE_OR_BETA',
                              milestone.is_release_or_beta)
 set_define('MOZILLA_VERSION', depends(milestone)(lambda m: '"%s"' % m.version))
 set_config('MOZILLA_VERSION', milestone.version)
 set_define('MOZILLA_VERSION_U', milestone.version)
 set_define('MOZILLA_UAVERSION', depends(milestone)(lambda m: '"%s"' % m.uaversion))
--- a/build/moz.configure/memory.configure
+++ b/build/moz.configure/memory.configure
@@ -53,17 +53,16 @@ def replace_malloc(value, jemalloc, mile
     if build_project == 'memory':
         return True
     if milestone.is_nightly and jemalloc and build_project != 'js':
         return True
 
 
 set_config('MOZ_REPLACE_MALLOC', replace_malloc)
 set_define('MOZ_REPLACE_MALLOC', replace_malloc)
-add_old_configure_assignment('MOZ_REPLACE_MALLOC', replace_malloc)
 
 
 @depends(replace_malloc, build_project)
 def replace_malloc_static(replace_malloc, build_project):
     # Default to statically linking replace-malloc libraries that can be
     # statically linked, except when building with --enable-project=memory.
     if replace_malloc and build_project != 'memory':
         return True
deleted file mode 100644
--- a/build/moz.configure/pgo.configure
+++ /dev/null
@@ -1,58 +0,0 @@
-# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# 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/.
-
-llvm_profdata = check_prog('LLVM_PROFDATA', ['llvm-profdata'],
-                           allow_missing=True)
-
-add_old_configure_assignment('LLVM_PROFDATA', llvm_profdata)
-
-# PGO
-# ==============================================================
-js_option('--enable-profile-generate',
-          help='Build a PGO instrumented binary')
-
-imply_option('MOZ_PGO',
-             depends_if('--enable-profile-generate')(lambda _: True))
-
-set_config('MOZ_PROFILE_GENERATE',
-           depends_if('--enable-profile-generate')(lambda _: True))
-
-js_option('--enable-profile-use',
-          help='Use a generated profile during the build')
-
-js_option('--with-pgo-profile-path',
-          help='Path to the (unmerged) profile path to use during the build',
-          nargs=1)
-
-imply_option('MOZ_PGO',
-             depends_if('--enable-profile-use')(lambda _: True))
-
-set_config('MOZ_PROFILE_USE',
-           depends_if('--enable-profile-use')(lambda _: True))
-
-js_option(env='MOZ_PGO', help='Build with profile guided optimizations')
-
-set_config('MOZ_PGO', depends('MOZ_PGO')(lambda x: bool(x)))
-add_old_configure_assignment('MOZ_PGO', depends('MOZ_PGO')(lambda x: bool(x)))
-
-@depends('--with-pgo-profile-path', '--enable-profile-use', 'LLVM_PROFDATA')
-def pgo_profile_path(path, pgo_use, profdata):
-    if not path:
-        return
-    if path and not pgo_use:
-        die('Pass --enable-profile-use to use --with-pgo-profile-path.')
-    if path and not profdata:
-        die('LLVM_PROFDATA must be set to process the pgo profile.')
-    return path[0]
-
-set_config('PGO_PROFILE_PATH', pgo_profile_path)
-
-option('--with-pgo-jarlog',
-       help='Use the provided jarlog file when packaging during a profile-use '
-            'build',
-       nargs=1)
-
-set_config('PGO_JARLOG_PATH', depends_if('--with-pgo-jarlog')(lambda p: p))
--- a/build/moz.configure/rust.configure
+++ b/build/moz.configure/rust.configure
@@ -256,17 +256,16 @@ def rust_target_env_name(triple):
 # converting a string to uppercase.
 set_config('RUST_TARGET_ENV_NAME', rust_target_env_name)
 
 # This is used for putting source info into symbol files.
 set_config('RUSTC_COMMIT', depends(rustc_info)(lambda i: i.commit))
 
 # Until we remove all the other Rust checks in old-configure.
 add_old_configure_assignment('RUSTC', rustc)
-add_old_configure_assignment('RUST_TARGET', rust_target_triple)
 
 # Rustdoc is required by Rust tests below.
 js_option(env='RUSTDOC', nargs=1, help='Path to the rustdoc program')
 
 rustdoc = check_prog('RUSTDOC', add_rustup_path('rustdoc'),
                      input='RUSTDOC', allow_missing=True)
 
 # This option is separate from --enable-tests because Rust tests are particularly
--- a/build/moz.configure/toolchain.configure
+++ b/build/moz.configure/toolchain.configure
@@ -17,17 +17,16 @@ js_option('--enable-release',
 def developer_options(value):
     if not value:
         return True
 
 
 add_old_configure_assignment('DEVELOPER_OPTIONS', developer_options)
 set_config('DEVELOPER_OPTIONS', developer_options)
 
-
 # Code optimization
 # ==============================================================
 
 js_option('--disable-optimize',
           nargs='?',
           help='Disable optimizations via compiler flags')
 
 
@@ -372,19 +371,16 @@ def compiler_wrapper(wrapper, ccache):
         if wrapper:
             return tuple([ccache] + wrapper)
         else:
             return (ccache,)
     elif wrapper:
         return tuple(wrapper)
 
 
-add_old_configure_assignment('COMPILER_WRAPPER', compiler_wrapper)
-
-
 @depends_if(compiler_wrapper)
 def using_compiler_wrapper(compiler_wrapper):
     return True
 
 
 set_config('MOZ_USING_COMPILER_WRAPPER', using_compiler_wrapper)
 
 
@@ -746,44 +742,68 @@ def vc_compiler_path(host, target, vs_ma
         # Choose the newest version.
         data = all_versions[-1][1]
     paths = data.get(vc_target)
     if not paths:
         return
     return paths
 
 
-@depends(vc_compiler_path)
+@dependable
+@imports('os')
+@imports(_from='os', _import='environ')
+def original_path():
+    return environ['PATH'].split(os.pathsep)
+
+
+@depends(vc_compiler_path, original_path)
 @imports('os')
 @imports(_from='os', _import='environ')
-def toolchain_search_path(vc_compiler_path):
-    result = [environ.get('PATH')]
+def toolchain_search_path(vc_compiler_path, original_path):
+    result = list(original_path)
 
     if vc_compiler_path:
-        result.extend(vc_compiler_path)
+        # The second item, if there is one, is necessary to have in $PATH for
+        # Windows to load the required DLLs from there.
+        if len(vc_compiler_path) > 1:
+            environ['PATH'] = os.pathsep.join(result + vc_compiler_path[1:])
+
+        # The first item is where the programs are going to be
+        result.append(vc_compiler_path[0])
 
     # Also add in the location to which `mach bootstrap` or
     # `mach artifact toolchain` installs clang.
     mozbuild_state_dir = environ.get('MOZBUILD_STATE_PATH',
                                      os.path.expanduser(os.path.join('~', '.mozbuild')))
     bootstrap_clang_path = os.path.join(mozbuild_state_dir, 'clang', 'bin')
     result.append(bootstrap_clang_path)
 
     bootstrap_cbindgen_path = os.path.join(mozbuild_state_dir, 'cbindgen')
     result.append(bootstrap_cbindgen_path)
 
-    if vc_compiler_path:
-        # We're going to alter PATH for good in windows.configure, but we also
-        # need to do it for the valid_compiler() check below. This is only needed
-        # on Windows, where MSVC needs PATH set to find dlls.
-        environ['PATH'] = os.pathsep.join(result)
-
     return result
 
 
+# As a workaround until bug 1516228 and bug 1516253 are fixed, set the PATH
+# variable for the build to contain the toolchain search path.
+@depends(toolchain_search_path)
+@imports('os')
+@imports(_from='os', _import='environ')
+def altered_path(toolchain_search_path):
+    path = environ['PATH'].split(os.pathsep)
+    altered_path = list(toolchain_search_path)
+    for p in path:
+        if p not in altered_path:
+            altered_path.append(p)
+    return os.pathsep.join(altered_path)
+
+
+set_config('PATH', altered_path)
+
+
 @template
 def default_c_compilers(host_or_target, other_c_compiler=None):
     '''Template defining the set of default C compilers for the host and
     target platforms.
     `host_or_target` is either `host` or `target` (the @depends functions
     from init.configure.
     `other_c_compiler` is the `target` C compiler when `host_or_target` is `host`.
     '''
@@ -988,36 +1008,16 @@ def compiler(language, host_or_target, c
             wrapper.extend(provided_wrapper)
             flags = provided_compiler.flags
         else:
             flags = []
 
         if not flags and macos_sdk and host_or_target.os == 'OSX':
             flags = ['-isysroot', macos_sdk]
 
-        # Ideally, we'd always use the absolute path, but unfortunately, on
-        # Windows, the compiler is very often in a directory containing spaces.
-        # Unfortunately, due to the way autoconf does its compiler tests with
-        # eval, that doesn't work out. So in that case, check that the
-        # compiler can still be found in $PATH, and use the file name instead
-        # of the full path.
-        if quote(compiler) != compiler:
-            full_path = os.path.abspath(compiler)
-            compiler = os.path.basename(compiler)
-            found_compiler = find_program(compiler)
-            if not found_compiler:
-                die('%s is not in your $PATH'
-                    % quote(os.path.dirname(full_path)))
-            if os.path.normcase(find_program(compiler)) != os.path.normcase(
-                    full_path):
-                die('Found `%s` before `%s` in your $PATH. '
-                    'Please reorder your $PATH.',
-                    quote(os.path.dirname(found_compiler)),
-                    quote(os.path.dirname(full_path)))
-
         info = check_compiler(wrapper + [compiler] + flags, language,
                               host_or_target)
 
         # Check that the additional flags we got are enough to not require any
         # more flags. If we get an exception, just ignore it; it's liable to be
         # invalid command-line flags, which means the compiler we're checking
         # doesn't support those command-line flags and will fail one or more of
         # the checks below.
@@ -1477,16 +1477,65 @@ set_config('PREPROCESS_OPTION', preproce
 
 @depends(target, host)
 def is_windows(target, host):
     return host.kernel == 'WINNT' and target.kernel == 'WINNT'
 
 
 include('windows.configure', when=is_windows)
 
+# PGO
+# ==============================================================
+llvm_profdata = check_prog('LLVM_PROFDATA', ['llvm-profdata'],
+                           allow_missing=True,
+                           paths=toolchain_search_path)
+
+js_option('--enable-profile-generate',
+          help='Build a PGO instrumented binary')
+
+imply_option('MOZ_PGO',
+             depends_if('--enable-profile-generate')(lambda _: True))
+
+set_config('MOZ_PROFILE_GENERATE',
+           depends_if('--enable-profile-generate')(lambda _: True))
+
+js_option('--enable-profile-use',
+          help='Use a generated profile during the build')
+
+js_option('--with-pgo-profile-path',
+          help='Path to the (unmerged) profile path to use during the build',
+          nargs=1)
+
+imply_option('MOZ_PGO',
+             depends_if('--enable-profile-use')(lambda _: True))
+
+set_config('MOZ_PROFILE_USE',
+           depends_if('--enable-profile-use')(lambda _: True))
+
+
+@depends('--with-pgo-profile-path', '--enable-profile-use', 'LLVM_PROFDATA')
+def pgo_profile_path(path, pgo_use, profdata):
+    if not path:
+        return
+    if path and not pgo_use:
+        die('Pass --enable-profile-use to use --with-pgo-profile-path.')
+    if path and not profdata:
+        die('LLVM_PROFDATA must be set to process the pgo profile.')
+    return path[0]
+
+
+set_config('PGO_PROFILE_PATH', pgo_profile_path)
+
+option('--with-pgo-jarlog',
+       help='Use the provided jarlog file when packaging during a profile-use '
+            'build',
+       nargs=1)
+
+set_config('PGO_JARLOG_PATH', depends_if('--with-pgo-jarlog')(lambda p: p))
+
 # LTO
 # ==============================================================
 
 js_option('--enable-lto',
           nargs='?',
           choices=('full', 'thin'),
           help='Enable LTO')
 
@@ -1941,17 +1990,18 @@ def as_info(target, c_compiler):
 # One would expect the assembler to be specified merely as a program.  But in
 # cases where the assembler is passed down into js/, it can be specified in
 # the same way as CC: a program + a list of argument flags.  We might as well
 # permit the same behavior in general, even though it seems somewhat unusual.
 # So we have to do the same sort of dance as we did above with
 # `provided_compiler`.
 provided_assembler = provided_program('AS')
 assembler = check_prog('_AS', input=provided_assembler.program,
-                       what='the assembler', progs=as_info.names)
+                       what='the assembler', progs=as_info.names,
+                       paths=toolchain_search_path)
 
 @depends(as_info, assembler, provided_assembler, c_compiler)
 def as_with_flags(as_info, assembler, provided_assembler, c_compiler):
     if provided_assembler:
         return provided_assembler.wrapper + \
             [provided_assembler.program] + \
             provided_assembler.flags
 
--- a/build/moz.configure/windows.configure
+++ b/build/moz.configure/windows.configure
@@ -251,37 +251,33 @@ def valid_ucrt_sdk_dir(windows_sdk_dir, 
     return namespace(
         path=sdk.path,
         include=sdk.include,
         lib=sdk.lib,
         version=version,
     )
 
 
-@depends(c_compiler)
+@depends(c_compiler, toolchain_search_path)
 @imports('os')
-def vc_path(c_compiler):
+def vc_path(c_compiler, toolchain_search_path):
     vc_path_env = os.environ.get('VC_PATH')
     if vc_path_env:
         return os.path.normpath(vc_path_env)
 
     if c_compiler.type not in ('msvc', 'clang-cl'):
         return
 
-    # Normally, we'd start from c_compiler.compiler, but for now, it's not the
-    # ideal full path to the compiler. At least, we're guaranteed find_program
-    # will get us the one we found in toolchain.configure.
     vc_program = c_compiler.compiler
 
     # In clang-cl builds, we use the headers and libraries from an MSVC installation.
     if c_compiler.type == 'clang-cl':
-        vc_program = 'cl.exe'
+        vc_program = find_program('cl.exe', paths=toolchain_search_path)
 
-    cl = find_program(vc_program)
-    result = os.path.dirname(cl)
+    result = os.path.dirname(vc_program)
     while True:
         next, p = os.path.split(result)
         if next == result:
             die('Cannot determine the Visual C++ directory the compiler (%s) '
                 'is in' % cl)
         result = next
         if p.lower() == 'bin':
             break
@@ -463,30 +459,16 @@ link = check_prog('LINKER', ('lld-link.e
                   paths=toolchain_search_path)
 
 host_link = check_prog('HOST_LINKER', ('lld-link.exe', 'link.exe'),
                        paths=toolchain_search_path)
 
 add_old_configure_assignment('LINKER', link)
 
 
-# Normally, we'd just have CC, etc. set to absolute paths, but the build system
-# doesn't currently handle properly the case where the paths contain spaces.
-# Additionally, there's the issue described in toolchain.configure, in
-# valid_compiler().
-@depends(sdk_bin_path)
-@imports('os')
-def alter_path(sdk_bin_path):
-    path = os.pathsep.join(sdk_bin_path)
-    os.environ['PATH'] = path
-    return path
-
-
-set_config('PATH', alter_path)
-
 check_prog('MAKECAB', ('makecab.exe',))
 
 
 @depends(c_compiler, using_sccache)
 def need_showincludes_prefix(info, using_sccache):
     # sccache does its own -showIncludes prefix checking.
     # clang-cl uses a gcc-style dependency scheme, see toolchain.configure.
     if info.type == 'msvc' and not using_sccache:
--- a/build/win64-aarch64/mozconfig.vs2017
+++ b/build/win64-aarch64/mozconfig.vs2017
@@ -40,8 +40,15 @@ if [ -d "${VSPATH}" ]; then
     libs="${libs} -LIBPATH:${VSWINPATH}/SDK/Lib/${win_sdk_version}/ucrt/x64"
     export HOST_LDFLAGS="${libs}"
 
     export WIN64_LINK="${VSPATH}/VC/bin/Hostx64/x64/link.exe"
     export WIN64_LIB="${VSPATH}/VC/lib/x64:${VSPATH}/VC/atlmfc/lib/x64:${VSPATH}/SDK/Lib/${win_sdk_version}/ucrt/x64:${VSPATH}/SDK/Lib/${win_sdk_version}/um/x64:${VSPATH}/DIA SDK/lib/amd64"
 fi
 
 . $topsrcdir/build/mozconfig.vs-common
+
+mk_export_correct_style WINDOWSSDKDIR
+mk_export_correct_style WIN32_REDIST_DIR
+mk_export_correct_style WIN_DIA_SDK_BIN_DIR
+mk_export_correct_style PATH
+mk_export_correct_style INCLUDE
+mk_export_correct_style LIB
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -314,25 +314,16 @@ ifdef MOZ_IOS
 _LOADER_PATH := @rpath
 else
 _LOADER_PATH := @executable_path
 endif
 EXTRA_DSO_LDOPTS	+= -dynamiclib -install_name $(_LOADER_PATH)/$(SHARED_LIBRARY) -compatibility_version 1 -current_version 1 -single_module
 endif
 endif
 
-ifeq ($(OS_ARCH),NetBSD)
-ifneq (,$(filter arc cobalt hpcmips mipsco newsmips pmax sgimips,$(OS_TEST)))
-ifneq (,$(filter layout/%,$(relativesrcdir)))
-OS_CFLAGS += -Wa,-xgot
-OS_CXXFLAGS += -Wa,-xgot
-endif
-endif
-endif
-
 ifdef SYMBOLS_FILE
 ifeq ($(OS_TARGET),WINNT)
 ifndef GNU_CC
 EXTRA_DSO_LDOPTS += -DEF:$(call normalizepath,$(SYMBOLS_FILE))
 else
 EXTRA_DSO_LDOPTS += $(call normalizepath,$(SYMBOLS_FILE))
 endif
 else
--- a/devtools/client/aboutdebugging-new/src/actions/debug-targets.js
+++ b/devtools/client/aboutdebugging-new/src/actions/debug-targets.js
@@ -198,47 +198,41 @@ function requestWorkers() {
     try {
       const {
         otherWorkers,
         serviceWorkers,
         sharedWorkers,
       } = await clientWrapper.listWorkers();
 
       for (const serviceWorker of serviceWorkers) {
-        const { registrationActor } = serviceWorker;
-        if (!registrationActor) {
+        const { registrationFront } = serviceWorker;
+        if (!registrationFront) {
           continue;
         }
 
-        const { subscription } = await clientWrapper.request({
-          to: registrationActor,
-          type: "getPushSubscription",
-        });
-
+        const subscription = await registrationFront.getPushSubscription();
         serviceWorker.subscription = subscription;
       }
 
       dispatch({
         type: REQUEST_WORKERS_SUCCESS,
         otherWorkers,
         serviceWorkers,
         sharedWorkers,
       });
     } catch (e) {
       dispatch({ type: REQUEST_WORKERS_FAILURE, error: e });
     }
   };
 }
 
-function startServiceWorker(actor) {
+function startServiceWorker(registrationFront) {
   return async (_, getState) => {
-    const clientWrapper = getCurrentClient(getState().runtimes);
-
     try {
-      await clientWrapper.request({ to: actor, type: "start" });
+      await registrationFront.start();
     } catch (e) {
       console.error(e);
     }
   };
 }
 
 module.exports = {
   inspectDebugTarget,
--- a/devtools/client/aboutdebugging-new/src/actions/runtimes.js
+++ b/devtools/client/aboutdebugging-new/src/actions/runtimes.js
@@ -21,16 +21,17 @@ const { remoteClientManager } =
 const {
   CONNECT_RUNTIME_FAILURE,
   CONNECT_RUNTIME_START,
   CONNECT_RUNTIME_SUCCESS,
   DEBUG_TARGETS,
   DISCONNECT_RUNTIME_FAILURE,
   DISCONNECT_RUNTIME_START,
   DISCONNECT_RUNTIME_SUCCESS,
+  PAGE_TYPES,
   REMOTE_RUNTIMES_UPDATED,
   RUNTIME_PREFERENCE,
   RUNTIMES,
   UNWATCH_RUNTIME_FAILURE,
   UNWATCH_RUNTIME_START,
   UNWATCH_RUNTIME_SUCCESS,
   UPDATE_CONNECTION_PROMPT_SETTING_FAILURE,
   UPDATE_CONNECTION_PROMPT_SETTING_START,
@@ -57,21 +58,19 @@ async function getRuntimeInfo(runtime, c
     icon,
     isMultiE10s,
     name,
     type,
     version,
   };
 }
 
-function onUSBDebuggerClientClosed() {
-  // After scanUSBRuntimes action, updateUSBRuntimes action is called.
-  // The closed runtime will be unwatched and disconnected explicitly in the action
-  // if needed.
-  window.AboutDebugging.store.dispatch(Actions.scanUSBRuntimes());
+function onRemoteDebuggerClientClosed() {
+  window.AboutDebugging.onNetworkLocationsUpdated();
+  window.AboutDebugging.onUSBRuntimesUpdated();
 }
 
 function onMultiE10sUpdated() {
   window.AboutDebugging.store.dispatch(updateMultiE10s());
 }
 
 function connectRuntime(id) {
   return async (dispatch, getState) => {
@@ -87,27 +86,25 @@ function connectRuntime(id) {
       const connectionPromptEnabled = await clientWrapper.getPreference(promptPrefName);
       const runtimeDetails = {
         clientWrapper,
         connectionPromptEnabled,
         info,
         isMultiE10s,
       };
 
-      clientWrapper.addListener("closed", onUSBDebuggerClientClosed);
-
       const deviceFront = await clientWrapper.getFront("device");
       if (deviceFront) {
         deviceFront.on("multi-e10s-updated", onMultiE10sUpdated);
       }
 
-      if (runtime.type === RUNTIMES.USB) {
+      if (runtime.type !== RUNTIMES.THIS_FIREFOX) {
         // `closed` event will be emitted when disabling remote debugging
-        // on the connected USB runtime.
-        clientWrapper.addOneTimeListener("closed", onUSBDebuggerClientClosed);
+        // on the connected remote runtime.
+        clientWrapper.addOneTimeListener("closed", onRemoteDebuggerClientClosed);
       }
 
       dispatch({
         type: CONNECT_RUNTIME_SUCCESS,
         runtime: {
           id,
           runtimeDetails,
           type: runtime.type,
@@ -126,18 +123,18 @@ function disconnectRuntime(id) {
       const runtime = findRuntimeById(id, getState().runtimes);
       const { clientWrapper } = runtime.runtimeDetails;
 
       const deviceFront = await clientWrapper.getFront("device");
       if (deviceFront) {
         deviceFront.off("multi-e10s-updated", onMultiE10sUpdated);
       }
 
-      if (runtime.type === RUNTIMES.USB) {
-        clientWrapper.removeListener("closed", onUSBDebuggerClientClosed);
+      if (runtime.type !== RUNTIMES.THIS_FIREFOX) {
+        clientWrapper.removeListener("closed", onRemoteDebuggerClientClosed);
       }
 
       await clientWrapper.close();
 
       dispatch({
         type: DISCONNECT_RUNTIME_SUCCESS,
         runtime: {
           id,
@@ -266,84 +263,99 @@ function updateUSBRuntimes(adbRuntimes) 
       isUnknown: adbRuntime.isUnknown(),
       name: adbRuntime.shortName,
       type: RUNTIMES.USB,
     };
   });
   return updateRemoteRuntimes(runtimes, RUNTIMES.USB);
 }
 
+/**
+ * Check that a given runtime can still be found in the provided array of runtimes, and
+ * that the connection of the associated DebuggerClient is still valid.
+ * Note that this check is only valid for runtimes which match the type of the runtimes
+ * in the array.
+ */
+function _isRuntimeValid(runtime, runtimes) {
+  const isRuntimeAvailable = runtimes.some(r => r.id === runtime.id);
+  const isConnectionValid = runtime.runtimeDetails &&
+    !runtime.runtimeDetails.clientWrapper.isClosed();
+  return isRuntimeAvailable && isConnectionValid;
+}
+
 function updateRemoteRuntimes(runtimes, type) {
   return async (dispatch, getState) => {
     const currentRuntime = getCurrentRuntime(getState().runtimes);
 
-    if (currentRuntime &&
-        currentRuntime.type === type &&
-        !runtimes.find(runtime => currentRuntime.id === runtime.id)) {
-      // Since current USB runtime was invalid, move to this firefox page.
+    // Check if the updated remote runtimes should trigger a navigation out of the current
+    // runtime page.
+    if (currentRuntime && currentRuntime.type === type &&
+      !_isRuntimeValid(currentRuntime, runtimes)) {
+      // Since current remote runtime is invalid, move to this firefox page.
       // This case is considered as followings and so on:
       // * Remove ADB addon
       // * (Physically) Disconnect USB runtime
       //
-      // The reason why we call selectPage before USB_RUNTIMES_UPDATED was fired is below.
-      // Current runtime can not be retrieved after USB_RUNTIMES_UPDATED action, since
-      // that updates runtime state. So, before that we fire selectPage action so that to
-      // transact unwatchRuntime correctly.
-
-      await dispatch(Actions.selectPage(RUNTIMES.THIS_FIREFOX, RUNTIMES.THIS_FIREFOX));
+      // The reason we call selectPage before REMOTE_RUNTIMES_UPDATED is fired is below.
+      // Current runtime can not be retrieved after REMOTE_RUNTIMES_UPDATED action, since
+      // that updates runtime state. So, before that we fire selectPage action to execute
+      // `unwatchRuntime` correctly.
+      await dispatch(Actions.selectPage(PAGE_TYPES.RUNTIME, RUNTIMES.THIS_FIREFOX));
     }
 
     // Retrieve runtimeDetails from existing runtimes.
     runtimes.forEach(runtime => {
       const existingRuntime = findRuntimeById(runtime.id, getState().runtimes);
-      runtime.runtimeDetails = existingRuntime ? existingRuntime.runtimeDetails : null;
+      const isConnectionValid = existingRuntime && existingRuntime.runtimeDetails &&
+        !existingRuntime.runtimeDetails.clientWrapper.isClosed();
+      runtime.runtimeDetails = isConnectionValid ? existingRuntime.runtimeDetails : null;
     });
 
-    // Disconnect runtimes that were no longer valid
-    const validIds = runtimes.map(r => r.id);
     const existingRuntimes = getAllRuntimes(getState().runtimes);
-    const invalidRuntimes = existingRuntimes.filter(r => {
-      return r.type === type && !validIds.includes(r.id);
-    });
-
-    for (const invalidRuntime of invalidRuntimes) {
-      const isConnected = !!invalidRuntime.runtimeDetails;
-      if (isConnected) {
-        await dispatch(disconnectRuntime(invalidRuntime.id));
+    for (const runtime of existingRuntimes) {
+      // Runtime was connected before.
+      const isConnected = runtime.runtimeDetails;
+      // Runtime is of the same type as the updated runtimes array, so we should check it.
+      const isSameType = runtime.type === type;
+      if (isConnected && isSameType && !_isRuntimeValid(runtime, runtimes)) {
+        // Disconnect runtimes that were no longer valid.
+        await dispatch(disconnectRuntime(runtime.id));
       }
     }
 
     dispatch({ type: REMOTE_RUNTIMES_UPDATED, runtimes, runtimeType: type });
 
     for (const runtime of getAllRuntimes(getState().runtimes)) {
       if (runtime.type !== type) {
         continue;
       }
 
+      // Reconnect clients already available in the RemoteClientManager.
       const isConnected = !!runtime.runtimeDetails;
       const hasConnectedClient = remoteClientManager.hasClient(runtime.id, runtime.type);
       if (!isConnected && hasConnectedClient) {
         await dispatch(connectRuntime(runtime.id));
       }
     }
   };
 }
 
 /**
  * Remove all the listeners added on client objects. Since those objects are persisted
  * regardless of the about:debugging lifecycle, all the added events should be removed
  * before leaving about:debugging.
  */
 function removeRuntimeListeners() {
   return (dispatch, getState) => {
-    const { usbRuntimes } = getState().runtimes;
-    for (const runtime of usbRuntimes) {
+    const allRuntimes = getAllRuntimes(getState().runtimes);
+    const remoteRuntimes = allRuntimes.filter(r => r.type !== RUNTIMES.THIS_FIREFOX);
+    for (const runtime of remoteRuntimes) {
       if (runtime.runtimeDetails) {
         const { clientWrapper } = runtime.runtimeDetails;
-        clientWrapper.removeListener("closed", onUSBDebuggerClientClosed);
+        clientWrapper.removeListener("closed", onRemoteDebuggerClientClosed);
       }
     }
   };
 }
 
 module.exports = {
   connectRuntime,
   disconnectRuntime,
--- a/devtools/client/aboutdebugging-new/src/actions/ui.js
+++ b/devtools/client/aboutdebugging-new/src/actions/ui.js
@@ -9,57 +9,65 @@ const {
   ADB_ADDON_INSTALL_SUCCESS,
   ADB_ADDON_INSTALL_FAILURE,
   ADB_ADDON_UNINSTALL_START,
   ADB_ADDON_UNINSTALL_SUCCESS,
   ADB_ADDON_UNINSTALL_FAILURE,
   ADB_ADDON_STATUS_UPDATED,
   DEBUG_TARGET_COLLAPSIBILITY_UPDATED,
   NETWORK_LOCATIONS_UPDATED,
-  PAGE_SELECTED,
   PAGE_TYPES,
+  SELECT_PAGE_FAILURE,
+  SELECT_PAGE_START,
+  SELECT_PAGE_SUCCESS,
   USB_RUNTIMES_SCAN_START,
   USB_RUNTIMES_SCAN_SUCCESS,
 } = require("../constants");
 
 const NetworkLocationsModule = require("../modules/network-locations");
 const { adbAddon } = require("devtools/shared/adb/adb-addon");
 const { refreshUSBRuntimes } = require("../modules/usb-runtimes");
 
 const Actions = require("./index");
 
 function selectPage(page, runtimeId) {
   return async (dispatch, getState) => {
-    const isSamePage = (oldPage, newPage) => {
-      if (newPage === PAGE_TYPES.RUNTIME && oldPage === PAGE_TYPES.RUNTIME) {
-        return runtimeId === getState().runtimes.selectedRuntimeId;
-      }
-      return newPage === oldPage;
-    };
+    dispatch({ type: SELECT_PAGE_START });
+
+    try {
+      const isSamePage = (oldPage, newPage) => {
+        if (newPage === PAGE_TYPES.RUNTIME && oldPage === PAGE_TYPES.RUNTIME) {
+          return runtimeId === getState().runtimes.selectedRuntimeId;
+        }
+        return newPage === oldPage;
+      };
 
-    const currentPage = getState().ui.selectedPage;
-    // Nothing to dispatch if the page is the same as the current page, or
-    // if we are not providing any page.
-    // Note: maybe we should have a PAGE_SELECTED_FAILURE action for proper logging
-    if (!page || isSamePage(currentPage, page)) {
-      return;
+      const currentPage = getState().ui.selectedPage;
+      // Nothing to dispatch if the page is the same as the current page, or
+      // if we are not providing any page.
+      // TODO: we should dispatch SELECT_PAGE_FAILURE if page is missing. See Bug 1518559.
+      if (!page || isSamePage(currentPage, page)) {
+        return;
+      }
+
+      // Stop watching current runtime, if currently on a RUNTIME page.
+      if (currentPage === PAGE_TYPES.RUNTIME) {
+        const currentRuntimeId = getState().runtimes.selectedRuntimeId;
+        await dispatch(Actions.unwatchRuntime(currentRuntimeId));
+      }
+
+      // Start watching current runtime, if moving to a RUNTIME page.
+      if (page === PAGE_TYPES.RUNTIME) {
+        await dispatch(Actions.watchRuntime(runtimeId));
+      }
+
+      dispatch({ type: SELECT_PAGE_SUCCESS, page, runtimeId });
+    } catch (e) {
+      dispatch({ type: SELECT_PAGE_FAILURE, error: e });
     }
-
-    // Stop watching current runtime, if currently on a RUNTIME page.
-    if (currentPage === PAGE_TYPES.RUNTIME) {
-      const currentRuntimeId = getState().runtimes.selectedRuntimeId;
-      await dispatch(Actions.unwatchRuntime(currentRuntimeId));
-    }
-
-    // Start watching current runtime, if moving to a RUNTIME page.
-    if (page === PAGE_TYPES.RUNTIME) {
-      await dispatch(Actions.watchRuntime(runtimeId));
-    }
-
-    dispatch({ type: PAGE_SELECTED, page, runtimeId });
   };
 }
 
 function updateDebugTargetCollapsibility(key, isCollapsed) {
   return { type: DEBUG_TARGET_COLLAPSIBILITY_UPDATED, key, isCollapsed };
 }
 
 function addNetworkLocation(location) {
--- a/devtools/client/aboutdebugging-new/src/components/App.js
+++ b/devtools/client/aboutdebugging-new/src/components/App.js
@@ -66,30 +66,31 @@ class App extends PureComponent {
   // We are using it to read the route path.
   // See react-router docs:
   // https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/api/match.md
   renderRuntime({ match }) {
     // Redirect to This Firefox in these cases:
     // - If the runtimepage for a device is the first page shown (since we can't
     //   keep connections open between page reloads).
     // - If no runtimeId is given.
-    // - If runtime is not found in the runtimes list (this is handled later)
+    // - If runtime is not in the runtimes list or disconnected (this is handled later)
     const isDeviceFirstPage =
       !this.props.selectedPage &&
       match.params.runtimeId !== RUNTIMES.THIS_FIREFOX;
     if (!match.params.runtimeId || isDeviceFirstPage) {
       return Redirect({ to: `/runtime/${RUNTIMES.THIS_FIREFOX}` });
     }
 
     const isRuntimeAvailable = id => {
       const runtimes = [
         ...this.props.networkRuntimes,
         ...this.props.usbRuntimes,
       ];
-      return !!runtimes.find(x => x.id === id);
+      const runtime = runtimes.find(x => x.id === id);
+      return runtime && runtime.runtimeDetails;
     };
 
     const { dispatch } = this.props;
 
     let runtimeId = match.params.runtimeId || RUNTIMES.THIS_FIREFOX;
     if (match.params.runtimeId !== RUNTIMES.THIS_FIREFOX) {
       const rawId = decodeURIComponent(match.params.runtimeId);
       if (isRuntimeAvailable(rawId)) {
--- a/devtools/client/aboutdebugging-new/src/components/debugtarget/ServiceWorkerAction.js
+++ b/devtools/client/aboutdebugging-new/src/components/debugtarget/ServiceWorkerAction.js
@@ -35,17 +35,17 @@ class ServiceWorkerAction extends PureCo
 
   push() {
     const { dispatch, target } = this.props;
     dispatch(Actions.pushServiceWorker(target.id));
   }
 
   start() {
     const { dispatch, target } = this.props;
-    dispatch(Actions.startServiceWorker(target.details.registrationActor));
+    dispatch(Actions.startServiceWorker(target.details.registrationFront));
   }
 
   _renderAction() {
     const { dispatch, runtimeDetails, target } = this.props;
     const { isActive, isRunning } = target.details;
     const { isMultiE10s } = runtimeDetails;
 
     if (!isRunning) {
--- a/devtools/client/aboutdebugging-new/src/constants.js
+++ b/devtools/client/aboutdebugging-new/src/constants.js
@@ -15,27 +15,29 @@ const actionTypes = {
   CONNECT_RUNTIME_FAILURE: "CONNECT_RUNTIME_FAILURE",
   CONNECT_RUNTIME_START: "CONNECT_RUNTIME_START",
   CONNECT_RUNTIME_SUCCESS: "CONNECT_RUNTIME_SUCCESS",
   DEBUG_TARGET_COLLAPSIBILITY_UPDATED: "DEBUG_TARGET_COLLAPSIBILITY_UPDATED",
   DISCONNECT_RUNTIME_FAILURE: "DISCONNECT_RUNTIME_FAILURE",
   DISCONNECT_RUNTIME_START: "DISCONNECT_RUNTIME_START",
   DISCONNECT_RUNTIME_SUCCESS: "DISCONNECT_RUNTIME_SUCCESS",
   NETWORK_LOCATIONS_UPDATED: "NETWORK_LOCATIONS_UPDATED",
-  PAGE_SELECTED: "PAGE_SELECTED",
   REMOTE_RUNTIMES_UPDATED: "REMOTE_RUNTIMES_UPDATED",
   REQUEST_EXTENSIONS_FAILURE: "REQUEST_EXTENSIONS_FAILURE",
   REQUEST_EXTENSIONS_START: "REQUEST_EXTENSIONS_START",
   REQUEST_EXTENSIONS_SUCCESS: "REQUEST_EXTENSIONS_SUCCESS",
   REQUEST_TABS_FAILURE: "REQUEST_TABS_FAILURE",
   REQUEST_TABS_START: "REQUEST_TABS_START",
   REQUEST_TABS_SUCCESS: "REQUEST_TABS_SUCCESS",
   REQUEST_WORKERS_FAILURE: "REQUEST_WORKERS_FAILURE",
   REQUEST_WORKERS_START: "REQUEST_WORKERS_START",
   REQUEST_WORKERS_SUCCESS: "REQUEST_WORKERS_SUCCESS",
+  SELECT_PAGE_FAILURE: "SELECT_PAGE_FAILURE",
+  SELECT_PAGE_START: "SELECT_PAGE_START",
+  SELECT_PAGE_SUCCESS: "SELECT_PAGE_SUCCESS",
   TEMPORARY_EXTENSION_INSTALL_FAILURE: "TEMPORARY_EXTENSION_INSTALL_FAILURE",
   TEMPORARY_EXTENSION_INSTALL_START: "TEMPORARY_EXTENSION_INSTALL_START",
   TEMPORARY_EXTENSION_INSTALL_SUCCESS: "TEMPORARY_EXTENSION_INSTALL_SUCCESS",
   UNWATCH_RUNTIME_FAILURE: "UNWATCH_RUNTIME_FAILURE",
   UNWATCH_RUNTIME_START: "UNWATCH_RUNTIME_START",
   UNWATCH_RUNTIME_SUCCESS: "UNWATCH_RUNTIME_SUCCESS",
   UPDATE_CONNECTION_PROMPT_SETTING_FAILURE: "UPDATE_CONNECTION_PROMPT_SETTING_FAILURE",
   UPDATE_CONNECTION_PROMPT_SETTING_START: "UPDATE_CONNECTION_PROMPT_SETTING_START",
--- a/devtools/client/aboutdebugging-new/src/middleware/debug-target-listener.js
+++ b/devtools/client/aboutdebugging-new/src/middleware/debug-target-listener.js
@@ -40,21 +40,26 @@ function debugTargetListenerMiddleware(s
         }
 
         if (isSupportedDebugTarget(runtime.type, DEBUG_TARGETS.WORKER)) {
           clientWrapper.addListener("workerListChanged", onWorkersUpdated);
           clientWrapper.onFront("contentProcessTarget", front => {
             clientWrapper.contentProcessFronts.push(front);
             front.on("workerListChanged", onWorkersUpdated);
           });
+
+          clientWrapper.onFront("serviceWorkerRegistration", front => {
+            clientWrapper.serviceWorkerRegistrationFronts.push(front);
+            front.on("push-subscription-modified", onWorkersUpdated);
+            front.on("registration-changed", onWorkersUpdated);
+          });
+
           clientWrapper.addListener("serviceWorkerRegistrationListChanged",
             onWorkersUpdated);
           clientWrapper.addListener("processListChanged", onWorkersUpdated);
-          clientWrapper.addListener("registration-changed", onWorkersUpdated);
-          clientWrapper.addListener("push-subscription-modified", onWorkersUpdated);
         }
         break;
       }
       case UNWATCH_RUNTIME_START: {
         const { runtime } = action;
         const { clientWrapper } = runtime.runtimeDetails;
 
         if (isSupportedDebugTarget(runtime.type, DEBUG_TARGETS.TAB)) {
@@ -70,19 +75,23 @@ function debugTargetListenerMiddleware(s
           clientWrapper.removeListener("serviceWorkerRegistrationListChanged",
             onWorkersUpdated);
 
           for (const front of clientWrapper.contentProcessFronts) {
             front.off("workerListChanged", onWorkersUpdated);
           }
           clientWrapper.contentProcessFronts = [];
 
+          for (const front of clientWrapper.serviceWorkerRegistrationFronts) {
+            front.off("push-subscription-modified", onWorkersUpdated);
+            front.off("registration-changed", onWorkersUpdated);
+          }
+          clientWrapper.serviceWorkerRegistrationFronts = [];
+
           clientWrapper.removeListener("processListChanged", onWorkersUpdated);
-          clientWrapper.removeListener("registration-changed", onWorkersUpdated);
-          clientWrapper.removeListener("push-subscription-modified", onWorkersUpdated);
         }
         break;
       }
     }
 
     return next(action);
   };
 }
--- a/devtools/client/aboutdebugging-new/src/middleware/worker-component-data.js
+++ b/devtools/client/aboutdebugging-new/src/middleware/worker-component-data.js
@@ -43,26 +43,26 @@ function getServiceWorkerStatus(isActive
 function toComponentData(workers, isServiceWorker) {
   return workers.map(worker => {
     // Here `worker` is the worker object created by RootFront.listAllWorkers
     const type = DEBUG_TARGETS.WORKER;
     const icon = "chrome://devtools/skin/images/debugging-workers.svg";
     let { fetch } = worker;
     const {
       name,
-      registrationActor,
+      registrationFront,
       scope,
       subscription,
       workerTargetFront,
     } = worker;
 
     // For registering service workers, workerTargetFront will not be available.
     // The only valid identifier we can use at that point is the actorID for the
     // service worker registration.
-    const id = workerTargetFront ? workerTargetFront.actorID : registrationActor;
+    const id = workerTargetFront ? workerTargetFront.actorID : registrationFront.actorID;
 
     let isActive = false;
     let isRunning = false;
     let pushServiceEndpoint = null;
     let status = null;
 
     if (isServiceWorker) {
       fetch = fetch ? SERVICE_WORKER_FETCH_STATES.LISTENING
@@ -74,17 +74,17 @@ function toComponentData(workers, isServ
     }
 
     return {
       details: {
         fetch,
         isActive,
         isRunning,
         pushServiceEndpoint,
-        registrationActor,
+        registrationFront,
         scope,
         status,
       },
       icon,
       id,
       name,
       type,
     };
--- a/devtools/client/aboutdebugging-new/src/modules/client-wrapper.js
+++ b/devtools/client/aboutdebugging-new/src/modules/client-wrapper.js
@@ -28,16 +28,17 @@ const MAIN_ROOT_EVENTS = [
  * The ClientWrapper class is used to isolate aboutdebugging from the DevTools client API
  * The modules of about:debugging should never call DevTools client APIs directly.
  */
 class ClientWrapper {
   constructor(client) {
     this.client = client;
     // Array of contentProcessTarget fronts on which we will listen for worker events.
     this.contentProcessFronts = [];
+    this.serviceWorkerRegistrationFronts = [];
   }
 
   addOneTimeListener(evt, listener) {
     if (MAIN_ROOT_EVENTS.includes(evt)) {
       this.client.mainRoot.once(evt, listener);
     } else {
       this.client.addOneTimeListener(evt, listener);
     }
@@ -126,18 +127,18 @@ class ClientWrapper {
 
     return {
       otherWorkers: other,
       serviceWorkers: service,
       sharedWorkers: shared,
     };
   }
 
-  async request(options) {
-    return this.client.request(options);
+  async close() {
+    return this.client.close();
   }
 
-  async close() {
-    return this.client.close();
+  isClosed() {
+    return this.client._closed;
   }
 }
 
 exports.ClientWrapper = ClientWrapper;
--- a/devtools/client/aboutdebugging-new/src/reducers/ui-state.js
+++ b/devtools/client/aboutdebugging-new/src/reducers/ui-state.js
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const {
   ADB_ADDON_STATUS_UPDATED,
   DEBUG_TARGET_COLLAPSIBILITY_UPDATED,
   NETWORK_LOCATIONS_UPDATED,
-  PAGE_SELECTED,
+  SELECT_PAGE_SUCCESS,
   TEMPORARY_EXTENSION_INSTALL_FAILURE,
   TEMPORARY_EXTENSION_INSTALL_SUCCESS,
   USB_RUNTIMES_SCAN_START,
   USB_RUNTIMES_SCAN_SUCCESS,
 } = require("../constants");
 
 function UiState(locations = [], debugTargetCollapsibilities = {},
                  networkEnabled = false, wifiEnabled = false,
@@ -46,17 +46,17 @@ function uiReducer(state = UiState(), ac
       return Object.assign({}, state, { debugTargetCollapsibilities });
     }
 
     case NETWORK_LOCATIONS_UPDATED: {
       const { locations } = action;
       return Object.assign({}, state, { networkLocations: locations });
     }
 
-    case PAGE_SELECTED: {
+    case SELECT_PAGE_SUCCESS: {
       const { page, runtimeId } = action;
       return Object.assign({}, state,
         { selectedPage: page, selectedRuntime: runtimeId });
     }
 
     case USB_RUNTIMES_SCAN_START: {
       return Object.assign({}, state, { isScanningUsb: true });
     }
--- a/devtools/client/aboutdebugging-new/src/types/debug-target.js
+++ b/devtools/client/aboutdebugging-new/src/types/debug-target.js
@@ -24,18 +24,18 @@ const tabTargetDetails = {
 
 const workerTargetDetails = {
   // (service worker specific) one of "LISTENING", "NOT_LISTENING". undefined otherwise.
   fetch: PropTypes.string,
   // (service worker specific) true if they reached the activated state.
   isActive: PropTypes.bool,
   // (service worker specific) true if they are currently running.
   isRunning: PropTypes.bool,
-  // actor id for the ServiceWorkerRegistration related to this service worker.
-  registrationActor: PropTypes.string,
+  // front for the ServiceWorkerRegistration related to this service worker.
+  registrationFront: PropTypes.object,
   // (service worker specific) scope of the service worker registration.
   scope: PropTypes.string,
   // (service worker specific) one of "RUNNING", "REGISTERING", "STOPPED".
   status: PropTypes.string,
 };
 
 const debugTarget = {
   // details property will contain a type-specific object.
--- a/devtools/client/aboutdebugging-new/test/browser/browser.ini
+++ b/devtools/client/aboutdebugging-new/test/browser/browser.ini
@@ -37,16 +37,17 @@ skip-if = (os == 'linux' && bits == 32) 
 [browser_aboutdebugging_debug-target-pane_usb_runtime.js]
 [browser_aboutdebugging_devtools.js]
 [browser_aboutdebugging_navigate.js]
 [browser_aboutdebugging_persist_connection.js]
 [browser_aboutdebugging_routes.js]
 [browser_aboutdebugging_runtime_connection-prompt.js]
 [browser_aboutdebugging_runtime_usbclient_closed.js]
 [browser_aboutdebugging_select_network_runtime.js]
+[browser_aboutdebugging_serviceworker_multie10s.js]
 [browser_aboutdebugging_serviceworker_push.js]
 [browser_aboutdebugging_serviceworker_pushservice_url.js]
 [browser_aboutdebugging_sidebar_network_runtimes.js]
 [browser_aboutdebugging_sidebar_usb_runtime.js]
 [browser_aboutdebugging_sidebar_usb_runtime_connect.js]
 [browser_aboutdebugging_sidebar_usb_runtime_refresh.js]
 [browser_aboutdebugging_sidebar_usb_runtime_select.js]
 [browser_aboutdebugging_sidebar_usb_status.js]
--- a/devtools/client/aboutdebugging-new/test/browser/browser_aboutdebugging_runtime_usbclient_closed.js
+++ b/devtools/client/aboutdebugging-new/test/browser/browser_aboutdebugging_runtime_usbclient_closed.js
@@ -1,41 +1,102 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 /* import-globals-from helper-mocks.js */
 Services.scriptloader.loadSubScript(CHROME_URL_ROOT + "helper-mocks.js", this);
 
-const RUNTIME_DEVICE_ID = "1234";
-const RUNTIME_DEVICE_NAME = "A device";
-const RUNTIME_NAME = "Test application";
+const NETWORK_RUNTIME_HOST = "localhost:6080";
+const NETWORK_RUNTIME_APP_NAME = "TestNetworkApp";
+const USB_RUNTIME_ID = "test-runtime-id";
+const USB_DEVICE_NAME = "test device name";
+const USB_APP_NAME = "TestApp";
 
-// Test that about:debugging navigates back to the default page when the server for the
-// current USB runtime is closed.
-add_task(async function() {
+// Test that about:debugging navigates back to the default page when a USB device is
+// unplugged.
+add_task(async function testUsbDeviceUnplugged() {
   const mocks = new Mocks();
-
   const { document, tab } = await openAboutDebugging();
 
-  const usbClient = mocks.createUSBRuntime(RUNTIME_DEVICE_ID, {
-    deviceName: RUNTIME_DEVICE_NAME,
-    name: RUNTIME_NAME,
+  mocks.createUSBRuntime(USB_RUNTIME_ID, {
+    deviceName: USB_DEVICE_NAME,
+    name: USB_APP_NAME,
   });
   mocks.emitUSBUpdate();
 
   info("Connect to and select the USB device");
-  await connectToRuntime(RUNTIME_DEVICE_NAME, document);
-  await selectRuntime(RUNTIME_DEVICE_NAME, RUNTIME_NAME, document);
+  await connectToRuntime(USB_DEVICE_NAME, document);
+  await selectRuntime(USB_DEVICE_NAME, USB_APP_NAME, document);
 
-  info("Simulate a client disconnection");
-  mocks.removeUSBRuntime(RUNTIME_DEVICE_ID);
-  usbClient._eventEmitter.emit("closed");
+  info("Simulate a device unplugged");
+  mocks.removeUSBRuntime(USB_RUNTIME_ID);
+  mocks.emitUSBUpdate();
 
   info("Wait until the sidebar item for this runtime disappears");
-  await waitUntil(() => !findSidebarItemByText(RUNTIME_DEVICE_NAME, document));
+  await waitUntil(() => !findSidebarItemByText(USB_DEVICE_NAME, document));
 
   is(document.location.hash, `#/runtime/this-firefox`,
     "Redirection to the default page (this-firefox)");
 
   await removeTab(tab);
 });
+
+// Test that about:debugging navigates back to the default page when the server for the
+// current USB runtime is closed.
+add_task(async function testUsbClientDisconnected() {
+  const mocks = new Mocks();
+  const { document, tab } = await openAboutDebugging();
+
+  const usbClient = mocks.createUSBRuntime(USB_RUNTIME_ID, {
+    deviceName: USB_DEVICE_NAME,
+    name: USB_APP_NAME,
+  });
+  mocks.emitUSBUpdate();
+
+  info("Connect to and select the USB device");
+  await connectToRuntime(USB_DEVICE_NAME, document);
+  await selectRuntime(USB_DEVICE_NAME, USB_APP_NAME, document);
+
+  info("Simulate a client disconnection");
+  usbClient.isClosed = () => true;
+  usbClient._eventEmitter.emit("closed");
+
+  info("Wait until the connect button for this runtime appears");
+  await waitUntil(() => {
+    const item = findSidebarItemByText(USB_DEVICE_NAME, document);
+    return item && item.querySelector(".js-connect-button");
+  });
+
+  is(document.location.hash, `#/runtime/this-firefox`,
+    "Redirection to the default page (this-firefox)");
+  await removeTab(tab);
+});
+
+// Test that about:debugging navigates back to the default page when the server for the
+// current network runtime is closed.
+add_task(async function testNetworkClientDisconnected() {
+  const mocks = new Mocks();
+  const { document, tab } = await openAboutDebugging();
+
+  const networkClient = mocks.createNetworkRuntime(NETWORK_RUNTIME_HOST, {
+    name: NETWORK_RUNTIME_APP_NAME,
+  });
+
+  info("Connect to and select the network runtime");
+  await connectToRuntime(NETWORK_RUNTIME_HOST, document);
+  await selectRuntime(NETWORK_RUNTIME_HOST, NETWORK_RUNTIME_APP_NAME, document);
+
+  info("Simulate a client disconnection");
+  networkClient.isClosed = () => true;
+  networkClient._eventEmitter.emit("closed");
+
+  info("Wait until the connect button for this runtime appears");
+  await waitUntil(() => {
+    const item = findSidebarItemByText(NETWORK_RUNTIME_HOST, document);
+    return item && item.querySelector(".js-connect-button");
+  });
+
+  is(document.location.hash, `#/runtime/this-firefox`,
+    "Redirection to the default page (this-firefox)");
+  await removeTab(tab);
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging-new/test/browser/browser_aboutdebugging_serviceworker_multie10s.js
@@ -0,0 +1,90 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* global sendAsyncMessage */
+
+"use strict";
+
+/* import-globals-from helper-serviceworker.js */
+Services.scriptloader.loadSubScript(CHROME_URL_ROOT + "helper-serviceworker.js", this);
+
+const SERVICE_WORKER = URL_ROOT + "resources/service-workers/push-sw.js";
+const TAB_URL = URL_ROOT + "resources/service-workers/push-sw.html";
+const PROCESS_COUNT_PREF = "dom.ipc.processCount";
+const OPTOUT_PREF = "dom.ipc.multiOptOut";
+
+// Test that debugging service workers is disabled when we are in multi e10s
+// (enabling/disabling multi e10s via process count)
+add_task(async function() {
+  const enableFn = async () => pushPref(PROCESS_COUNT_PREF, 8);
+  const disableFn = async () => pushPref(PROCESS_COUNT_PREF, 1);
+
+  await testDebuggingSW(enableFn, disableFn);
+});
+
+// Test that debugging service workers is disabled when we are in multi e10s
+// (enabling/disabling multi e10s via opt out pref)
+add_task(async function() {
+  const enableFn = async () => pushPref(OPTOUT_PREF, 0);
+  const disableFn = async () => pushPref(OPTOUT_PREF, 1);
+
+  await testDebuggingSW(enableFn, disableFn);
+});
+
+async function testDebuggingSW(enableMultiE10sFn, disableMultiE10sFn) {
+  // enable service workers
+  await pushPref("dom.serviceWorkers.testing.enabled", true);
+
+  const { document, tab } = await openAboutDebugging();
+
+  // disable multi e10s
+  info("Disabling multi e10s");
+  await disableMultiE10sFn();
+
+  // Open a tab that registers a push service worker.
+  const swTab = await addTab(TAB_URL);
+
+  info("Forward service worker messages to the test");
+  await forwardServiceWorkerMessage(swTab);
+
+  info("Wait for the service worker to claim the test window before proceeding.");
+  await onTabMessage(swTab, "sw-claimed");
+
+  info("Wait until the service worker appears and is running");
+  await waitUntil(() => {
+    const target = findDebugTargetByText(SERVICE_WORKER, document);
+    const status = target && target.querySelector(".js-worker-status");
+    return status && status.textContent === "Running";
+  });
+
+  let targetElement = findDebugTargetByText(SERVICE_WORKER, document);
+  let pushButton = targetElement.querySelector(".js-push-button");
+  ok(!pushButton.disabled, "Push button is not disabled");
+  let inspectButton = targetElement.querySelector(".js-debug-target-inspect-button");
+  ok(!inspectButton.disabled, "Inspect button is not disabled");
+
+  // enable multi e10s
+  info("Enabling multi e10s");
+  await enableMultiE10sFn();
+
+  info("Wait for debug target to re-render");
+  await waitUntil(() => {
+    targetElement = findDebugTargetByText(SERVICE_WORKER, document);
+    pushButton = targetElement.querySelector(".js-push-button");
+    return pushButton.disabled;
+  });
+
+  ok(pushButton.disabled, "Push button is disabled");
+  inspectButton = targetElement.querySelector(".js-debug-target-inspect-button");
+  ok(inspectButton.disabled, "Inspect button is disabled");
+
+  info("Unregister the service worker");
+  await unregisterServiceWorker(swTab, "pushServiceWorkerRegistration");
+
+  info("Wait until the service worker disappears from about:debugging");
+  await waitUntil(() => !findDebugTargetByText(SERVICE_WORKER, document));
+
+  info("Remove browser tabs");
+  await removeTab(swTab);
+  await removeTab(tab);
+}
--- a/devtools/client/aboutdebugging-new/test/browser/mocks/helper-client-wrapper-mock.js
+++ b/devtools/client/aboutdebugging-new/test/browser/mocks/helper-client-wrapper-mock.js
@@ -22,16 +22,17 @@ function createClientMock() {
   EventEmitter.decorate(eventEmitter);
 
   return {
     // add a reference to the internal event emitter so that consumers can fire client
     // events.
     _eventEmitter: eventEmitter,
     _preferences: {},
     contentProcessFronts: [],
+    serviceWorkerRegistrationFronts: [],
     addOneTimeListener: (evt, listener) => {
       eventEmitter.once(evt, listener);
     },
     addListener: (evt, listener) => {
       eventEmitter.on(evt, listener);
     },
     removeListener: (evt, listener) => {
       eventEmitter.off(evt, listener);
@@ -43,19 +44,20 @@ function createClientMock() {
       },
       addListener: (evt, listener) => {
         eventEmitter.on(evt, listener);
       },
       removeListener: (evt, listener) => {
         eventEmitter.off(evt, listener);
       },
     },
-
     // no-op
     close: () => {},
+    // client is not closed
+    isClosed: () => false,
     // no-op
     connect: () => {},
     // no-op
     getDeviceDescription: () => {},
     // Return default preference value or null if no match.
     getPreference: function(prefName) {
       if (prefName in this._preferences) {
         return this._preferences[prefName];
--- a/devtools/client/aboutdebugging/components/workers/Panel.js
+++ b/devtools/client/aboutdebugging/components/workers/Panel.js
@@ -58,53 +58,61 @@ class WorkersPanel extends Component {
   componentDidMount() {
     const client = this.props.client;
     // When calling RootFront.listAllWorkers, ContentProcessTargetActor are created
     // for each content process, which sends `workerListChanged` events.
     client.mainRoot.onFront("contentProcessTarget", front => {
       front.on("workerListChanged", this.updateWorkers);
       this.state.contentProcessFronts.push(front);
     });
+    client.mainRoot.onFront("serviceWorkerRegistration", front => {
+      this.state.serviceWorkerRegistrationFronts.push(front);
+      front.on("push-subscription-modified", this.updateWorkers);
+      front.on("registration-changed", this.updateWorkers);
+    });
     client.mainRoot.on("workerListChanged", this.updateWorkers);
 
     client.mainRoot.on("serviceWorkerRegistrationListChanged", this.updateWorkers);
     client.mainRoot.on("processListChanged", this.updateWorkers);
-    client.addListener("registration-changed", this.updateWorkers);
 
     addMultiE10sListener(this.updateMultiE10S);
 
     this.updateMultiE10S();
     this.updateWorkers();
   }
 
   componentWillUnmount() {
     const client = this.props.client;
     client.mainRoot.off("processListChanged", this.updateWorkers);
     client.mainRoot.off("serviceWorkerRegistrationListChanged", this.updateWorkers);
     client.mainRoot.off("workerListChanged", this.updateWorkers);
     for (const front of this.state.contentProcessFronts) {
       front.off("workerListChanged", this.updateWorkers);
     }
-    client.removeListener("registration-changed", this.updateWorkers);
+    for (const front of this.state.serviceWorkerRegistrationFronts) {
+      front.off("push-subscription-modified", this.updateWorkers);
+      front.off("registration-changed", this.updateWorkers);
+    }
 
     removeMultiE10sListener(this.updateMultiE10S);
   }
 
   get initialState() {
     return {
       workers: {
         service: [],
         shared: [],
         other: [],
       },
       isMultiE10S: isMultiE10s(),
 
       // List of ContentProcessTargetFront registered from componentWillMount
       // from which we listen for worker list changes
       contentProcessFronts: [],
+      serviceWorkerRegistrationFronts: [],
     };
   }
 
   updateMultiE10S() {
     this.setState({ isMultiE10S: isMultiE10s() });
   }
 
   updateWorkers() {
--- a/devtools/client/aboutdebugging/components/workers/ServiceWorkerTarget.js
+++ b/devtools/client/aboutdebugging/components/workers/ServiceWorkerTarget.js
@@ -26,62 +26,50 @@ class ServiceWorkerTarget extends Compon
       debugDisabled: PropTypes.bool,
       target: PropTypes.shape({
         active: PropTypes.bool,
         fetch: PropTypes.bool.isRequired,
         icon: PropTypes.string,
         name: PropTypes.string.isRequired,
         url: PropTypes.string,
         scope: PropTypes.string.isRequired,
-        // registrationActor can be missing in e10s.
-        registrationActor: PropTypes.string,
+        // registrationFront can be missing in e10s.
+        registrationFront: PropTypes.object,
         workerTargetFront: PropTypes.object,
       }).isRequired,
     };
   }
 
   constructor(props) {
     super(props);
 
     this.state = {
       pushSubscription: null,
     };
 
     this.debug = this.debug.bind(this);
     this.push = this.push.bind(this);
     this.start = this.start.bind(this);
     this.unregister = this.unregister.bind(this);
-    this.onPushSubscriptionModified = this.onPushSubscriptionModified.bind(this);
     this.updatePushSubscription = this.updatePushSubscription.bind(this);
     this.isRunning = this.isRunning.bind(this);
     this.isActive = this.isActive.bind(this);
     this.getServiceWorkerStatus = this.getServiceWorkerStatus.bind(this);
     this.renderButtons = this.renderButtons.bind(this);
     this.renderUnregisterLink = this.renderUnregisterLink.bind(this);
   }
 
   componentDidMount() {
-    const { client } = this.props;
-    client.addListener("push-subscription-modified", this.onPushSubscriptionModified);
     this.updatePushSubscription();
   }
 
   componentDidUpdate(oldProps, oldState) {
-    const wasActive = oldProps.target.active;
-    if (!wasActive && this.isActive()) {
-      // While the service worker isn't active, any calls to `updatePushSubscription`
-      // won't succeed. If we just became active, make sure we didn't miss a push
-      // subscription change by updating it now.
-      this.updatePushSubscription();
-    }
-  }
-
-  componentWillUnmount() {
-    const { client } = this.props;
-    client.removeListener("push-subscription-modified", this.onPushSubscriptionModified);
+    // The parent component listens to "push-subscription-modified" events,
+    // so we should update the push subscription after each update.
+    this.updatePushSubscription();
   }
 
   debug() {
     if (!this.isRunning()) {
       // If the worker is not running, we can't debug it.
       return;
     }
 
@@ -102,51 +90,38 @@ class ServiceWorkerTarget extends Compon
   }
 
   start() {
     if (!this.isActive() || this.isRunning()) {
       // If the worker is not active or if it is already running, we can't start it.
       return;
     }
 
-    const { client, target } = this.props;
-    client.request({
-      to: target.registrationActor,
-      type: "start",
-    });
+    const { registrationFront } = this.props.target;
+    registrationFront.start();
   }
 
   unregister() {
-    const { client, target } = this.props;
-    client.request({
-      to: target.registrationActor,
-      type: "unregister",
-    });
+    const { registrationFront } = this.props.target;
+    registrationFront.unregister();
   }
 
-  onPushSubscriptionModified(type, data) {
-    const { target } = this.props;
-    if (data.from === target.registrationActor) {
-      this.updatePushSubscription();
-    }
-  }
-
-  updatePushSubscription() {
-    if (!this.props.target.registrationActor) {
-      // A valid registrationActor is needed to retrieve the push subscription.
+  async updatePushSubscription() {
+    const { registrationFront } = this.props.target;
+    if (!registrationFront) {
+      // A valid registrationFront is needed to retrieve the push subscription.
       return;
     }
 
-    const { client, target } = this.props;
-    client.request({
-      to: target.registrationActor,
-      type: "getPushSubscription",
-    }, ({ subscription }) => {
+    try {
+      const subscription = await registrationFront.getPushSubscription();
       this.setState({ pushSubscription: subscription });
-    });
+    } catch (e) {
+      // The registration might be destroyed before the request reaches the server.
+    }
   }
 
   isRunning() {
     // We know the target is running if it has a worker actor.
     return !!this.props.target.workerTargetFront;
   }
 
   isActive() {
@@ -191,17 +166,17 @@ class ServiceWorkerTarget extends Compon
       // Only debug button is available if the service worker is not active.
       return debugButton;
     }
     return startButton;
   }
 
   renderUnregisterLink() {
     if (!this.isActive()) {
-      // If not active, there might be no registrationActor available.
+      // If not active, there might be no registrationFront available.
       return null;
     }
 
     return dom.a({
       onClick: this.unregister,
       className: "unregister-link",
     }, Strings.GetStringFromName("unregister"));
   }
--- a/devtools/client/application/initializer.js
+++ b/devtools/client/application/initializer.js
@@ -32,26 +32,31 @@ window.Application = {
     this.updateDomain = this.updateDomain.bind(this);
 
     this.mount = document.querySelector("#mount");
     this.toolbox = toolbox;
     this.client = toolbox.target.client;
 
     this.store = configureStore();
     this.actions = bindActionCreators(actions, this.store.dispatch);
+    this.serviceWorkerRegistrationFronts = [];
 
     const serviceContainer = {
       selectTool(toolId) {
         return toolbox.selectTool(toolId);
       },
     };
     this.toolbox.target.activeTab.on("workerListChanged", this.updateWorkers);
     this.client.mainRoot.on("serviceWorkerRegistrationListChanged", this.updateWorkers);
-    this.client.addListener("registration-changed", this.updateWorkers);
     this.client.mainRoot.on("processListChanged", this.updateWorkers);
+    this.client.mainRoot.onFront("serviceWorkerRegistration", front => {
+      this.serviceWorkerRegistrationFronts.push(front);
+      front.on("push-subscription-modified", this.updateWorkers);
+      front.on("registration-changed", this.updateWorkers);
+    });
     this.toolbox.target.on("navigate", this.updateDomain);
 
     this.updateDomain();
     await this.updateWorkers();
 
     const fluentBundles = await this.createFluentBundles();
 
     // Render the root Application component.
@@ -78,27 +83,34 @@ window.Application = {
     return contexts;
   },
 
   async updateWorkers() {
     const { service } = await this.client.mainRoot.listAllWorkers();
     this.actions.updateWorkers(service);
   },
 
+  removeRegistrationFrontListeners() {
+    for (const front of this.serviceWorkerRegistrationFronts) {
+      front.off("push-subscription-modified", this.updateWorkers);
+      front.off("registration-changed", this.updateWorkers);
+    }
+    this.serviceWorkerRegistrationFronts = [];
+  },
+
   updateDomain() {
     this.actions.updateDomain(this.toolbox.target.url);
   },
 
   destroy() {
     this.toolbox.target.activeTab.off("workerListChanged", this.updateWorkers);
     this.client.mainRoot.off("serviceWorkerRegistrationListChanged",
       this.updateWorkers);
-    this.client.removeListener("registration-changed", this.updateWorkers);
     this.client.mainRoot.off("processListChanged", this.updateWorkers);
-
+    this.removeRegistrationFrontListeners();
     this.toolbox.target.off("navigate", this.updateDomain);
 
     unmountComponentAtNode(this.mount);
     this.mount = null;
     this.toolbox = null;
     this.client = null;
   },
 };
--- a/devtools/client/application/src/components/Worker.js
+++ b/devtools/client/application/src/components/Worker.js
@@ -28,18 +28,18 @@ class Worker extends Component {
   static get propTypes() {
     return {
       client: PropTypes.instanceOf(DebuggerClient).isRequired,
       debugDisabled: PropTypes.bool,
       worker: PropTypes.shape({
         active: PropTypes.bool,
         name: PropTypes.string.isRequired,
         scope: PropTypes.string.isRequired,
-        // registrationActor can be missing in e10s.
-        registrationActor: PropTypes.string,
+        // registrationFront can be missing in e10s.
+        registrationFront: PropTypes.object,
         workerTargetFront: PropTypes.object,
       }).isRequired,
     };
   }
 
   constructor(props) {
     super(props);
 
@@ -59,29 +59,23 @@ class Worker extends Component {
   }
 
   start() {
     if (!this.isActive() || this.isRunning()) {
       console.log("Running or inactive service workers cannot be started");
       return;
     }
 
-    const { client, worker } = this.props;
-    client.request({
-      to: worker.registrationActor,
-      type: "start",
-    });
+    const { registrationFront } = this.props.worker;
+    registrationFront.start();
   }
 
   unregister() {
-    const { client, worker } = this.props;
-    client.request({
-      to: worker.registrationActor,
-      type: "unregister",
-    });
+    const { registrationFront } = this.props.worker;
+    registrationFront.unregister();
   }
 
   isRunning() {
     // We know the worker is running if it has a worker actor.
     return !!this.props.worker.workerTargetFront;
   }
 
   isActive() {
--- a/devtools/client/application/test/head.js
+++ b/devtools/client/application/test/head.js
@@ -50,25 +50,22 @@ async function openNewTabAndApplicationP
   const target = await TargetFactory.forTab(tab);
 
   const toolbox = await gDevTools.showToolbox(target, "application");
   const panel = toolbox.getCurrentPanel();
   return { panel, tab, target, toolbox };
 }
 
 async function unregisterAllWorkers(client) {
-  info("Wait until all workers have a valid registrationActor");
+  info("Wait until all workers have a valid registrationFront");
   let workers;
   await asyncWaitUntil(async function() {
     workers = await client.mainRoot.listAllWorkers();
     const allWorkersRegistered =
-      workers.service.every(worker => !!worker.registrationActor);
+      workers.service.every(worker => !!worker.registrationFront);
     return allWorkersRegistered;
   });
 
   info("Unregister all service workers");
   for (const worker of workers.service) {
-    await client.request({
-      to: worker.registrationActor,
-      type: "unregister",
-    });
+    await worker.registrationFront.unregister();
   }
 }
--- a/devtools/client/definitions.js
+++ b/devtools/client/definitions.js
@@ -134,17 +134,17 @@ Tools.jsdebugger = {
   id: "jsdebugger",
   accesskey: l10n("debuggerMenu.accesskey"),
   ordinal: 3,
   icon: "chrome://devtools/skin/images/tool-debugger.svg",
   url: "chrome://devtools/content/debugger/new/index.html",
   label: l10n("ToolboxDebugger.label"),
   panelLabel: l10n("ToolboxDebugger.panelLabel"),
   get tooltip() {
-    return l10n("ToolboxDebugger.tooltip2");
+    return l10n("ToolboxDebugger.tooltip3");
   },
   inMenu: true,
   isTargetSupported: function() {
     return true;
   },
   build: function(iframeWindow, toolbox) {
     return new NewDebuggerPanel(iframeWindow, toolbox);
   },
--- a/devtools/client/locales/en-US/startup.properties
+++ b/devtools/client/locales/en-US/startup.properties
@@ -65,21 +65,20 @@ webConsoleCmd.accesskey=W
 # This string is displayed in the title of the tab when the debugger is
 # displayed inside the developer tools window and in the Developer Tools Menu.
 ToolboxDebugger.label=Debugger
 
 # LOCALIZATION NOTE (ToolboxDebugger.panelLabel):
 # This is used as the label for the toolbox panel.
 ToolboxDebugger.panelLabel=Debugger Panel
 
-# LOCALIZATION NOTE (ToolboxDebugger.tooltip2):
+# LOCALIZATION NOTE (ToolboxDebugger.tooltip3):
 # This string is displayed in the tooltip of the tab when the debugger is
-# displayed inside the developer tools window..
-# A keyboard shortcut for JS Debugger will be shown inside brackets.
-ToolboxDebugger.tooltip2=JavaScript Debugger (%S)
+# displayed inside the developer tools window.
+ToolboxDebugger.tooltip3=JavaScript Debugger
 
 # LOCALIZATION NOTE (debuggerMenu.accesskey)
 # Used for the menuitem in the tool menu
 debuggerMenu.accesskey=D
 
 # LOCALIZATION NOTE (ToolboxStyleEditor.label):
 # This string is displayed in the title of the tab when the style editor is
 # displayed inside the developer tools window and in the Developer Tools Menu.
--- a/devtools/client/webconsole/components/JSTerm.js
+++ b/devtools/client/webconsole/components/JSTerm.js
@@ -172,16 +172,28 @@ class JSTerm extends Component {
 
         const onArrowLeft = () => {
           if (this.autocompletePopup.isOpen || this.getAutoCompletionText()) {
             this.clearCompletion();
           }
           return "CodeMirror.Pass";
         };
 
+        const onArrowRight = () => {
+          // We only want to complete on Right arrow if the completion text is
+          // displayed.
+          if (this.getAutoCompletionText()) {
+            this.acceptProposedCompletion();
+            return null;
+          }
+
+          this.clearCompletion();
+          return "CodeMirror.Pass";
+        };
+
         this.editor = new Editor({
           autofocus: true,
           enableCodeFolding: false,
           autoCloseBrackets: false,
           gutters: [],
           lineWrapping: true,
           mode: Editor.modes.js,
           styleActiveLine: false,
@@ -234,27 +246,19 @@ class JSTerm extends Component {
 
             "Down": onArrowDown,
             "Cmd-Down": onArrowDown,
 
             "Left": onArrowLeft,
             "Ctrl-Left": onArrowLeft,
             "Cmd-Left": onArrowLeft,
 
-            "Right": () => {
-              // We only want to complete on Right arrow if the completion text is
-              // displayed.
-              if (this.getAutoCompletionText()) {
-                this.acceptProposedCompletion();
-                return null;
-              }
-
-              this.clearCompletion();
-              return "CodeMirror.Pass";
-            },
+            "Right": onArrowRight,
+            "Ctrl-Right": onArrowRight,
+            "Cmd-Right": onArrowRight,
 
             "Ctrl-N": () => {
               // Control-N differs from down arrow: it ignores autocomplete state.
               // Note that we preserve the default 'down' navigation within
               // multiline text.
               if (
                 Services.appinfo.OS === "Darwin"
                 && this.canCaretGoNext()
@@ -840,16 +844,25 @@ class JSTerm extends Component {
       }
 
       if (event.keyCode === KeyCodes.DOM_VK_LEFT &&
         (this.autocompletePopup.isOpen || this.getAutoCompletionText())
       ) {
         this.clearCompletion();
       }
 
+      // We only want to complete on Right arrow if the completion text is displayed.
+      if (event.keyCode === KeyCodes.DOM_VK_RIGHT) {
+        if (this.getAutoCompletionText()) {
+          this.acceptProposedCompletion();
+        }
+        this.clearCompletion();
+        event.preventDefault();
+      }
+
       return;
     } else if (event.keyCode == KeyCodes.DOM_VK_RETURN) {
       if (!this.autocompletePopup.isOpen && (
         event.shiftKey || !Debugger.isCompilableUnit(this.getInputValue())
       )) {
         // shift return or incomplete statement
         return;
       }
--- a/devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_arrow_keys.js
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_arrow_keys.js
@@ -63,19 +63,35 @@ async function performTests() {
   info("Test that arrow right selects selected autocomplete item");
   onPopUpClose = popup.once("popup-closed");
   EventUtils.synthesizeKey("KEY_ArrowRight");
   await onPopUpClose;
   checkInput("window.foo.aa|");
   is(popup.isOpen, false, "popup is closed");
   checkJsTermCompletionValue(jsterm, "", "completeNode is empty");
 
+  info("Test that Ctrl/Cmd + Left removes complete node");
   await setInputValueForAutocompletion(jsterm, "window.foo.a");
   const prefix = jsterm.getInputValue().replace(/[\S]/g, " ");
   checkJsTermCompletionValue(jsterm, prefix + "a", "completeNode has expected value");
 
   const isOSX = Services.appinfo.OS == "Darwin";
   EventUtils.synthesizeKey("KEY_ArrowLeft", {
     [isOSX ? "metaKey" : "ctrlKey"]: true,
   });
   checkJsTermCompletionValue(jsterm, "",
     "completeNode was cleared after Ctrl/Cmd + left");
+
+  info("Test that Ctrl/Cmd + Right closes the popup if there's text after cursor");
+  jsterm.setInputValue(".");
+  EventUtils.synthesizeKey("KEY_ArrowLeft");
+  onPopUpOpen = popup.once("popup-opened");
+  EventUtils.sendString("win");
+  await onPopUpOpen;
+  ok(popup.isOpen, "popup is open");
+
+  onPopUpClose = popup.once("popup-closed");
+  EventUtils.synthesizeKey("KEY_ArrowRight", {
+    [isOSX ? "metaKey" : "ctrlKey"]: true,
+  });
+  await onPopUpClose;
+  is(jsterm.getInputValue(), "win.", "input value wasn't modified");
 }
--- a/devtools/server/actors/targets/browsing-context.js
+++ b/devtools/server/actors/targets/browsing-context.js
@@ -39,17 +39,17 @@ const STRINGS_URI = "devtools/shared/loc
 const L10N = new LocalizationHelper(STRINGS_URI);
 
 const { ActorClassWithSpec, Actor, Pool } = require("devtools/shared/protocol");
 const { LazyPool, createExtraActors } = require("devtools/shared/protocol/lazy-pool");
 const { browsingContextTargetSpec } = require("devtools/shared/specs/targets/browsing-context");
 
 loader.lazyRequireGetter(this, "ThreadActor", "devtools/server/actors/thread", true);
 loader.lazyRequireGetter(this, "unwrapDebuggerObjectGlobal", "devtools/server/actors/thread", true);
-loader.lazyRequireGetter(this, "WorkerTargetActorList", "devtools/server/actors/worker/worker-list", true);
+loader.lazyRequireGetter(this, "WorkerTargetActorList", "devtools/server/actors/worker/worker-target-actor-list", true);
 loader.lazyImporter(this, "ExtensionContent", EXTENSION_CONTENT_JSM);
 
 loader.lazyRequireGetter(this, "StyleSheetActor", "devtools/server/actors/stylesheets", true);
 loader.lazyRequireGetter(this, "getSheetText", "devtools/server/actors/stylesheets", true);
 
 function getWindowID(window) {
   return window.windowUtils.currentInnerWindowID;
 }
--- a/devtools/server/actors/targets/content-process.js
+++ b/devtools/server/actors/targets/content-process.js
@@ -17,17 +17,17 @@ const Services = require("Services");
 const { ChromeDebuggerActor } = require("devtools/server/actors/thread");
 const { WebConsoleActor } = require("devtools/server/actors/webconsole");
 const makeDebugger = require("devtools/server/actors/utils/make-debugger");
 const { ActorPool } = require("devtools/server/actors/common");
 const { Pool } = require("devtools/shared/protocol");
 const { assert } = require("devtools/shared/DevToolsUtils");
 const { TabSources } = require("devtools/server/actors/utils/TabSources");
 
-loader.lazyRequireGetter(this, "WorkerTargetActorList", "devtools/server/actors/worker/worker-list", true);
+loader.lazyRequireGetter(this, "WorkerTargetActorList", "devtools/server/actors/worker/worker-target-actor-list", true);
 loader.lazyRequireGetter(this, "MemoryActor", "devtools/server/actors/memory", true);
 loader.lazyRequireGetter(this, "PromisesActor", "devtools/server/actors/promises", true);
 
 function ContentProcessTargetActor(connection) {
   this.conn = connection;
   this._contextPool = new ActorPool(this.conn);
   this.conn.addActorPool(this._contextPool);
   this.threadActor = null;
--- a/devtools/server/actors/thread.js
+++ b/devtools/server/actors/thread.js
@@ -1938,17 +1938,21 @@ const ThreadActor = ActorClassWithSpec(t
       if (!this.dbg.replaying) {
         return false;
       }
       sourceActor = this.sources.getSourceActor(source);
     } else {
       sourceActor = this.sources.createSourceActor(source);
     }
 
-    const bpActors = [...this.breakpointActorMap.findActors()];
+    const bpActors = [...this.breakpointActorMap.findActors()]
+    .filter((actor) => {
+      const bpSource = actor.generatedLocation.generatedSourceActor;
+      return bpSource.source ? bpSource.source === source : bpSource.url === source.url;
+    });
 
     // Bug 1225160: If addSource is called in response to a new script
     // notification, and this notification was triggered by loading a JSM from
     // chrome code, calling unsafeSynchronize could cause a debuggee timer to
     // fire. If this causes the JSM to be loaded a second time, the browser
     // will crash, because loading JSMS is not reentrant, and the first load
     // has not completed yet.
     //
--- a/devtools/server/actors/webbrowser.js
+++ b/devtools/server/actors/webbrowser.js
@@ -10,18 +10,19 @@ var { Ci } = require("chrome");
 var Services = require("Services");
 var { DebuggerServer } = require("devtools/server/main");
 var { ActorRegistry } = require("devtools/server/actors/utils/actor-registry");
 var DevToolsUtils = require("devtools/shared/DevToolsUtils");
 
 loader.lazyRequireGetter(this, "RootActor", "devtools/server/actors/root", true);
 loader.lazyRequireGetter(this, "FrameTargetActorProxy", "devtools/server/actors/targets/frame-proxy", true);
 loader.lazyRequireGetter(this, "WebExtensionActor", "devtools/server/actors/addon/webextension", true);
-loader.lazyRequireGetter(this, "WorkerTargetActorList", "devtools/server/actors/worker/worker-list", true);
-loader.lazyRequireGetter(this, "ServiceWorkerRegistrationActorList", "devtools/server/actors/worker/worker-list", true);
+loader.lazyRequireGetter(this, "WorkerTargetActorList", "devtools/server/actors/worker/worker-target-actor-list", true);
+loader.lazyRequireGetter(this, "ServiceWorkerRegistrationActorList",
+  "devtools/server/actors/worker/service-worker-registration-list", true);
 loader.lazyRequireGetter(this, "ProcessActorList", "devtools/server/actors/process", true);
 loader.lazyImporter(this, "AddonManager", "resource://gre/modules/AddonManager.jsm");
 loader.lazyImporter(this, "AppConstants", "resource://gre/modules/AppConstants.jsm");
 
 /**
  * Browser-specific actors.
  */
 
--- a/devtools/server/actors/worker/moz.build
+++ b/devtools/server/actors/worker/moz.build
@@ -1,11 +1,14 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
 DevToolsModules(
+    'push-subscription.js',
     'service-worker-process.js',
+    'service-worker-registration-list.js',
+    'service-worker-registration.js',
     'service-worker.js',
-    'worker-list.js',
+    'worker-target-actor-list.js',
 )
copy from devtools/server/actors/worker/service-worker.js
copy to devtools/server/actors/worker/push-subscription.js
--- a/devtools/server/actors/worker/service-worker.js
+++ b/devtools/server/actors/worker/push-subscription.js
@@ -1,38 +1,17 @@
 /* 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 { Ci } = require("chrome");
-const ChromeUtils = require("ChromeUtils");
-const Services = require("Services");
-const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
 const protocol = require("devtools/shared/protocol");
-const {
-  pushSubscriptionSpec,
-  serviceWorkerRegistrationSpec,
-  serviceWorkerSpec,
-} = require("devtools/shared/specs/worker/service-worker");
-
-loader.lazyRequireGetter(this, "ChromeUtils");
-
-XPCOMUtils.defineLazyServiceGetter(
-  this, "swm",
-  "@mozilla.org/serviceworkers/manager;1",
-  "nsIServiceWorkerManager"
-);
-
-XPCOMUtils.defineLazyServiceGetter(
-  this, "PushService",
-  "@mozilla.org/push/Service;1",
-  "nsIPushService"
-);
+const { pushSubscriptionSpec } =
+  require("devtools/shared/specs/worker/push-subscription");
 
 const PushSubscriptionActor = protocol.ActorClassWithSpec(pushSubscriptionSpec, {
   initialize(conn, subscription) {
     protocol.Actor.prototype.initialize.call(this, conn);
     this._subscription = subscription;
   },
 
   form(detail) {
@@ -49,205 +28,9 @@ const PushSubscriptionActor = protocol.A
     };
   },
 
   destroy() {
     this._subscription = null;
     protocol.Actor.prototype.destroy.call(this);
   },
 });
-
-const ServiceWorkerActor = protocol.ActorClassWithSpec(serviceWorkerSpec, {
-  initialize(conn, worker) {
-    protocol.Actor.prototype.initialize.call(this, conn);
-    this._worker = worker;
-  },
-
-  form() {
-    if (!this._worker) {
-      return null;
-    }
-
-    return {
-      url: this._worker.scriptSpec,
-      state: this._worker.state,
-      fetch: this._worker.handlesFetchEvents,
-    };
-  },
-
-  destroy() {
-    protocol.Actor.prototype.destroy.call(this);
-    this._worker = null;
-  },
-});
-
-// Lazily load the service-worker-process.js process script only once.
-let _serviceWorkerProcessScriptLoaded = false;
-
-const ServiceWorkerRegistrationActor =
-protocol.ActorClassWithSpec(serviceWorkerRegistrationSpec, {
-  /**
-   * Create the ServiceWorkerRegistrationActor
-   * @param DebuggerServerConnection conn
-   *   The server connection.
-   * @param ServiceWorkerRegistrationInfo registration
-   *   The registration's information.
-   */
-  initialize(conn, registration) {
-    protocol.Actor.prototype.initialize.call(this, conn);
-    this._conn = conn;
-    this._registration = registration;
-    this._pushSubscriptionActor = null;
-    this._registration.addListener(this);
-
-    const {installingWorker, waitingWorker, activeWorker} = registration;
-    this._installingWorker = new ServiceWorkerActor(conn, installingWorker);
-    this._waitingWorker = new ServiceWorkerActor(conn, waitingWorker);
-    this._activeWorker = new ServiceWorkerActor(conn, activeWorker);
-
-    Services.obs.addObserver(this, PushService.subscriptionModifiedTopic);
-  },
-
-  onChange() {
-    this._installingWorker.destroy();
-    this._waitingWorker.destroy();
-    this._activeWorker.destroy();
-
-    const {installingWorker, waitingWorker, activeWorker} = this._registration;
-    this._installingWorker = new ServiceWorkerActor(this._conn, installingWorker);
-    this._waitingWorker = new ServiceWorkerActor(this._conn, waitingWorker);
-    this._activeWorker = new ServiceWorkerActor(this._conn, activeWorker);
-
-    this.emit("registration-changed");
-  },
-
-  form(detail) {
-    if (detail === "actorid") {
-      return this.actorID;
-    }
-    const registration = this._registration;
-    const installingWorker = this._installingWorker.form();
-    const waitingWorker = this._waitingWorker.form();
-    const activeWorker = this._activeWorker.form();
-
-    const newestWorker = (activeWorker || waitingWorker || installingWorker);
-
-    const isE10s = Services.appinfo.browserTabsRemoteAutostart;
-    return {
-      actor: this.actorID,
-      scope: registration.scope,
-      url: registration.scriptSpec,
-      installingWorker,
-      waitingWorker,
-      activeWorker,
-      fetch: newestWorker && newestWorker.fetch,
-      // - In e10s: only active registrations are available.
-      // - In non-e10s: registrations always have at least one worker, if the worker is
-      // active, the registration is active.
-      active: isE10s ? true : !!activeWorker,
-      lastUpdateTime: registration.lastUpdateTime,
-    };
-  },
-
-  destroy() {
-    protocol.Actor.prototype.destroy.call(this);
-    Services.obs.removeObserver(this, PushService.subscriptionModifiedTopic);
-    this._registration.removeListener(this);
-    this._registration = null;
-    if (this._pushSubscriptionActor) {
-      this._pushSubscriptionActor.destroy();
-    }
-    this._pushSubscriptionActor = null;
-
-    this._installingWorker.destroy();
-    this._waitingWorker.destroy();
-    this._activeWorker.destroy();
-
-    this._installingWorker = null;
-    this._waitingWorker = null;
-    this._activeWorker = null;
-  },
-
-  /**
-   * Standard observer interface to listen to push messages and changes.
-   */
-  observe(subject, topic, data) {
-    const scope = this._registration.scope;
-    if (data !== scope) {
-      // This event doesn't concern us, pretend nothing happened.
-      return;
-    }
-    switch (topic) {
-      case PushService.subscriptionModifiedTopic:
-        if (this._pushSubscriptionActor) {
-          this._pushSubscriptionActor.destroy();
-          this._pushSubscriptionActor = null;
-        }
-        this.emit("push-subscription-modified");
-        break;
-    }
-  },
-
-  start() {
-    if (!_serviceWorkerProcessScriptLoaded) {
-      Services.ppmm.loadProcessScript(
-        "resource://devtools/server/actors/worker/service-worker-process.js", true);
-      _serviceWorkerProcessScriptLoaded = true;
-    }
-
-    // XXX: Send the permissions down to the content process before starting
-    // the service worker within the content process. As we don't know what
-    // content process we're starting the service worker in (as we're using a
-    // broadcast channel to talk to it), we just broadcast the permissions to
-    // everyone as well.
-    //
-    // This call should be replaced with a proper implementation when
-    // ServiceWorker debugging is improved to support multiple content processes
-    // correctly.
-    Services.perms.broadcastPermissionsForPrincipalToAllContentProcesses(
-      this._registration.principal);
-
-    Services.ppmm.broadcastAsyncMessage("serviceWorkerRegistration:start", {
-      scope: this._registration.scope,
-    });
-    return { type: "started" };
-  },
-
-  unregister() {
-    const { principal, scope } = this._registration;
-    const unregisterCallback = {
-      unregisterSucceeded: function() {},
-      unregisterFailed: function() {
-        console.error("Failed to unregister the service worker for " + scope);
-      },
-      QueryInterface: ChromeUtils.generateQI(
-        [Ci.nsIServiceWorkerUnregisterCallback]),
-    };
-    swm.propagateUnregister(principal, unregisterCallback, scope);
-
-    return { type: "unregistered" };
-  },
-
-  getPushSubscription() {
-    const registration = this._registration;
-    let pushSubscriptionActor = this._pushSubscriptionActor;
-    if (pushSubscriptionActor) {
-      return Promise.resolve(pushSubscriptionActor);
-    }
-    return new Promise((resolve, reject) => {
-      PushService.getSubscription(
-        registration.scope,
-        registration.principal,
-        (result, subscription) => {
-          if (!subscription) {
-            resolve(null);
-            return;
-          }
-          pushSubscriptionActor = new PushSubscriptionActor(this._conn, subscription);
-          this._pushSubscriptionActor = pushSubscriptionActor;
-          resolve(pushSubscriptionActor);
-        }
-      );
-    });
-  },
-});
-
-exports.ServiceWorkerRegistrationActor = ServiceWorkerRegistrationActor;
+exports.PushSubscriptionActor = PushSubscriptionActor;
rename from devtools/server/actors/worker/worker-list.js
rename to devtools/server/actors/worker/service-worker-registration-list.js
--- a/devtools/server/actors/worker/worker-list.js
+++ b/devtools/server/actors/worker/service-worker-registration-list.js
@@ -1,145 +1,25 @@
 /* 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 { Ci } = require("chrome");
 const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
-loader.lazyRequireGetter(this, "WorkerTargetActor", "devtools/server/actors/targets/worker", true);
-loader.lazyRequireGetter(this, "ServiceWorkerRegistrationActor", "devtools/server/actors/worker/service-worker", true);
-
-XPCOMUtils.defineLazyServiceGetter(
-  this, "wdm",
-  "@mozilla.org/dom/workers/workerdebuggermanager;1",
-  "nsIWorkerDebuggerManager"
-);
+loader.lazyRequireGetter(this, "ServiceWorkerRegistrationActor",
+  "devtools/server/actors/worker/service-worker-registration", true);
 
 XPCOMUtils.defineLazyServiceGetter(
   this, "swm",
   "@mozilla.org/serviceworkers/manager;1",
   "nsIServiceWorkerManager"
 );
 
-function matchWorkerDebugger(dbg, options) {
-  if ("type" in options && dbg.type !== options.type) {
-    return false;
-  }
-  if ("window" in options) {
-    let window = dbg.window;
-    while (window !== null && window.parent !== window) {
-      window = window.parent;
-    }
-
-    if (window !== options.window) {
-      return false;
-    }
-  }
-
-  return true;
-}
-
-function WorkerTargetActorList(conn, options) {
-  this._conn = conn;
-  this._options = options;
-  this._actors = new Map();
-  this._onListChanged = null;
-  this._mustNotify = false;
-  this.onRegister = this.onRegister.bind(this);
-  this.onUnregister = this.onUnregister.bind(this);
-}
-
-WorkerTargetActorList.prototype = {
-  getList() {
-    // Create a set of debuggers.
-    const dbgs = new Set();
-    for (const dbg of wdm.getWorkerDebuggerEnumerator()) {
-      if (matchWorkerDebugger(dbg, this._options)) {
-        dbgs.add(dbg);
-      }
-    }
-
-    // Delete each actor for which we don't have a debugger.
-    for (const [dbg ] of this._actors) {
-      if (!dbgs.has(dbg)) {
-        this._actors.delete(dbg);
-      }
-    }
-
-    // Create an actor for each debugger for which we don't have one.
-    for (const dbg of dbgs) {
-      if (!this._actors.has(dbg)) {
-        this._actors.set(dbg, new WorkerTargetActor(this._conn, dbg));
-      }
-    }
-
-    const actors = [];
-    for (const [, actor] of this._actors) {
-      actors.push(actor);
-    }
-
-    if (!this._mustNotify) {
-      if (this._onListChanged !== null) {
-        wdm.addListener(this);
-      }
-      this._mustNotify = true;
-    }
-
-    return Promise.resolve(actors);
-  },
-
-  get onListChanged() {
-    return this._onListChanged;
-  },
-
-  set onListChanged(onListChanged) {
-    if (typeof onListChanged !== "function" && onListChanged !== null) {
-      throw new Error("onListChanged must be either a function or null.");
-    }
-    if (onListChanged === this._onListChanged) {
-      return;
-    }
-
-    if (this._mustNotify) {
-      if (this._onListChanged === null && onListChanged !== null) {
-        wdm.addListener(this);
-      }
-      if (this._onListChanged !== null && onListChanged === null) {
-        wdm.removeListener(this);
-      }
-    }
-    this._onListChanged = onListChanged;
-  },
-
-  _notifyListChanged() {
-    this._onListChanged();
-
-    if (this._onListChanged !== null) {
-      wdm.removeListener(this);
-    }
-    this._mustNotify = false;
-  },
-
-  onRegister(dbg) {
-    if (matchWorkerDebugger(dbg, this._options)) {
-      this._notifyListChanged();
-    }
-  },
-
-  onUnregister(dbg) {
-    if (matchWorkerDebugger(dbg, this._options)) {
-      this._notifyListChanged();
-    }
-  },
-};
-
-exports.WorkerTargetActorList = WorkerTargetActorList;
-
 function ServiceWorkerRegistrationActorList(conn) {
   this._conn = conn;
   this._actors = new Map();
   this._onListChanged = null;
   this._mustNotify = false;
   this.onRegister = this.onRegister.bind(this);
   this.onUnregister = this.onUnregister.bind(this);
 }
copy from devtools/server/actors/worker/service-worker.js
copy to devtools/server/actors/worker/service-worker-registration.js
--- a/devtools/server/actors/worker/service-worker.js
+++ b/devtools/server/actors/worker/service-worker-registration.js
@@ -4,86 +4,34 @@
 
 "use strict";
 
 const { Ci } = require("chrome");
 const ChromeUtils = require("ChromeUtils");
 const Services = require("Services");
 const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
 const protocol = require("devtools/shared/protocol");
-const {
-  pushSubscriptionSpec,
-  serviceWorkerRegistrationSpec,
-  serviceWorkerSpec,
-} = require("devtools/shared/specs/worker/service-worker");
-
-loader.lazyRequireGetter(this, "ChromeUtils");
+const { serviceWorkerRegistrationSpec } =
+  require("devtools/shared/specs/worker/service-worker-registration");
+const { PushSubscriptionActor } =
+  require("devtools/server/actors/worker/push-subscription");
+const { ServiceWorkerActor } = require("devtools/server/actors/worker/service-worker");
 
 XPCOMUtils.defineLazyServiceGetter(
   this, "swm",
   "@mozilla.org/serviceworkers/manager;1",
   "nsIServiceWorkerManager"
 );
 
 XPCOMUtils.defineLazyServiceGetter(
   this, "PushService",
   "@mozilla.org/push/Service;1",
   "nsIPushService"
 );
 
-const PushSubscriptionActor = protocol.ActorClassWithSpec(pushSubscriptionSpec, {
-  initialize(conn, subscription) {
-    protocol.Actor.prototype.initialize.call(this, conn);
-    this._subscription = subscription;
-  },
-
-  form(detail) {
-    if (detail === "actorid") {
-      return this.actorID;
-    }
-    const subscription = this._subscription;
-    return {
-      actor: this.actorID,
-      endpoint: subscription.endpoint,
-      pushCount: subscription.pushCount,
-      lastPush: subscription.lastPush,
-      quota: subscription.quota,
-    };
-  },
-
-  destroy() {
-    this._subscription = null;
-    protocol.Actor.prototype.destroy.call(this);
-  },
-});
-
-const ServiceWorkerActor = protocol.ActorClassWithSpec(serviceWorkerSpec, {
-  initialize(conn, worker) {
-    protocol.Actor.prototype.initialize.call(this, conn);
-    this._worker = worker;
-  },
-
-  form() {
-    if (!this._worker) {
-      return null;
-    }
-
-    return {
-      url: this._worker.scriptSpec,
-      state: this._worker.state,
-      fetch: this._worker.handlesFetchEvents,
-    };
-  },
-
-  destroy() {
-    protocol.Actor.prototype.destroy.call(this);
-    this._worker = null;
-  },
-});
-
 // Lazily load the service-worker-process.js process script only once.
 let _serviceWorkerProcessScriptLoaded = false;
 
 const ServiceWorkerRegistrationActor =
 protocol.ActorClassWithSpec(serviceWorkerRegistrationSpec, {
   /**
    * Create the ServiceWorkerRegistrationActor
    * @param DebuggerServerConnection conn
@@ -93,34 +41,24 @@ protocol.ActorClassWithSpec(serviceWorke
    */
   initialize(conn, registration) {
     protocol.Actor.prototype.initialize.call(this, conn);
     this._conn = conn;
     this._registration = registration;
     this._pushSubscriptionActor = null;
     this._registration.addListener(this);
 
-    const {installingWorker, waitingWorker, activeWorker} = registration;
-    this._installingWorker = new ServiceWorkerActor(conn, installingWorker);
-    this._waitingWorker = new ServiceWorkerActor(conn, waitingWorker);
-    this._activeWorker = new ServiceWorkerActor(conn, activeWorker);
+    this._createServiceWorkerActors();
 
     Services.obs.addObserver(this, PushService.subscriptionModifiedTopic);
   },
 
   onChange() {
-    this._installingWorker.destroy();
-    this._waitingWorker.destroy();
-    this._activeWorker.destroy();
-
-    const {installingWorker, waitingWorker, activeWorker} = this._registration;
-    this._installingWorker = new ServiceWorkerActor(this._conn, installingWorker);
-    this._waitingWorker = new ServiceWorkerActor(this._conn, waitingWorker);
-    this._activeWorker = new ServiceWorkerActor(this._conn, activeWorker);
-
+    this._destroyServiceWorkerActors();
+    this._createServiceWorkerActors();
     this.emit("registration-changed");
   },
 
   form(detail) {
     if (detail === "actorid") {
       return this.actorID;
     }
     const registration = this._registration;
@@ -152,19 +90,17 @@ protocol.ActorClassWithSpec(serviceWorke
     Services.obs.removeObserver(this, PushService.subscriptionModifiedTopic);
     this._registration.removeListener(this);
     this._registration = null;
     if (this._pushSubscriptionActor) {
       this._pushSubscriptionActor.destroy();
     }
     this._pushSubscriptionActor = null;
 
-    this._installingWorker.destroy();
-    this._waitingWorker.destroy();
-    this._activeWorker.destroy();
+    this._destroyServiceWorkerActors();
 
     this._installingWorker = null;
     this._waitingWorker = null;
     this._activeWorker = null;
   },
 
   /**
    * Standard observer interface to listen to push messages and changes.
@@ -243,11 +179,31 @@ protocol.ActorClassWithSpec(serviceWorke
           }
           pushSubscriptionActor = new PushSubscriptionActor(this._conn, subscription);
           this._pushSubscriptionActor = pushSubscriptionActor;
           resolve(pushSubscriptionActor);
         }
       );
     });
   },
+
+  _destroyServiceWorkerActors() {
+    this._installingWorker.destroy();
+    this._waitingWorker.destroy();
+    this._activeWorker.destroy();
+  },
+
+  _createServiceWorkerActors() {
+    const {installingWorker, waitingWorker, activeWorker} = this._registration;
+
+    this._installingWorker = new ServiceWorkerActor(this._conn, installingWorker);
+    this._waitingWorker = new ServiceWorkerActor(this._conn, waitingWorker);
+    this._activeWorker = new ServiceWorkerActor(this._conn, activeWorker);
+
+    // Add the ServiceWorker actors as children of this ServiceWorkerRegistration actor,
+    // assigning them valid actorIDs.
+    this.manage(this._installingWorker);
+    this.manage(this._waitingWorker);
+    this.manage(this._activeWorker);
+  },
 });
 
 exports.ServiceWorkerRegistrationActor = ServiceWorkerRegistrationActor;
--- a/devtools/server/actors/worker/service-worker.js
+++ b/devtools/server/actors/worker/service-worker.js
@@ -1,253 +1,35 @@
 /* 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 { Ci } = require("chrome");
-const ChromeUtils = require("ChromeUtils");
-const Services = require("Services");
-const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
 const protocol = require("devtools/shared/protocol");
-const {
-  pushSubscriptionSpec,
-  serviceWorkerRegistrationSpec,
-  serviceWorkerSpec,
-} = require("devtools/shared/specs/worker/service-worker");
-
-loader.lazyRequireGetter(this, "ChromeUtils");
-
-XPCOMUtils.defineLazyServiceGetter(
-  this, "swm",
-  "@mozilla.org/serviceworkers/manager;1",
-  "nsIServiceWorkerManager"
-);
-
-XPCOMUtils.defineLazyServiceGetter(
-  this, "PushService",
-  "@mozilla.org/push/Service;1",
-  "nsIPushService"
-);
-
-const PushSubscriptionActor = protocol.ActorClassWithSpec(pushSubscriptionSpec, {
-  initialize(conn, subscription) {
-    protocol.Actor.prototype.initialize.call(this, conn);
-    this._subscription = subscription;
-  },
-
-  form(detail) {
-    if (detail === "actorid") {
-      return this.actorID;
-    }
-    const subscription = this._subscription;
-    return {
-      actor: this.actorID,
-      endpoint: subscription.endpoint,
-      pushCount: subscription.pushCount,
-      lastPush: subscription.lastPush,
-      quota: subscription.quota,
-    };
-  },
-
-  destroy() {
-    this._subscription = null;
-    protocol.Actor.prototype.destroy.call(this);
-  },
-});
+const { serviceWorkerSpec } = require("devtools/shared/specs/worker/service-worker");
 
 const ServiceWorkerActor = protocol.ActorClassWithSpec(serviceWorkerSpec, {
   initialize(conn, worker) {
     protocol.Actor.prototype.initialize.call(this, conn);
     this._worker = worker;
   },
 
   form() {
     if (!this._worker) {
       return null;
     }
 
     return {
+      actor: this.actorID,
       url: this._worker.scriptSpec,
       state: this._worker.state,
       fetch: this._worker.handlesFetchEvents,
     };
   },
 
   destroy() {
     protocol.Actor.prototype.destroy.call(this);
     this._worker = null;
   },
 });
 
-// Lazily load the service-worker-process.js process script only once.
-let _serviceWorkerProcessScriptLoaded = false;
-
-const ServiceWorkerRegistrationActor =
-protocol.ActorClassWithSpec(serviceWorkerRegistrationSpec, {
-  /**
-   * Create the ServiceWorkerRegistrationActor
-   * @param DebuggerServerConnection conn
-   *   The server connection.
-   * @param ServiceWorkerRegistrationInfo registration
-   *   The registration's information.
-   */
-  initialize(conn, registration) {
-    protocol.Actor.prototype.initialize.call(this, conn);
-    this._conn = conn;
-    this._registration = registration;
-    this._pushSubscriptionActor = null;
-    this._registration.addListener(this);
-
-    const {installingWorker, waitingWorker, activeWorker} = registration;
-    this._installingWorker = new ServiceWorkerActor(conn, installingWorker);
-    this._waitingWorker = new ServiceWorkerActor(conn, waitingWorker);
-    this._activeWorker = new ServiceWorkerActor(conn, activeWorker);
-
-    Services.obs.addObserver(this, PushService.subscriptionModifiedTopic);
-  },
-
-  onChange() {
-    this._installingWorker.destroy();
-    this._waitingWorker.destroy();
-    this._activeWorker.destroy();
-
-    const {installingWorker, waitingWorker, activeWorker} = this._registration;
-    this._installingWorker = new ServiceWorkerActor(this._conn, installingWorker);
-    this._waitingWorker = new ServiceWorkerActor(this._conn, waitingWorker);
-    this._activeWorker = new ServiceWorkerActor(this._conn, activeWorker);
-
-    this.emit("registration-changed");
-  },
-
-  form(detail) {
-    if (detail === "actorid") {
-      return this.actorID;
-    }
-    const registration = this._registration;
-    const installingWorker = this._installingWorker.form();
-    const waitingWorker = this._waitingWorker.form();
-    const activeWorker = this._activeWorker.form();
-
-    const newestWorker = (activeWorker || waitingWorker || installingWorker);
-
-    const isE10s = Services.appinfo.browserTabsRemoteAutostart;
-    return {
-      actor: this.actorID,
-      scope: registration.scope,
-      url: registration.scriptSpec,
-      installingWorker,
-      waitingWorker,
-      activeWorker,
-      fetch: newestWorker && newestWorker.fetch,
-      // - In e10s: only active registrations are available.
-      // - In non-e10s: registrations always have at least one worker, if the worker is
-      // active, the registration is active.
-      active: isE10s ? true : !!activeWorker,
-      lastUpdateTime: registration.lastUpdateTime,
-    };
-  },
-
-  destroy() {
-    protocol.Actor.prototype.destroy.call(this);
-    Services.obs.removeObserver(this, PushService.subscriptionModifiedTopic);
-    this._registration.removeListener(this);
-    this._registration = null;
-    if (this._pushSubscriptionActor) {
-      this._pushSubscriptionActor.destroy();
-    }
-    this._pushSubscriptionActor = null;
-
-    this._installingWorker.destroy();
-    this._waitingWorker.destroy();
-    this._activeWorker.destroy();
-
-    this._installingWorker = null;
-    this._waitingWorker = null;
-    this._activeWorker = null;
-  },
-
-  /**
-   * Standard observer interface to listen to push messages and changes.
-   */
-  observe(subject, topic, data) {
-    const scope = this._registration.scope;
-    if (data !== scope) {
-      // This event doesn't concern us, pretend nothing happened.
-      return;
-    }
-    switch (topic) {
-      case PushService.subscriptionModifiedTopic:
-        if (this._pushSubscriptionActor) {
-          this._pushSubscriptionActor.destroy();
-          this._pushSubscriptionActor = null;
-        }
-        this.emit("push-subscription-modified");
-        break;
-    }
-  },
-
-  start() {
-    if (!_serviceWorkerProcessScriptLoaded) {
-      Services.ppmm.loadProcessScript(
-        "resource://devtools/server/actors/worker/service-worker-process.js", true);
-      _serviceWorkerProcessScriptLoaded = true;
-    }
-
-    // XXX: Send the permissions down to the content process before starting
-    // the service worker within the content process. As we don't know what
-    // content process we're starting the service worker in (as we're using a
-    // broadcast channel to talk to it), we just broadcast the permissions to
-    // everyone as well.
-    //
-    // This call should be replaced with a proper implementation when
-    // ServiceWorker debugging is improved to support multiple content processes
-    // correctly.
-    Services.perms.broadcastPermissionsForPrincipalToAllContentProcesses(
-      this._registration.principal);
-
-    Services.ppmm.broadcastAsyncMessage("serviceWorkerRegistration:start", {
-      scope: this._registration.scope,
-    });
-    return { type: "started" };
-  },
-
-  unregister() {
-    const { principal, scope } = this._registration;
-    const unregisterCallback = {
-      unregisterSucceeded: function() {},
-      unregisterFailed: function() {
-        console.error("Failed to unregister the service worker for " + scope);
-      },
-      QueryInterface: ChromeUtils.generateQI(
-        [Ci.nsIServiceWorkerUnregisterCallback]),
-    };
-    swm.propagateUnregister(principal, unregisterCallback, scope);
-
-    return { type: "unregistered" };
-  },
-
-  getPushSubscription() {
-    const registration = this._registration;
-    let pushSubscriptionActor = this._pushSubscriptionActor;
-    if (pushSubscriptionActor) {
-      return Promise.resolve(pushSubscriptionActor);
-    }
-    return new Promise((resolve, reject) => {
-      PushService.getSubscription(
-        registration.scope,
-        registration.principal,
-        (result, subscription) => {
-          if (!subscription) {
-            resolve(null);
-            return;
-          }
-          pushSubscriptionActor = new PushSubscriptionActor(this._conn, subscription);
-          this._pushSubscriptionActor = pushSubscriptionActor;
-          resolve(pushSubscriptionActor);
-        }
-      );
-    });
-  },
-});
-
-exports.ServiceWorkerRegistrationActor = ServiceWorkerRegistrationActor;
+exports.ServiceWorkerActor = ServiceWorkerActor;
copy from devtools/server/actors/worker/worker-list.js
copy to devtools/server/actors/worker/worker-target-actor-list.js
--- a/devtools/server/actors/worker/worker-list.js
+++ b/devtools/server/actors/worker/worker-target-actor-list.js
@@ -1,31 +1,23 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-const { Ci } = require("chrome");
 const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
 loader.lazyRequireGetter(this, "WorkerTargetActor", "devtools/server/actors/targets/worker", true);
-loader.lazyRequireGetter(this, "ServiceWorkerRegistrationActor", "devtools/server/actors/worker/service-worker", true);
 
 XPCOMUtils.defineLazyServiceGetter(
   this, "wdm",
   "@mozilla.org/dom/workers/workerdebuggermanager;1",
   "nsIWorkerDebuggerManager"
 );
 
-XPCOMUtils.defineLazyServiceGetter(
-  this, "swm",
-  "@mozilla.org/serviceworkers/manager;1",
-  "nsIServiceWorkerManager"
-);
-
 function matchWorkerDebugger(dbg, options) {
   if ("type" in options && dbg.type !== options.type) {
     return false;
   }
   if ("window" in options) {
     let window = dbg.window;
     while (window !== null && window.parent !== window) {
       window = window.parent;
@@ -129,97 +121,8 @@ WorkerTargetActorList.prototype = {
   onUnregister(dbg) {
     if (matchWorkerDebugger(dbg, this._options)) {
       this._notifyListChanged();
     }
   },
 };
 
 exports.WorkerTargetActorList = WorkerTargetActorList;
-
-function ServiceWorkerRegistrationActorList(conn) {
-  this._conn = conn;
-  this._actors = new Map();
-  this._onListChanged = null;
-  this._mustNotify = false;
-  this.onRegister = this.onRegister.bind(this);
-  this.onUnregister = this.onUnregister.bind(this);
-}
-
-ServiceWorkerRegistrationActorList.prototype = {
-  getList() {
-    // Create a set of registrations.
-    const registrations = new Set();
-    const array = swm.getAllRegistrations();
-    for (let index = 0; index < array.length; ++index) {
-      registrations.add(
-        array.queryElementAt(index, Ci.nsIServiceWorkerRegistrationInfo));
-    }
-
-    // Delete each actor for which we don't have a registration.
-    for (const [registration ] of this._actors) {
-      if (!registrations.has(registration)) {
-        this._actors.delete(registration);
-      }
-    }
-
-    // Create an actor for each registration for which we don't have one.
-    for (const registration of registrations) {
-      if (!this._actors.has(registration)) {
-        this._actors.set(registration,
-          new ServiceWorkerRegistrationActor(this._conn, registration));
-      }
-    }
-
-    if (!this._mustNotify) {
-      if (this._onListChanged !== null) {
-        swm.addListener(this);
-      }
-      this._mustNotify = true;
-    }
-
-    const actors = [];
-    for (const [, actor] of this._actors) {
-      actors.push(actor);
-    }
-
-    return Promise.resolve(actors);
-  },
-
-  get onListchanged() {
-    return this._onListchanged;
-  },
-
-  set onListChanged(onListChanged) {
-    if (typeof onListChanged !== "function" && onListChanged !== null) {
-      throw new Error("onListChanged must be either a function or null.");
-    }
-
-    if (this._mustNotify) {
-      if (this._onListChanged === null && onListChanged !== null) {
-        swm.addListener(this);
-      }
-      if (this._onListChanged !== null && onListChanged === null) {
-        swm.removeListener(this);
-      }
-    }
-    this._onListChanged = onListChanged;
-  },
-
-  _notifyListChanged() {
-    this._onListChanged();
-
-    if (this._onListChanged !== null) {
-      swm.removeListener(this);
-    }
-    this._mustNotify = false;
-  },
-
-  onRegister(registration) {
-    this._notifyListChanged();
-  },
-
-  onUnregister(registration) {
-    this._notifyListChanged();
-  },
-};
-
-exports.ServiceWorkerRegistrationActorList = ServiceWorkerRegistrationActorList;
--- a/devtools/shared/fronts/moz.build
+++ b/devtools/shared/fronts/moz.build
@@ -3,16 +3,17 @@
 # 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/.
 
 DIRS += [
     'addon',
     'inspector',
     'targets',
+    'worker',
 ]
 
 DevToolsModules(
     'accessibility.js',
     'actor-registry.js',
     'animation.js',
     'canvas.js',
     'changes.js',
--- a/devtools/shared/fronts/root.js
+++ b/devtools/shared/fronts/root.js
@@ -77,25 +77,29 @@ class RootFront extends FrontClassWithSp
     }
 
     const result = {
       service: [],
       shared: [],
       other: [],
     };
 
-    registrations.forEach(form => {
+    registrations.forEach(front => {
+      // All the information is simply mirrored from the registration front.
+      // However since registering workers will fetch similar information from the worker
+      // target front and will not have a service worker registration front, consumers
+      // should not read meta data directly on the registration front instance.
       result.service.push({
-        name: form.url,
-        url: form.url,
-        scope: form.scope,
-        fetch: form.fetch,
-        registrationActor: form.actor,
-        active: form.active,
-        lastUpdateTime: form.lastUpdateTime,
+        active: front.active,
+        fetch: front.fetch,
+        lastUpdateTime: front.lastUpdateTime,
+        name: front.url,
+        registrationFront: front,
+        scope: front.scope,
+        url: front.url,
       });
     });
 
     workers.forEach(front => {
       const worker = {
         name: front.url,
         url: front.url,
         workerTargetFront: front,
new file mode 100644
--- /dev/null
+++ b/devtools/shared/fronts/worker/moz.build
@@ -0,0 +1,11 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+DevToolsModules(
+    'push-subscription.js',
+    'service-worker-registration.js',
+    'service-worker.js',
+)
new file mode 100644
--- /dev/null
+++ b/devtools/shared/fronts/worker/push-subscription.js
@@ -0,0 +1,39 @@
+/* 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 { pushSubscriptionSpec } =
+  require("devtools/shared/specs/worker/push-subscription");
+const { FrontClassWithSpec, registerFront } = require("devtools/shared/protocol");
+
+class PushSubscriptionFront extends FrontClassWithSpec(pushSubscriptionSpec) {
+  get endpoint() {
+    return this._form.endpoint;
+  }
+
+  get pushCount() {
+    return this._form.pushCount;
+  }
+
+  get lastPush() {
+    return this._form.lastPush;
+  }
+
+  get quota() {
+    return this._form.quota;
+  }
+
+  form(form, detail) {
+    if (detail === "actorid") {
+      this.actorID = form;
+      return;
+    }
+
+    this.actorID = form.actor;
+    this._form = form;
+  }
+}
+
+exports.PushSubscriptionFront = PushSubscriptionFront;
+registerFront(PushSubscriptionFront);
new file mode 100644
--- /dev/null
+++ b/devtools/shared/fronts/worker/service-worker-registration.js
@@ -0,0 +1,68 @@
+/* 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 { serviceWorkerRegistrationSpec } =
+  require("devtools/shared/specs/worker/service-worker-registration");
+const { FrontClassWithSpec, registerFront, types } = require("devtools/shared/protocol");
+
+class ServiceWorkerRegistrationFront extends
+  FrontClassWithSpec(serviceWorkerRegistrationSpec) {
+  get active() {
+    return this._form.active;
+  }
+
+  get fetch() {
+    return this._form.fetch;
+  }
+
+  get lastUpdateTime() {
+    return this._form.lastUpdateTime;
+  }
+
+  get scope() {
+    return this._form.scope;
+  }
+
+  get type() {
+    return this._form.type;
+  }
+
+  get url() {
+    return this._form.url;
+  }
+
+  get activeWorker() {
+    return this._getServiceWorker("activeWorker");
+  }
+
+  get installingWorker() {
+    return this._getServiceWorker("installingWorker");
+  }
+
+  get waitingWorker() {
+    return this._getServiceWorker("waitingWorker");
+  }
+
+  _getServiceWorker(type) {
+    const workerForm = this._form[type];
+    if (!workerForm) {
+      return null;
+    }
+    return types.getType("serviceWorker").read(workerForm, this);
+  }
+
+  form(form, detail) {
+    if (detail === "actorid") {
+      this.actorID = form;
+      return;
+    }
+
+    this.actorID = form.actor;
+    this._form = form;
+  }
+}
+
+exports.ServiceWorkerRegistrationFront = ServiceWorkerRegistrationFront;
+registerFront(ServiceWorkerRegistrationFront);
new file mode 100644
--- /dev/null
+++ b/devtools/shared/fronts/worker/service-worker.js
@@ -0,0 +1,34 @@
+/* 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 { serviceWorkerSpec } = require("devtools/shared/specs/worker/service-worker");
+const { FrontClassWithSpec, registerFront } = require("devtools/shared/protocol");
+
+class ServiceWorkerFront extends FrontClassWithSpec(serviceWorkerSpec) {
+  get fetch() {
+    return this._form.fetch;
+  }
+
+  get url() {
+    return this._form.url;
+  }
+
+  get state() {
+    return this._form.state;
+  }
+
+  form(form, detail) {
+    if (detail === "actorid") {
+      this.actorID = form;
+      return;
+    }
+
+    this.actorID = form.actor;
+    this._form = form;
+  }
+}
+
+exports.ServiceWorkerFront = ServiceWorkerFront;
+registerFront(ServiceWorkerFront);
--- a/devtools/shared/specs/index.js
+++ b/devtools/shared/specs/index.js
@@ -285,19 +285,29 @@ const Types = exports.__TypesForTests = 
     front: null,
   },
   {
     types: ["gl-shader", "gl-program", "webgl"],
     spec: "devtools/shared/specs/webgl",
     front: "devtools/shared/fronts/webgl",
   },
   {
-    types: ["pushSubscription", "serviceWorkerRegistration", "serviceWorker"],
+    types: ["pushSubscription"],
+    spec: "devtools/shared/specs/worker/push-subscription",
+    front: "devtools/shared/fronts/worker/push-subscription",
+  },
+  {
+    types: ["serviceWorker"],
     spec: "devtools/shared/specs/worker/service-worker",
-    front: null,
+    front: "devtools/shared/fronts/worker/service-worker",
+  },
+  {
+    types: ["serviceWorkerRegistration"],
+    spec: "devtools/shared/specs/worker/service-worker-registration",
+    front: "devtools/shared/fronts/worker/service-worker-registration",
   },
 ];
 
 const lazySpecs = new Map();
 const lazyFronts = new Map();
 
 // Convert the human readable `Types` list into efficient maps
 Types.forEach(item => {
--- a/devtools/shared/specs/root.js
+++ b/devtools/shared/specs/root.js
@@ -4,17 +4,17 @@
 "use strict";
 
 const { types, generateActorSpec, RetVal, Arg, Option } = require("devtools/shared/protocol");
 
 types.addDictType("root.listWorkers", {
   workers: "array:workerTarget",
 });
 types.addDictType("root.listServiceWorkerRegistrations", {
-  registrations: "array:json",
+  registrations: "array:serviceWorkerRegistration",
 });
 types.addDictType("root.listProcesses", {
   processes: "array:json",
 });
 types.addDictType("root.listTabs", {
   tabs: "array:browsingContextTarget",
   selected: "number",
 });
--- a/devtools/shared/specs/worker/moz.build
+++ b/devtools/shared/specs/worker/moz.build
@@ -1,9 +1,11 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
 DevToolsModules(
+    'push-subscription.js',
+    'service-worker-registration.js',
     'service-worker.js',
 )
copy from devtools/shared/specs/worker/service-worker.js
copy to devtools/shared/specs/worker/push-subscription.js
--- a/devtools/shared/specs/worker/service-worker.js
+++ b/devtools/shared/specs/worker/push-subscription.js
@@ -1,51 +1,12 @@
 /* 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 {RetVal, generateActorSpec} = require("devtools/shared/protocol");
+const { generateActorSpec } = require("devtools/shared/protocol");
 
 const pushSubscriptionSpec = generateActorSpec({
   typeName: "pushSubscription",
 });
 
 exports.pushSubscriptionSpec = pushSubscriptionSpec;
-
-const serviceWorkerRegistrationSpec = generateActorSpec({
-  typeName: "serviceWorkerRegistration",
-
-  events: {
-    "push-subscription-modified": {
-      type: "push-subscription-modified",
-    },
-    "registration-changed": {
-      type: "registration-changed",
-    },
-  },
-
-  methods: {
-    start: {
-      request: {},
-      response: RetVal("json"),
-    },
-    unregister: {
-      request: {},
-      response: RetVal("json"),
-    },
-    getPushSubscription: {
-      request: {},
-      response: {
-        subscription: RetVal("nullable:pushSubscription"),
-      },
-    },
-  },
-});
-
-exports.serviceWorkerRegistrationSpec = serviceWorkerRegistrationSpec;
-
-const serviceWorkerSpec = generateActorSpec({
-  typeName: "serviceWorker",
-});
-
-exports.serviceWorkerSpec = serviceWorkerSpec;
-
copy from devtools/shared/specs/worker/service-worker.js
copy to devtools/shared/specs/worker/service-worker-registration.js
--- a/devtools/shared/specs/worker/service-worker.js
+++ b/devtools/shared/specs/worker/service-worker-registration.js
@@ -1,51 +1,37 @@
 /* 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 {RetVal, generateActorSpec} = require("devtools/shared/protocol");
-
-const pushSubscriptionSpec = generateActorSpec({
-  typeName: "pushSubscription",
-});
-
-exports.pushSubscriptionSpec = pushSubscriptionSpec;
+const { RetVal, generateActorSpec } = require("devtools/shared/protocol");
 
 const serviceWorkerRegistrationSpec = generateActorSpec({
   typeName: "serviceWorkerRegistration",
 
   events: {
     "push-subscription-modified": {
       type: "push-subscription-modified",
     },
     "registration-changed": {
       type: "registration-changed",
     },
   },
 
   methods: {
     start: {
       request: {},
-      response: RetVal("json"),
     },
     unregister: {
       request: {},
-      response: RetVal("json"),
     },
     getPushSubscription: {
       request: {},
       response: {
         subscription: RetVal("nullable:pushSubscription"),
       },
     },
   },
 });
 
 exports.serviceWorkerRegistrationSpec = serviceWorkerRegistrationSpec;
 
-const serviceWorkerSpec = generateActorSpec({
-  typeName: "serviceWorker",
-});
-
-exports.serviceWorkerSpec = serviceWorkerSpec;
-
--- a/devtools/shared/specs/worker/service-worker.js
+++ b/devtools/shared/specs/worker/service-worker.js
@@ -1,51 +1,13 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
-const {RetVal, generateActorSpec} = require("devtools/shared/protocol");
-
-const pushSubscriptionSpec = generateActorSpec({
-  typeName: "pushSubscription",
-});
-
-exports.pushSubscriptionSpec = pushSubscriptionSpec;
-
-const serviceWorkerRegistrationSpec = generateActorSpec({
-  typeName: "serviceWorkerRegistration",
-
-  events: {
-    "push-subscription-modified": {
-      type: "push-subscription-modified",
-    },
-    "registration-changed": {
-      type: "registration-changed",
-    },
-  },
-
-  methods: {
-    start: {
-      request: {},
-      response: RetVal("json"),
-    },
-    unregister: {
-      request: {},
-      response: RetVal("json"),
-    },
-    getPushSubscription: {
-      request: {},
-      response: {
-        subscription: RetVal("nullable:pushSubscription"),
-      },
-    },
-  },
-});
-
-exports.serviceWorkerRegistrationSpec = serviceWorkerRegistrationSpec;
+const { generateActorSpec } = require("devtools/shared/protocol");
 
 const serviceWorkerSpec = generateActorSpec({
   typeName: "serviceWorker",
 });
 
 exports.serviceWorkerSpec = serviceWorkerSpec;
 
--- a/dom/base/AutocompleteFieldList.h
+++ b/dom/base/AutocompleteFieldList.h
@@ -13,16 +13,21 @@
  * The second argument is the string value of the token
  */
 
 #ifndef AUTOCOMPLETE_FIELD_NAME
 #define AUTOCOMPLETE_FIELD_NAME(name_, value_)
 #define DEFINED_AUTOCOMPLETE_FIELD_NAME
 #endif
 
+#ifndef AUTOCOMPLETE_NO_PERSIST_FIELD_NAME
+#define AUTOCOMPLETE_NO_PERSIST_FIELD_NAME(name_, value_)
+#define DEFINED_AUTOCOMPLETE_NO_PERSIST_FIELD_NAME
+#endif
+
 #ifndef AUTOCOMPLETE_CONTACT_FIELD_NAME
 #define AUTOCOMPLETE_CONTACT_FIELD_NAME(name_, value_)
 #define DEFINED_AUTOCOMPLETE_CONTACT_FIELD_NAME
 #endif
 
 #ifndef AUTOCOMPLETE_FIELD_HINT
 #define AUTOCOMPLETE_FIELD_HINT(name_, value_)
 #define DEFINED_AUTOCOMPLETE_FIELD_HINT
@@ -139,16 +144,26 @@ AUTOCOMPLETE_FIELD_NAME(LANGUAGE, "langu
 AUTOCOMPLETE_FIELD_NAME(BDAY, "bday")
 AUTOCOMPLETE_FIELD_NAME(BDAY_DAY, "bday-day")
 AUTOCOMPLETE_FIELD_NAME(BDAY_MONTH, "bday-month")
 AUTOCOMPLETE_FIELD_NAME(BDAY_YEAR, "bday-year")
 AUTOCOMPLETE_FIELD_NAME(SEX, "sex")
 AUTOCOMPLETE_FIELD_NAME(URL, "url")
 AUTOCOMPLETE_FIELD_NAME(PHOTO, "photo")
 
+// Field for which we don't want to automatically persist the value e.g. in
+// session/form history.
+AUTOCOMPLETE_NO_PERSIST_FIELD_NAME(OFF, "off")
+// passwords:
+AUTOCOMPLETE_NO_PERSIST_FIELD_NAME(CURRENT_PASSWORD, "current-password")
+AUTOCOMPLETE_NO_PERSIST_FIELD_NAME(NEW_PASSWORD, "new-password")
+// credit card numbers
+AUTOCOMPLETE_NO_PERSIST_FIELD_NAME(CC_NUMBER, "cc-number")
+AUTOCOMPLETE_NO_PERSIST_FIELD_NAME(CC_CSC, "cc-csc")
+
 // Contact category types
 AUTOCOMPLETE_CONTACT_FIELD_NAME(TEL, "tel")
 AUTOCOMPLETE_CONTACT_FIELD_NAME(TEL_COUNTRY_CODE, "tel-country-code")
 AUTOCOMPLETE_CONTACT_FIELD_NAME(TEL_NATIONAL, "tel-national")
 AUTOCOMPLETE_CONTACT_FIELD_NAME(TEL_AREA_CODE, "tel-area-code")
 AUTOCOMPLETE_CONTACT_FIELD_NAME(TEL_LOCAL, "tel-local")
 AUTOCOMPLETE_CONTACT_FIELD_NAME(TEL_LOCAL_PREFIX, "tel-local-prefix")
 AUTOCOMPLETE_CONTACT_FIELD_NAME(TEL_LOCAL_SUFFIX, "tel-local-suffix")
@@ -179,16 +194,21 @@ AUTOCOMPLETE_CATEGORY(CONTACT, "contact"
 #undef DEFINED_AUTOCOMPLETE_UNSUPPORTED_FIELD_CONTACT_HINT
 #endif
 
 #ifdef DEFINED_AUTOCOMPLETE_FIELD_NAME
 #undef AUTOCOMPLETE_FIELD_NAME
 #undef DEFINED_AUTOCOMPLETE_FIELD_NAME
 #endif
 
+#ifdef DEFINED_AUTOCOMPLETE_NO_PERSIST_FIELD_NAME
+#undef AUTOCOMPLETE_NO_PERSIST_FIELD_NAME
+#undef DEFINED_AUTOCOMPLETE_NO_PERSIST_FIELD_NAME
+#endif
+
 #ifdef DEFINED_AUTOCOMPLETE_CONTACT_FIELD_NAME
 #undef AUTOCOMPLETE_CONTACT_FIELD_NAME
 #undef DEFINED_AUTOCOMPLETE_CONTACT_FIELD_NAME
 #endif
 
 #ifdef DEFINED_AUTOCOMPLETE_FIELD_HINT
 #undef AUTOCOMPLETE_FIELD_HINT
 #undef DEFINED_AUTOCOMPLETE_FIELD_HINT
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -357,16 +357,23 @@ uint32_t nsContentUtils::sInnerOrOuterWi
 // http://www.whatwg.org/specs/web-apps/current-work/#autofill-field-name
 enum AutocompleteUnsupportedFieldName : uint8_t {
 #define AUTOCOMPLETE_UNSUPPORTED_FIELD_NAME(name_, value_) \
   eAutocompleteUnsupportedFieldName_##name_,
 #include "AutocompleteFieldList.h"
 #undef AUTOCOMPLETE_UNSUPPORTED_FIELD_NAME
 };
 
+enum AutocompleteNoPersistFieldName : uint8_t {
+#define AUTOCOMPLETE_NO_PERSIST_FIELD_NAME(name_, value_) \
+  eAutocompleteNoPersistFieldName_##name_,
+#include "AutocompleteFieldList.h"
+#undef AUTOCOMPLETE_NO_PERSIST_FIELD_NAME
+};
+
 enum AutocompleteUnsupportFieldContactHint : uint8_t {
 #define AUTOCOMPLETE_UNSUPPORTED_FIELD_CONTACT_HINT(name_, value_) \
   eAutocompleteUnsupportedFieldContactHint_##name_,
 #include "AutocompleteFieldList.h"
 #undef AUTOCOMPLETE_UNSUPPORTED_FIELD_CONTACT_HINT
 };
 
 enum AutocompleteFieldName : uint8_t {
@@ -399,16 +406,23 @@ enum AutocompleteCategory {
 
 static const nsAttrValue::EnumTable kAutocompleteUnsupportedFieldNameTable[] = {
 #define AUTOCOMPLETE_UNSUPPORTED_FIELD_NAME(name_, value_) \
   {value_, eAutocompleteUnsupportedFieldName_##name_},
 #include "AutocompleteFieldList.h"
 #undef AUTOCOMPLETE_UNSUPPORTED_FIELD_NAME
     {nullptr, 0}};
 
+static const nsAttrValue::EnumTable kAutocompleteNoPersistFieldNameTable[] = {
+#define AUTOCOMPLETE_NO_PERSIST_FIELD_NAME(name_, value_) \
+  {value_, eAutocompleteNoPersistFieldName_##name_},
+#include "AutocompleteFieldList.h"
+#undef AUTOCOMPLETE_NO_PERSIST_FIELD_NAME
+    {nullptr, 0}};
+
 static const nsAttrValue::EnumTable
     kAutocompleteUnsupportedContactFieldHintTable[] = {
 #define AUTOCOMPLETE_UNSUPPORTED_FIELD_CONTACT_HINT(name_, value_) \
   {value_, eAutocompleteUnsupportedFieldContactHint_##name_},
 #include "AutocompleteFieldList.h"
 #undef AUTOCOMPLETE_UNSUPPORTED_FIELD_CONTACT_HINT
         {nullptr, 0}};
 
@@ -1127,16 +1141,18 @@ nsContentUtils::InternalSerializeAutocom
     if (enumValue.Equals(NS_LITERAL_STRING("off"), eIgnoreCase) ||
         enumValue.Equals(NS_LITERAL_STRING("on"), eIgnoreCase)) {
       if (numTokens > 1) {
         return eAutocompleteAttrState_Invalid;
       }
       enumValue.ToString(str);
       ASCIIToLower(str);
       aInfo.mFieldName.Assign(str);
+      aInfo.mCanAutomaticallyPersist =
+          !enumValue.Equals(NS_LITERAL_STRING("off"), eIgnoreCase);
       return eAutocompleteAttrState_Valid;
     }
 
     // Only allow on/off if form autofill @autocomplete values aren't enabled
     // and it doesn't grant all valid values.
     if (!sIsFormAutofillAutocompleteEnabled && !aGrantAllValidValue) {
       return eAutocompleteAttrState_Invalid;
     }
@@ -1161,16 +1177,19 @@ nsContentUtils::InternalSerializeAutocom
 
     category = eAutocompleteCategory_CONTACT;
   }
 
   enumValue.ToString(str);
   ASCIIToLower(str);
   aInfo.mFieldName.Assign(str);
 
+  aInfo.mCanAutomaticallyPersist = !enumValue.ParseEnumValue(
+      tokenString, kAutocompleteNoPersistFieldNameTable, false);
+
   // We are done if this was the only token.
   if (numTokens == 1) {
     return eAutocompleteAttrState_Valid;
   }
 
   --index;
   tokenString = nsDependentAtomString(aAttrVal->AtomAt(index));
 
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -1392,16 +1392,33 @@ nsDOMWindowUtils::GetScrollXYFloat(bool 
   NS_ENSURE_SUCCESS(rv, rv);
   *aScrollX = nsPresContext::AppUnitsToFloatCSSPixels(scrollPos.x);
   *aScrollY = nsPresContext::AppUnitsToFloatCSSPixels(scrollPos.y);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsDOMWindowUtils::ScrollToVisual(float aOffsetX, float aOffsetY) {
+  nsCOMPtr<Document> doc = GetDocument();
+  NS_ENSURE_STATE(doc);
+
+  nsPresContext* presContext = doc->GetPresContext();
+  NS_ENSURE_TRUE(presContext, NS_ERROR_NOT_AVAILABLE);
+
+  // This should only be called on the root content document.
+  NS_ENSURE_TRUE(presContext->IsRootContentDocument(), NS_ERROR_INVALID_ARG);
+
+  presContext->PresShell()->SetPendingVisualViewportOffset(
+      Some(CSSPoint::ToAppUnits(CSSPoint(aOffsetX, aOffsetY))));
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsDOMWindowUtils::GetVisualViewportOffsetRelativeToLayoutViewport(
     float* aOffsetX, float* aOffsetY) {
   *aOffsetX = 0;
   *aOffsetY = 0;
 
   nsCOMPtr<Document> doc = GetDocument();
   NS_ENSURE_STATE(doc);
 
--- a/dom/events/DataTransferItemList.cpp
+++ b/dom/events/DataTransferItemList.cpp
@@ -168,17 +168,17 @@ DataTransferItem* DataTransferItemList::
 DataTransferItem* DataTransferItemList::Add(File& aData,
                                             nsIPrincipal& aSubjectPrincipal,
                                             ErrorResult& aRv) {
   if (mDataTransfer->IsReadOnly()) {
     return nullptr;
   }
 
   nsCOMPtr<nsISupports> supports = do_QueryObject(&aData);
-  nsCOMPtr<nsIWritableVariant> data = new nsVariant();
+  nsCOMPtr<nsIWritableVariant> data = new nsVariantCC();
   data->SetAsISupports(supports);
 
   nsAutoString type;
   aData.GetType(type);
 
   if (!DataTransfer::PrincipalMaySetData(type, data, &aSubjectPrincipal)) {
     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
     return nullptr;
--- a/dom/html/test/forms/test_autocompleteinfo.html
+++ b/dom/html/test/forms/test_autocompleteinfo.html
@@ -25,22 +25,25 @@ Test getAutocompleteInfo() on <input> an
 var values = [
   // Missing or empty attribute
   [undefined, {}, ""],
   ["", {}, ""],
 
   // One token
   ["on", {fieldName: "on" }, "on"],
   ["On", {fieldName: "on" }, "on"],
-  ["off", {fieldName: "off" }, "off" ],
+  ["off", {fieldName: "off", canAutomaticallyPersist: false}, "off" ],
   ["name", {fieldName: "name" }, "name"],
   [" name ", {fieldName: "name" }, "name"],
   ["username", {fieldName: "username"}, ""],
   [" username ", {fieldName: "username"}, ""],
-  ["cc-csc", {fieldName: "cc-csc"}, ""],
+  ["current-password", {fieldName: "current-password", canAutomaticallyPersist: false}, ""],
+  ["new-password", {fieldName: "new-password", canAutomaticallyPersist: false}, ""],
+  ["cc-number", {fieldName: "cc-number", canAutomaticallyPersist: false}, "cc-number"],
+  ["cc-csc", {fieldName: "cc-csc", canAutomaticallyPersist: false}, ""],
   ["language", {fieldName: "language"}, ""],
   ["tel-extension", {fieldName: "tel-extension"}, ""],
   ["foobar", {}, ""],
   ["section-blue", {}, ""],
 
   // Two tokens
   ["on off", {}, ""],
   ["off on", {}, ""],
@@ -134,16 +137,18 @@ function testAutocompleteInfoValue(aEnab
       is(info.section, "section" in test[1] ? test[1].section : "",
         "Checking autocompleteInfo.section for " + field + ": " + test[0]);
       is(info.addressType, "addressType" in test[1] ? test[1].addressType : "",
         "Checking autocompleteInfo.addressType for " + field + ": " + test[0]);
       is(info.contactType, "contactType" in test[1] ? test[1].contactType : "",
         "Checking autocompleteInfo.contactType for " + field + ": " + test[0]);
       is(info.fieldName, "fieldName" in test[1] ? test[1].fieldName : "",
         "Checking autocompleteInfo.fieldName for " + field + ": " + test[0]);
+      is(info.canAutomaticallyPersist, "canAutomaticallyPersist" in test[1] ? test[1].canAutomaticallyPersist : true,
+        "Checking autocompleteInfo.canAutomaticallyPersist for " + field + ": " + test[0]);
     }
   }
 }
 
 function testAutocomplete(aField, aType, aEnabled) {
   aField.type = aType;
   if (aEnabled) {
     ok(aField.getAutocompleteInfo() !== null, "getAutocompleteInfo shouldn't return null");
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
@@ -824,16 +824,29 @@ interface nsIDOMWindowUtils : nsISupport
   void getScrollbarSize(in boolean aFlushLayout, out long aWidth, out long aHeight);
 
   /**
    * Returns the given element's bounds without flushing pending layout changes.
    */
   DOMRect getBoundsWithoutFlushing(in Element aElement);
 
   /**
+   * Scroll the visual viewport to the given coordinates, relative to the
+   * document origin.
+   * Only applicable to the window associated with the root content document.
+   * Note: this does not take effect right away. Rather, the visual scroll
+   *       request is sent to APZ with the next transaction, and will be
+   *       reflected in the main thread with the subsequent APZ repaint request.
+   * Please see the caveats mentioned at nsIPresShell::
+   * SetPendingVisualViewportOffset(), and request APZ review if adding a new
+   * call to this.
+   */
+  void scrollToVisual(in float aOffsetX, in float aOffsetY);
+ 
+  /**
    * Returns the offset of the window's visual viewport relative to the
    * layout viewport.
    */
   void getVisualViewportOffsetRelativeToLayoutViewport(out float aOffsetX,
                                                        out float aOffsetY);
 
   const long FLUSH_NONE = -1;
   const long FLUSH_STYLE = 0;
--- a/dom/plugins/test/testplugin/testplugin.mozbuild
+++ b/dom/plugins/test/testplugin/testplugin.mozbuild
@@ -39,17 +39,17 @@ DisableStlWrapping()
 
 NO_PGO = True
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
     RCFILE  = 'nptest.rc'
     RESFILE = 'nptest.res'
     DEFFILE = 'nptest.def'
 
-if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa' and '64' in CONFIG['OS_TEST']:
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa' and CONFIG['TARGET_CPU'] == 'x86_64':
     OS_LIBS += ['-framework Carbon']
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gtk3':
     CXXFLAGS += CONFIG['MOZ_GTK2_CFLAGS']
     CFLAGS += CONFIG['MOZ_GTK2_CFLAGS']
     OS_LIBS += CONFIG['MOZ_GTK2_LIBS']
     OS_LIBS += CONFIG['XLDFLAGS']
     OS_LIBS += CONFIG['XLIBS']
--- a/dom/webidl/AutocompleteInfo.webidl
+++ b/dom/webidl/AutocompleteInfo.webidl
@@ -9,9 +9,10 @@
  * getAutocompleteInfo method.
  */
 
 dictionary AutocompleteInfo {
   DOMString section = "";
   DOMString addressType = "";
   DOMString contactType = "";
   DOMString fieldName = "";
+  boolean canAutomaticallyPersist = true;
 };
--- a/gfx/layers/FrameMetrics.h
+++ b/gfx/layers/FrameMetrics.h
@@ -99,16 +99,18 @@ struct FrameMetrics {
         mSmoothScrollOffset(0, 0),
         mRootCompositionSize(0, 0),
         mDisplayPortMargins(0, 0, 0, 0),
         mPresShellId(-1),
         mLayoutViewport(0, 0, 0, 0),
         mExtraResolution(),
         mPaintRequestTime(),
         mScrollUpdateType(eNone),
+        mVisualViewportOffset(0, 0),
+        mVisualScrollUpdateType(eNone),
         mIsRootContent(false),
         mIsRelative(false),
         mDoSmoothScroll(false),
         mIsScrollInfoLayer(false) {}
 
   // Default copy ctor and operator= are fine
 
   bool operator==(const FrameMetrics& aOther) const {
@@ -128,16 +130,18 @@ struct FrameMetrics {
            mSmoothScrollOffset == aOther.mSmoothScrollOffset &&
            mRootCompositionSize == aOther.mRootCompositionSize &&
            mDisplayPortMargins == aOther.mDisplayPortMargins &&
            mPresShellId == aOther.mPresShellId &&
            mLayoutViewport.IsEqualEdges(aOther.mLayoutViewport) &&
            mExtraResolution == aOther.mExtraResolution &&
            mPaintRequestTime == aOther.mPaintRequestTime &&
            mScrollUpdateType == aOther.mScrollUpdateType &&
+           mVisualViewportOffset == aOther.mVisualViewportOffset &&
+           mVisualScrollUpdateType == aOther.mVisualScrollUpdateType &&
            mIsRootContent == aOther.mIsRootContent &&
            mIsRelative == aOther.mIsRelative &&
            mDoSmoothScroll == aOther.mDoSmoothScroll &&
            mIsScrollInfoLayer == aOther.mIsScrollInfoLayer;
   }
 
   bool operator!=(const FrameMetrics& aOther) const {
     return !operator==(aOther);
@@ -475,16 +479,30 @@ struct FrameMetrics {
   }
   const TimeStamp& GetPaintRequestTime() const { return mPaintRequestTime; }
 
   void SetIsScrollInfoLayer(bool aIsScrollInfoLayer) {
     mIsScrollInfoLayer = aIsScrollInfoLayer;
   }
   bool IsScrollInfoLayer() const { return mIsScrollInfoLayer; }
 
+  void SetVisualViewportOffset(const CSSPoint& aVisualViewportOffset) {
+    mVisualViewportOffset = aVisualViewportOffset;
+  }
+  const CSSPoint& GetVisualViewportOffset() const {
+    return mVisualViewportOffset;
+  }
+
+  void SetVisualScrollUpdateType(ScrollOffsetUpdateType aUpdateType) {
+    mVisualScrollUpdateType = aUpdateType;
+  }
+  ScrollOffsetUpdateType GetVisualScrollUpdateType() const {
+    return mVisualScrollUpdateType;
+  }
+
   // Determine if the visual viewport is outside of the layout viewport and
   // adjust the x,y-offset in mLayoutViewport accordingly. This is necessary to
   // allow APZ to async-scroll the layout viewport.
   //
   // This is a no-op if mIsRootContent is false.
   void RecalculateLayoutViewportOffset();
 
   // Helper function for RecalculateViewportOffset(). Exposed so that
@@ -635,16 +653,28 @@ struct FrameMetrics {
 
   // The time at which the APZC last requested a repaint for this scroll frame.
   TimeStamp mPaintRequestTime;
 
   // Whether mScrollOffset was updated by something other than the APZ code, and
   // if the APZC receiving this metrics should update its local copy.
   ScrollOffsetUpdateType mScrollUpdateType;
 
+  // These fields are used when the main thread wants to set a visual viewport
+  // offset that's distinct from the layout viewport offset.
+  // In this case, mVisualScrollUpdateType is set to eMainThread, and
+  // mVisualViewportOffset is set to desired visual viewport offset (relative
+  // to the document, like mScrollOffset).
+  // TODO: Get rid of mVisualViewportOffset: between mViewport.TopLeft() and
+  //       mScrollOffset, we have enough storage for the two scroll offsets.
+  //       However, to avoid confusion, that first requires refactoring
+  //       existing to consistently use the two fields for those two purposes.
+  CSSPoint mVisualViewportOffset;
+  ScrollOffsetUpdateType mVisualScrollUpdateType;
+
   // Whether or not this is the root scroll frame for the root content document.
   bool mIsRootContent : 1;
 
   // When mIsRelative, the scroll offset was updated using a relative API,
   // such as `ScrollBy`, and can combined with an async scroll.
   bool mIsRelative : 1;
 
   // When mDoSmoothScroll, the scroll offset should be animated to
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -4291,17 +4291,17 @@ void AsyncPanZoomController::NotifyLayer
                           Metrics().GetCompositionBounds().Width()) &&
       FuzzyEqualsAdditive(aLayerMetrics.GetCompositionBounds().Height(),
                           Metrics().GetCompositionBounds().Height());
 #if defined(MOZ_WIDGET_ANDROID)
   entertainViewportUpdates = true;
 #endif
   if (entertainViewportUpdates) {
     if (Metrics().GetLayoutViewport().Size() !=
-            aLayerMetrics.GetLayoutViewport().Size()) {
+        aLayerMetrics.GetLayoutViewport().Size()) {
       needContentRepaint = true;
       viewportUpdated = true;
     }
     if (viewportUpdated || scrollOffsetUpdated) {
       Metrics().SetLayoutViewport(aLayerMetrics.GetLayoutViewport());
     }
   }
 
@@ -4495,16 +4495,43 @@ void AsyncPanZoomController::NotifyLayer
       Metrics().ApplySmoothScrollUpdateFrom(aLayerMetrics);
     }
     needContentRepaint = true;
     mExpectedGeckoMetrics = aLayerMetrics;
 
     SmoothScrollTo(Metrics().GetSmoothScrollOffset());
   }
 
+  // If the main thread asked us to scroll the visual viewport to a particular
+  // location, do so. This is different from a layout viewport offset update
+  // in that the layout viewport offset is limited to the layout scroll range
+  // (this will be enforced by the main thread once bug 1516056 is fixed),
+  // while the visual viewport offset is not. The main thread can also ask
+  // us to scroll both the layout and visual viewports to distinct (but
+  // compatible) locations.
+  FrameMetrics::ScrollOffsetUpdateType visualUpdateType =
+      aLayerMetrics.GetVisualScrollUpdateType();
+  MOZ_ASSERT(visualUpdateType == FrameMetrics::eNone ||
+             visualUpdateType == FrameMetrics::eMainThread);
+  if (visualUpdateType == FrameMetrics::eMainThread) {
+    Metrics().ClampAndSetScrollOffset(aLayerMetrics.GetVisualViewportOffset());
+
+    // The rest of this branch largely follows the code in the
+    // |if (scrollOffsetUpdated)| branch above.
+    Metrics().RecalculateLayoutViewportOffset();
+    mCompositedLayoutViewport = Metrics().GetLayoutViewport();
+    mCompositedScrollOffset = Metrics().GetScrollOffset();
+    mExpectedGeckoMetrics = aLayerMetrics;
+    if (!mAnimation || !mAnimation->HandleScrollOffsetUpdate(Nothing())) {
+      CancelAnimation();
+    }
+    needContentRepaint = true;
+    ScheduleComposite();
+  }
+
   if (viewportUpdated) {
     // While we want to accept the main thread's layout viewport _size_,
     // its position may be out of date in light of async scrolling, to
     // adjust it if necessary to make sure it continues to enclose the
     // visual viewport.
     // Note: it's important to do this _after_ we've accepted any
     // updated composition bounds.
     Metrics().RecalculateLayoutViewportOffset();
--- a/gfx/layers/ipc/LayersMessageUtils.h
+++ b/gfx/layers/ipc/LayersMessageUtils.h
@@ -190,16 +190,18 @@ struct ParamTraits<mozilla::layers::Fram
     WriteParam(aMsg, aParam.mSmoothScrollOffset);
     WriteParam(aMsg, aParam.mRootCompositionSize);
     WriteParam(aMsg, aParam.mDisplayPortMargins);
     WriteParam(aMsg, aParam.mPresShellId);
     WriteParam(aMsg, aParam.mLayoutViewport);
     WriteParam(aMsg, aParam.mExtraResolution);
     WriteParam(aMsg, aParam.mPaintRequestTime);
     WriteParam(aMsg, aParam.mScrollUpdateType);
+    WriteParam(aMsg, aParam.mVisualViewportOffset);
+    WriteParam(aMsg, aParam.mVisualScrollUpdateType);
     WriteParam(aMsg, aParam.mIsRootContent);
     WriteParam(aMsg, aParam.mIsRelative);
     WriteParam(aMsg, aParam.mDoSmoothScroll);
     WriteParam(aMsg, aParam.mIsScrollInfoLayer);
   }
 
   static bool Read(const Message* aMsg, PickleIterator* aIter,
                    paramType* aResult) {
@@ -219,16 +221,18 @@ struct ParamTraits<mozilla::layers::Fram
         ReadParam(aMsg, aIter, &aResult->mSmoothScrollOffset) &&
         ReadParam(aMsg, aIter, &aResult->mRootCompositionSize) &&
         ReadParam(aMsg, aIter, &aResult->mDisplayPortMargins) &&
         ReadParam(aMsg, aIter, &aResult->mPresShellId) &&
         ReadParam(aMsg, aIter, &aResult->mLayoutViewport) &&
         ReadParam(aMsg, aIter, &aResult->mExtraResolution) &&
         ReadParam(aMsg, aIter, &aResult->mPaintRequestTime) &&
         ReadParam(aMsg, aIter, &aResult->mScrollUpdateType) &&
+        ReadParam(aMsg, aIter, &aResult->mVisualViewportOffset) &&
+        ReadParam(aMsg, aIter, &aResult->mVisualScrollUpdateType) &&
         ReadBoolForBitfield(aMsg, aIter, aResult,
                             &paramType::SetIsRootContent) &&
         ReadBoolForBitfield(aMsg, aIter, aResult, &paramType::SetIsRelative) &&
         ReadBoolForBitfield(aMsg, aIter, aResult,
                             &paramType::SetDoSmoothScroll) &&
         ReadBoolForBitfield(aMsg, aIter, aResult,
                             &paramType::SetIsScrollInfoLayer));
   }
--- a/gfx/layers/wr/AsyncImagePipelineManager.cpp
+++ b/gfx/layers/wr/AsyncImagePipelineManager.cpp
@@ -398,17 +398,17 @@ void AsyncImagePipelineManager::ApplyAsy
                              aPipeline->mScBounds.Height()};
   wr::DisplayListBuilder builder(aPipelineId, contentSize);
 
   float opacity = 1.0f;
   Maybe<wr::WrSpatialId> referenceFrameId = builder.PushStackingContext(
       wr::ToRoundedLayoutRect(aPipeline->mScBounds), nullptr, nullptr, &opacity,
       aPipeline->mScTransform.IsIdentity() ? nullptr : &aPipeline->mScTransform,
       wr::TransformStyle::Flat, nullptr, aPipeline->mMixBlendMode,
-      nsTArray<wr::WrFilterOp>(), true,
+      nsTArray<wr::FilterOp>(), true,
       // This is fine to do unconditionally because we only push images here.
       wr::RasterSpace::Screen());
 
   Maybe<wr::SpaceAndClipChainHelper> spaceAndClipChainHelper;
   if (referenceFrameId) {
     spaceAndClipChainHelper.emplace(builder, referenceFrameId.ref());
   }
 
--- a/gfx/layers/wr/StackingContextHelper.cpp
+++ b/gfx/layers/wr/StackingContextHelper.cpp
@@ -19,17 +19,17 @@ StackingContextHelper::StackingContextHe
       mIsPreserve3D(false),
       mRasterizeLocally(false) {
   // mOrigin remains at 0,0
 }
 
 StackingContextHelper::StackingContextHelper(
     const StackingContextHelper& aParentSC, const ActiveScrolledRoot* aAsr,
     nsIFrame* aContainerFrame, nsDisplayItem* aContainerItem,
-    wr::DisplayListBuilder& aBuilder, const nsTArray<wr::WrFilterOp>& aFilters,
+    wr::DisplayListBuilder& aBuilder, const nsTArray<wr::FilterOp>& aFilters,
     const LayoutDeviceRect& aBounds, const gfx::Matrix4x4* aBoundTransform,
     const wr::WrAnimationProperty* aAnimation, const float* aOpacityPtr,
     const gfx::Matrix4x4* aTransformPtr, const gfx::Matrix4x4* aPerspectivePtr,
     const gfx::CompositionOp& aMixBlendMode, bool aBackfaceVisible,
     bool aIsPreserve3D,
     const Maybe<nsDisplayTransform*>& aDeferredTransformItem,
     const wr::WrClipId* aClipNodeId, bool aAnimated)
     : mBuilder(&aBuilder),
--- a/gfx/layers/wr/StackingContextHelper.h
+++ b/gfx/layers/wr/StackingContextHelper.h
@@ -26,17 +26,17 @@ namespace layers {
  * some of the coordinate space transformations needed.
  */
 class MOZ_RAII StackingContextHelper {
  public:
   StackingContextHelper(
       const StackingContextHelper& aParentSC, const ActiveScrolledRoot* aAsr,
       nsIFrame* aContainerFrame, nsDisplayItem* aContainerItem,
       wr::DisplayListBuilder& aBuilder,
-      const nsTArray<wr::WrFilterOp>& aFilters = nsTArray<wr::WrFilterOp>(),
+      const nsTArray<wr::FilterOp>& aFilters = nsTArray<wr::FilterOp>(),
       const LayoutDeviceRect& aBounds = LayoutDeviceRect(),
       const gfx::Matrix4x4* aBoundTransform = nullptr,
       const wr::WrAnimationProperty* aAnimation = nullptr,
       const float* aOpacityPtr = nullptr,
       const gfx::Matrix4x4* aTransformPtr = nullptr,
       const gfx::Matrix4x4* aPerspectivePtr = nullptr,
       const gfx::CompositionOp& aMixBlendMode = gfx::CompositionOp::OP_OVER,
       bool aBackfaceVisible = true, bool aIsPreserve3D = false,
--- a/gfx/layers/wr/WebRenderCommandBuilder.cpp
+++ b/gfx/layers/wr/WebRenderCommandBuilder.cpp
@@ -1427,17 +1427,17 @@ void WebRenderCommandBuilder::EmptyTrans
 bool WebRenderCommandBuilder::NeedsEmptyTransaction() {
   return !mLastCanvasDatas.IsEmpty();
 }
 
 void WebRenderCommandBuilder::BuildWebRenderCommands(
     wr::DisplayListBuilder& aBuilder,
     wr::IpcResourceUpdateQueue& aResourceUpdates, nsDisplayList* aDisplayList,
     nsDisplayListBuilder* aDisplayListBuilder, WebRenderScrollData& aScrollData,
-    wr::LayoutSize& aContentSize, const nsTArray<wr::WrFilterOp>& aFilters) {
+    wr::LayoutSize& aContentSize, const nsTArray<wr::FilterOp>& aFilters) {
   StackingContextHelper sc;
   aScrollData = WebRenderScrollData(mManager);
   MOZ_ASSERT(mLayerScrollData.empty());
   mLastCanvasDatas.Clear();
   mLastAsr = nullptr;
   mBuilderDumpIndex = 0;
   mContainsSVGGroup = false;
   MOZ_ASSERT(mDumpIndent == 0);
--- a/gfx/layers/wr/WebRenderCommandBuilder.h
+++ b/gfx/layers/wr/WebRenderCommandBuilder.h
@@ -52,17 +52,17 @@ class WebRenderCommandBuilder {
   bool NeedsEmptyTransaction();
 
   void BuildWebRenderCommands(wr::DisplayListBuilder& aBuilder,
                               wr::IpcResourceUpdateQueue& aResourceUpdates,
                               nsDisplayList* aDisplayList,
                               nsDisplayListBuilder* aDisplayListBuilder,
                               WebRenderScrollData& aScrollData,
                               wr::LayoutSize& aContentSize,
-                              const nsTArray<wr::WrFilterOp>& aFilters);
+                              const nsTArray<wr::FilterOp>& aFilters);
 
   void PushOverrideForASR(const ActiveScrolledRoot* aASR,
                           const wr::WrSpatialId& aSpatialId);
   void PopOverrideForASR(const ActiveScrolledRoot* aASR);
 
   Maybe<wr::ImageKey> CreateImageKey(
       nsDisplayItem* aItem, ImageContainer* aContainer,
       mozilla::wr::DisplayListBuilder& aBuilder,
--- a/gfx/layers/wr/WebRenderLayerManager.cpp
+++ b/gfx/layers/wr/WebRenderLayerManager.cpp
@@ -241,17 +241,17 @@ void WebRenderLayerManager::EndTransacti
                                            EndTransactionFlags aFlags) {
   // This should never get called, all callers should use
   // EndTransactionWithoutLayer instead.
   MOZ_ASSERT(false);
 }
 
 void WebRenderLayerManager::EndTransactionWithoutLayer(
     nsDisplayList* aDisplayList, nsDisplayListBuilder* aDisplayListBuilder,
-    const nsTArray<wr::WrFilterOp>& aFilters,
+    const nsTArray<wr::FilterOp>& aFilters,
     WebRenderBackgroundData* aBackground) {
   AUTO_PROFILER_TRACING("Paint", "RenderLayers");
 
   // Since we don't do repeat transactions right now, just set the time
   mAnimationReadyTime = TimeStamp::Now();
 
   WrBridge()->BeginTransaction();
 
--- a/gfx/layers/wr/WebRenderLayerManager.h
+++ b/gfx/layers/wr/WebRenderLayerManager.h
@@ -74,17 +74,17 @@ class WebRenderLayerManager final : publ
 
   virtual bool BeginTransactionWithTarget(gfxContext* aTarget,
                                           const nsCString& aURL) override;
   virtual bool BeginTransaction(const nsCString& aURL) override;
   virtual bool EndEmptyTransaction(
       EndTransactionFlags aFlags = END_DEFAULT) override;
   void EndTransactionWithoutLayer(
       nsDisplayList* aDisplayList, nsDisplayListBuilder* aDisplayListBuilder,
-      const nsTArray<wr::WrFilterOp>& aFilters = nsTArray<wr::WrFilterOp>(),
+      const nsTArray<wr::FilterOp>& aFilters = nsTArray<wr::FilterOp>(),
       WebRenderBackgroundData* aBackground = nullptr);
   virtual void EndTransaction(
       DrawPaintedLayerCallback aCallback, void* aCallbackData,
       EndTransactionFlags aFlags = END_DEFAULT) override;
 
   virtual LayersBackend GetBackendType() override {
     return LayersBackend::LAYERS_WR;
   }
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -574,16 +574,17 @@ void WebRenderDebugPrefChangeCallback(co
   GFX_WEBRENDER_DEBUG(".compact-profiler", 1 << 7)
   GFX_WEBRENDER_DEBUG(".echo-driver-messages", 1 << 8)
   GFX_WEBRENDER_DEBUG(".new-frame-indicator", 1 << 9)
   GFX_WEBRENDER_DEBUG(".new-scene-indicator", 1 << 10)
   GFX_WEBRENDER_DEBUG(".show-overdraw", 1 << 11)
   GFX_WEBRENDER_DEBUG(".gpu-cache", 1 << 12)
   GFX_WEBRENDER_DEBUG(".slow-frame-indicator", 1 << 13)
   GFX_WEBRENDER_DEBUG(".texture-cache.clear-evicted", 1 << 14)
+  GFX_WEBRENDER_DEBUG(".texture-cache.disable-shrink", 1 << 16)
 #undef GFX_WEBRENDER_DEBUG
 
   gfx::gfxVars::SetWebRenderDebugFlags(flags);
 }
 
 #if defined(USE_SKIA)
 static uint32_t GetSkiaGlyphCacheSize() {
   // Only increase font cache size on non-android to save memory.
--- a/gfx/webrender_bindings/WebRenderAPI.cpp
+++ b/gfx/webrender_bindings/WebRenderAPI.cpp
@@ -671,17 +671,17 @@ void DisplayListBuilder::Finalize(wr::La
                           &aOutDisplayList.dl.inner);
 }
 
 Maybe<wr::WrSpatialId> DisplayListBuilder::PushStackingContext(
     const wr::LayoutRect& aBounds, const wr::WrClipId* aClipNodeId,
     const WrAnimationProperty* aAnimation, const float* aOpacity,
     const gfx::Matrix4x4* aTransform, wr::TransformStyle aTransformStyle,
     const gfx::Matrix4x4* aPerspective, const wr::MixBlendMode& aMixBlendMode,
-    const nsTArray<wr::WrFilterOp>& aFilters, bool aIsBackfaceVisible,
+    const nsTArray<wr::FilterOp>& aFilters, bool aIsBackfaceVisible,
     const wr::RasterSpace& aRasterSpace) {
   MOZ_ASSERT(mClipChainLeaf.isNothing(),
              "Non-empty leaf from clip chain given, but not used with SC!");
 
   wr::LayoutTransform matrix;
   if (aTransform) {
     matrix = ToLayoutTransform(*aTransform);
   }
--- a/gfx/webrender_bindings/WebRenderAPI.h
+++ b/gfx/webrender_bindings/WebRenderAPI.h
@@ -330,17 +330,17 @@ class DisplayListBuilder {
 
   Maybe<wr::WrSpatialId> PushStackingContext(
       const wr::LayoutRect&
           aBounds,  // TODO: We should work with strongly typed rects
       const wr::WrClipId* aClipNodeId,
       const wr::WrAnimationProperty* aAnimation, const float* aOpacity,
       const gfx::Matrix4x4* aTransform, wr::TransformStyle aTransformStyle,
       const gfx::Matrix4x4* aPerspective, const wr::MixBlendMode& aMixBlendMode,
-      const nsTArray<wr::WrFilterOp>& aFilters, bool aIsBackfaceVisible,
+      const nsTArray<wr::FilterOp>& aFilters, bool aIsBackfaceVisible,
       const wr::RasterSpace& aRasterSpace);
   void PopStackingContext(bool aIsReferenceFrame);
 
   wr::WrClipChainId DefineClipChain(const Maybe<wr::WrClipChainId>& aParent,
                                     const nsTArray<wr::WrClipId>& aClips);
 
   wr::WrClipId DefineClip(
       const Maybe<wr::WrSpaceAndClip>& aParent, const wr::LayoutRect& aClipRect,
--- a/gfx/webrender_bindings/WebRenderTypes.h
+++ b/gfx/webrender_bindings/WebRenderTypes.h
@@ -766,44 +766,16 @@ struct ByteBuffer {
   bool mOwned;
 };
 
 struct BuiltDisplayList {
   wr::VecU8 dl;
   wr::BuiltDisplayListDescriptor dl_desc;
 };
 
-static inline wr::WrFilterOpType ToWrFilterOpType(uint32_t type) {
-  switch (type) {
-    case NS_STYLE_FILTER_BLUR:
-      return wr::WrFilterOpType::Blur;
-    case NS_STYLE_FILTER_BRIGHTNESS:
-      return wr::WrFilterOpType::Brightness;
-    case NS_STYLE_FILTER_CONTRAST:
-      return wr::WrFilterOpType::Contrast;
-    case NS_STYLE_FILTER_GRAYSCALE:
-      return wr::WrFilterOpType::Grayscale;
-    case NS_STYLE_FILTER_HUE_ROTATE:
-      return wr::WrFilterOpType::HueRotate;
-    case NS_STYLE_FILTER_INVERT:
-      return wr::WrFilterOpType::Invert;
-    case NS_STYLE_FILTER_OPACITY:
-      return wr::WrFilterOpType::Opacity;
-    case NS_STYLE_FILTER_SATURATE:
-      return wr::WrFilterOpType::Saturate;
-    case NS_STYLE_FILTER_SEPIA:
-      return wr::WrFilterOpType::Sepia;
-    case NS_STYLE_FILTER_DROP_SHADOW:
-      return wr::WrFilterOpType::DropShadow;
-  }
-  MOZ_ASSERT_UNREACHABLE("Tried to convert unknown filter type.");
-  return wr::WrFilterOpType::Grayscale;
-}
-
-
 // Corresponds to a clip id for a clip chain in webrender. Similar to
 // WrClipId but a separate struct so we don't get them mixed up in C++.
 struct WrClipChainId {
   uint64_t id;
 
   bool operator==(const WrClipChainId& other) const { return id == other.id; }
 
   static WrClipChainId Empty() {
--- a/gfx/webrender_bindings/src/bindings.rs
+++ b/gfx/webrender_bindings/src/bindings.rs
@@ -2,17 +2,17 @@ use std::ffi::{CStr, CString};
 use std::io::Cursor;
 use std::{mem, slice, ptr, env};
 use std::path::PathBuf;
 use std::rc::Rc;
 use std::cell::RefCell;
 use std::sync::Arc;
 use std::sync::atomic::{AtomicUsize, Ordering};
 use std::ops::Range;
-use std::os::raw::{c_void, c_char, c_float};
+use std::os::raw::{c_void, c_char};
 #[cfg(target_os = "android")]
 use std::os::raw::{c_int};
 use gleam::gl;
 
 use webrender::api::*;
 use webrender::{ReadPixelsFormat, Renderer, RendererOptions, RendererStats, ThreadListener};
 use webrender::{ExternalImage, ExternalImageHandler, ExternalImageSource};
 use webrender::DebugFlags;
@@ -472,44 +472,16 @@ pub enum WrAnimationType {
 }
 
 #[repr(C)]
 pub struct WrAnimationProperty {
     effect_type: WrAnimationType,
     id: u64,
 }
 
-#[repr(u32)]
-#[derive(Copy, Clone)]
-pub enum WrFilterOpType {
-  Blur = 0,
-  Brightness = 1,
-  Contrast = 2,
-  Grayscale = 3,
-  HueRotate = 4,
-  Invert = 5,
-  Opacity = 6,
-  Saturate = 7,
-  Sepia = 8,
-  DropShadow = 9,
-  ColorMatrix = 10,
-  SrgbToLinear = 11,
-  LinearToSrgb = 12,
-}
-
-#[repr(C)]
-#[derive(Copy, Clone)]
-pub struct WrFilterOp {
-    filter_type: WrFilterOpType,
-    argument: c_float, // holds radius for DropShadow; value for other filters
-    offset: LayoutVector2D, // only used for DropShadow
-    color: ColorF, // only used for DropShadow
-    matrix: [f32;20], // only used in ColorMatrix
-}
-
 /// cbindgen:derive-eq=false
 #[repr(C)]
 #[derive(Debug)]
 pub struct WrTransformProperty {
     pub id: u64,
     pub transform: LayoutTransform,
 }
 
@@ -1907,42 +1879,26 @@ pub extern "C" fn wr_dp_push_stacking_co
                                               spatial_id: WrSpatialId,
                                               clip_node_id: *const WrClipId,
                                               animation: *const WrAnimationProperty,
                                               opacity: *const f32,
                                               transform: *const LayoutTransform,
                                               transform_style: TransformStyle,
                                               perspective: *const LayoutTransform,
                                               mix_blend_mode: MixBlendMode,
-                                              filters: *const WrFilterOp,
+                                              filters: *const FilterOp,
                                               filter_count: usize,
                                               is_backface_visible: bool,
                                               glyph_raster_space: RasterSpace,
                                               ) -> WrSpatialId {
     debug_assert!(unsafe { !is_in_render_thread() });
 
     let c_filters = make_slice(filters, filter_count);
     let mut filters : Vec<FilterOp> = c_filters.iter().map(|c_filter| {
-        match c_filter.filter_type {
-            WrFilterOpType::Blur => FilterOp::Blur(c_filter.argument),
-            WrFilterOpType::Brightness => FilterOp::Brightness(c_filter.argument),
-            WrFilterOpType::Contrast => FilterOp::Contrast(c_filter.argument),
-            WrFilterOpType::Grayscale => FilterOp::Grayscale(c_filter.argument),
-            WrFilterOpType::HueRotate => FilterOp::HueRotate(c_filter.argument),
-            WrFilterOpType::Invert => FilterOp::Invert(c_filter.argument),
-            WrFilterOpType::Opacity => FilterOp::Opacity(PropertyBinding::Value(c_filter.argument), c_filter.argument),
-            WrFilterOpType::Saturate => FilterOp::Saturate(c_filter.argument),
-            WrFilterOpType::Sepia => FilterOp::Sepia(c_filter.argument),
-            WrFilterOpType::DropShadow => FilterOp::DropShadow(c_filter.offset,
-                                                               c_filter.argument,
-                                                               c_filter.color),
-            WrFilterOpType::ColorMatrix => FilterOp::ColorMatrix(c_filter.matrix),
-            WrFilterOpType::SrgbToLinear => FilterOp::SrgbToLinear,
-            WrFilterOpType::LinearToSrgb => FilterOp::LinearToSrgb,
-        }
+                                                           *c_filter
     }).collect();
 
     let clip_node_id_ref = unsafe { clip_node_id.as_ref() };
 
     let transform_ref = unsafe { transform.as_ref() };
     let mut transform_binding = match transform_ref {
         Some(transform) => Some(PropertyBinding::Value(transform.clone())),
         None => None,
--- a/gfx/webrender_bindings/webrender_ffi_generated.h
+++ b/gfx/webrender_bindings/webrender_ffi_generated.h
@@ -239,34 +239,16 @@ enum class WrExternalImageBufferType {
 enum class WrExternalImageType : uint32_t {
   RawData,
   NativeTexture,
   Invalid,
 
   Sentinel /* this must be last for serialization purposes. */
 };
 
-enum class WrFilterOpType : uint32_t {
-  Blur = 0,
-  Brightness = 1,
-  Contrast = 2,
-  Grayscale = 3,
-  HueRotate = 4,
-  Invert = 5,
-  Opacity = 6,
-  Saturate = 7,
-  Sepia = 8,
-  DropShadow = 9,
-  ColorMatrix = 10,
-  SrgbToLinear = 11,
-  LinearToSrgb = 12,
-
-  Sentinel /* this must be last for serialization purposes. */
-};
-
 enum class YuvColorSpace : uint32_t {
   Rec601 = 0,
   Rec709 = 1,
 
   Sentinel /* this must be last for serialization purposes. */
 };
 
 template<typename T>
@@ -912,22 +894,395 @@ struct TypedTransform3D {
            m42 == aOther.m42 &&
            m43 == aOther.m43 &&
            m44 == aOther.m44;
   }
 };
 
 using LayoutTransform = TypedTransform3D<float, LayoutPixel, LayoutPixel>;
 
-struct WrFilterOp {
-  WrFilterOpType filter_type;
-  float argument;
-  LayoutVector2D offset;
-  ColorF color;
-  float matrix[20];
+struct PropertyBindingId {
+  IdNamespace namespace_;
+  uint32_t uid;
+
+  bool operator==(const PropertyBindingId& aOther) const {
+    return namespace_ == aOther.namespace_ &&
+           uid == aOther.uid;
+  }
+};
+
+/// A unique key that is used for connecting animated property
+/// values to bindings in the display list.
+template<typename T>
+struct PropertyBindingKey {
+  PropertyBindingId id;
+
+  bool operator==(const PropertyBindingKey& aOther) const {
+    return id == aOther.id;
+  }
+};
+
+/// A binding property can either be a specific value
+/// (the normal, non-animated case) or point to a binding location
+/// to fetch the current value from.
+/// Note that Binding has also a non-animated value, the value is
+/// used for the case where the animation is still in-delay phase
+/// (i.e. the animation doesn't produce any animation values).
+template<typename T>
+struct PropertyBinding {
+  enum class Tag {
+    Value,
+    Binding,
+
+    Sentinel /* this must be last for serialization purposes. */
+  };
+
+  struct Value_Body {
+    T _0;
+
+    bool operator==(const Value_Body& aOther) const {
+      return _0 == aOther._0;
+    }
+  };
+
+  struct Binding_Body {
+    PropertyBindingKey<T> _0;
+    T _1;
+
+    bool operator==(const Binding_Body& aOther) const {
+      return _0 == aOther._0 &&
+             _1 == aOther._1;
+    }
+  };
+
+  Tag tag;
+  union {
+    Value_Body value;
+    Binding_Body binding;
+  };
+
+  static PropertyBinding Value(const T &a0) {
+    PropertyBinding result;
+    result.value._0 = a0;
+    result.tag = Tag::Value;
+    return result;
+  }
+
+  static PropertyBinding Binding(const PropertyBindingKey<T> &a0,
+                                 const T &a1) {
+    PropertyBinding result;
+    result.binding._0 = a0;
+    result.binding._1 = a1;
+    result.tag = Tag::Binding;
+    return result;
+  }
+
+  bool IsValue() const {
+    return tag == Tag::Value;
+  }
+
+  bool IsBinding() const {
+    return tag == Tag::Binding;
+  }
+
+  bool operator==(const PropertyBinding& aOther) const {
+    if (tag != aOther.tag) {
+      return false;
+    }
+    switch (tag) {
+      case Tag::Value: return value == aOther.value;
+      case Tag::Binding: return binding == aOther.binding;
+      default: return true;
+    }
+  }
+};
+
+struct FilterOp {
+  enum class Tag {
+    /// Filter that does no transformation of the colors, needed for
+    /// debug purposes only.
+    Identity,
+    Blur,
+    Brightness,
+    Contrast,
+    Grayscale,
+    HueRotate,
+    Invert,
+    Opacity,
+    Saturate,
+    Sepia,
+    DropShadow,
+    ColorMatrix,
+    SrgbToLinear,
+    LinearToSrgb,
+
+    Sentinel /* this must be last for serialization purposes. */
+  };
+
+  struct Blur_Body {
+    float _0;
+
+    bool operator==(const Blur_Body& aOther) const {
+      return _0 == aOther._0;
+    }
+  };
+
+  struct Brightness_Body {
+    float _0;
+
+    bool operator==(const Brightness_Body& aOther) const {
+      return _0 == aOther._0;
+    }
+  };
+
+  struct Contrast_Body {
+    float _0;
+
+    bool operator==(const Contrast_Body& aOther) const {
+      return _0 == aOther._0;
+    }
+  };
+
+  struct Grayscale_Body {
+    float _0;
+
+    bool operator==(const Grayscale_Body& aOther) const {
+      return _0 == aOther._0;
+    }
+  };
+
+  struct HueRotate_Body {
+    float _0;
+
+    bool operator==(const HueRotate_Body& aOther) const {
+      return _0 == aOther._0;
+    }
+  };
+
+  struct Invert_Body {
+    float _0;
+
+    bool operator==(const Invert_Body& aOther) const {
+      return _0 == aOther._0;
+    }
+  };
+
+  struct Opacity_Body {
+    PropertyBinding<float> _0;
+    float _1;
+
+    bool operator==(const Opacity_Body& aOther) const {
+      return _0 == aOther._0 &&
+             _1 == aOther._1;
+    }
+  };
+
+  struct Saturate_Body {
+    float _0;
+
+    bool operator==(const Saturate_Body& aOther) const {
+      return _0 == aOther._0;
+    }
+  };
+
+  struct Sepia_Body {
+    float _0;
+
+    bool operator==(const Sepia_Body& aOther) const {
+      return _0 == aOther._0;
+    }
+  };
+
+  struct DropShadow_Body {
+    LayoutVector2D _0;
+    float _1;
+    ColorF _2;
+
+    bool operator==(const DropShadow_Body& aOther) const {
+      return _0 == aOther._0 &&
+             _1 == aOther._1 &&
+             _2 == aOther._2;
+    }
+  };
+
+  struct ColorMatrix_Body {
+    float _0[20];
+  };
+
+  Tag tag;
+  union {
+    Blur_Body blur;
+    Brightness_Body brightness;
+    Contrast_Body contrast;
+    Grayscale_Body grayscale;
+    HueRotate_Body hue_rotate;
+    Invert_Body invert;
+    Opacity_Body opacity;
+    Saturate_Body saturate;
+    Sepia_Body sepia;
+    DropShadow_Body drop_shadow;
+    ColorMatrix_Body color_matrix;
+  };
+
+  static FilterOp Identity() {
+    FilterOp result;
+    result.tag = Tag::Identity;
+    return result;
+  }
+
+  static FilterOp Blur(const float &a0) {
+    FilterOp result;
+    result.blur._0 = a0;
+    result.tag = Tag::Blur;
+    return result;
+  }
+
+  static FilterOp Brightness(const float &a0) {
+    FilterOp result;
+    result.brightness._0 = a0;
+    result.tag = Tag::Brightness;
+    return result;
+  }
+
+  static FilterOp Contrast(const float &a0) {
+    FilterOp result;
+    result.contrast._0 = a0;
+    result.tag = Tag::Contrast;
+    return result;
+  }
+
+  static FilterOp Grayscale(const float &a0) {
+    FilterOp result;
+    result.grayscale._0 = a0;
+    result.tag = Tag::Grayscale;
+    return result;
+  }
+
+  static FilterOp HueRotate(const float &a0) {
+    FilterOp result;
+    result.hue_rotate._0 = a0;
+    result.tag = Tag::HueRotate;
+    return result;
+  }
+
+  static FilterOp Invert(const float &a0) {
+    FilterOp result;
+    result.invert._0 = a0;
+    result.tag = Tag::Invert;
+    return result;
+  }
+
+  static FilterOp Opacity(const PropertyBinding<float> &a0,
+                          const float &a1) {
+    FilterOp result;
+    result.opacity._0 = a0;
+    result.opacity._1 = a1;
+    result.tag = Tag::Opacity;
+    return result;
+  }
+
+  static FilterOp Saturate(const float &a0) {
+    FilterOp result;
+    result.saturate._0 = a0;
+    result.tag = Tag::Saturate;
+    return result;
+  }
+
+  static FilterOp Sepia(const float &a0) {
+    FilterOp result;
+    result.sepia._0 = a0;
+    result.tag = Tag::Sepia;
+    return result;
+  }
+
+  static FilterOp DropShadow(const LayoutVector2D &a0,
+                             const float &a1,
+                             const ColorF &a2) {
+    FilterOp result;
+    result.drop_shadow._0 = a0;
+    result.drop_shadow._1 = a1;
+    result.drop_shadow._2 = a2;
+    result.tag = Tag::DropShadow;
+    return result;
+  }
+
+  static FilterOp ColorMatrix(const float (&a0)[20]) {
+    FilterOp result;
+    for (int i = 0; i < 20; i++) {result.color_matrix._0[i] = a0[i];}
+    result.tag = Tag::ColorMatrix;
+    return result;
+  }
+
+  static FilterOp SrgbToLinear() {
+    FilterOp result;
+    result.tag = Tag::SrgbToLinear;
+    return result;
+  }
+
+  static FilterOp LinearToSrgb() {
+    FilterOp result;
+    result.tag = Tag::LinearToSrgb;
+    return result;
+  }
+
+  bool IsIdentity() const {
+    return tag == Tag::Identity;
+  }
+
+  bool IsBlur() const {
+    return tag == Tag::Blur;
+  }
+
+  bool IsBrightness() const {
+    return tag == Tag::Brightness;
+  }
+
+  bool IsContrast() const {
+    return tag == Tag::Contrast;
+  }
+
+  bool IsGrayscale() const {
+    return tag == Tag::Grayscale;
+  }
+
+  bool IsHueRotate() const {
+    return tag == Tag::HueRotate;
+  }
+
+  bool IsInvert() const {
+    return tag == Tag::Invert;
+  }
+
+  bool IsOpacity() const {
+    return tag == Tag::Opacity;
+  }
+
+  bool IsSaturate() const {
+    return tag == Tag::Saturate;
+  }
+
+  bool IsSepia() const {
+    return tag == Tag::Sepia;
+  }
+
+  bool IsDropShadow() const {
+    return tag == Tag::DropShadow;
+  }
+
+  bool IsColorMatrix() const {
+    return tag == Tag::ColorMatrix;
+  }
+
+  bool IsSrgbToLinear() const {
+    return tag == Tag::SrgbToLinear;
+  }
+
+  bool IsLinearToSrgb() const {
+    return tag == Tag::LinearToSrgb;
+  }
 };
 
 /// Configure whether the contents of a stacking context
 /// should be rasterized in local space or screen space.
 /// Local space rasterized pictures are typically used
 /// when we want to cache the output, and performance is
 /// important. Note that this is a performance hint only,
 /// which WR may choose to ignore.
@@ -1584,17 +1939,17 @@ WrSpatialId wr_dp_push_stacking_context(
                                         WrSpatialId aSpatialId,
                                         const WrClipId *aClipNodeId,
                                         const WrAnimationProperty *aAnimation,
                                         const float *aOpacity,
                                         const LayoutTransform *aTransform,
                                         TransformStyle aTransformStyle,
                                         const LayoutTransform *aPerspective,
                                         MixBlendMode aMixBlendMode,
-                                        const WrFilterOp *aFilters,
+                                        const FilterOp *aFilters,
                                         uintptr_t aFilterCount,
                                         bool aIsBackfaceVisible,
                                         RasterSpace aGlyphRasterSpace)
 WR_FUNC;
 
 WR_INLINE
 void wr_dp_push_text(WrState *aState,
                      LayoutRect aBounds,
--- a/gfx/wr/webrender/src/capture.rs
+++ b/gfx/wr/webrender/src/capture.rs
@@ -5,16 +5,18 @@
 use std::fs::File;
 use std::path::{Path, PathBuf};
 
 use api::{CaptureBits, ExternalImageData, ImageDescriptor, TexelRect};
 #[cfg(feature = "png")]
 use device::ReadPixelsFormat;
 #[cfg(feature = "png")]
 use api::DeviceIntSize;
+#[cfg(feature = "capture")]
+use print_tree::{PrintableTree, PrintTree};
 use ron;
 use serde;
 
 
 #[derive(Clone)]
 pub struct CaptureConfig {
     pub root: PathBuf,
     pub bits: CaptureBits,
@@ -50,16 +52,31 @@ impl CaptureConfig {
             .join(name)
             .with_extension("ron");
         let mut file = File::create(path)
             .unwrap();
         write!(file, "{}\n", ron)
             .unwrap();
     }
 
+    #[cfg(feature = "capture")]
+    pub fn serialize_tree<T, P>(&self, data: &T, name: P)
+    where
+        T: PrintableTree,
+        P: AsRef<Path>
+    {
+        let path = self.root
+            .join(name)
+            .with_extension("tree");
+        let file = File::create(path)
+            .unwrap();
+        let mut pt = PrintTree::new_with_sink("", file);
+        data.print_with(&mut pt);
+    }
+
     #[cfg(feature = "replay")]
     pub fn deserialize<T, P>(root: &PathBuf, name: P) -> Option<T>
     where
         T: for<'a> serde::Deserialize<'a>,
         P: AsRef<Path>,
     {
         use std::io::Read;
 
--- a/gfx/wr/webrender/src/clip_scroll_tree.rs
+++ b/gfx/wr/webrender/src/clip_scroll_tree.rs
@@ -2,17 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use api::{ExternalScrollId, LayoutPoint, LayoutRect, LayoutVector2D};
 use api::{PipelineId, ScrollClamping, ScrollNodeState, ScrollLocation, ScrollSensitivity};
 use api::{LayoutSize, LayoutTransform, PropertyBinding, TransformStyle, WorldPoint};
 use gpu_types::TransformPalette;
 use internal_types::{FastHashMap, FastHashSet};
-use print_tree::{PrintTree, PrintTreePrinter};
+use print_tree::{PrintableTree, PrintTree, PrintTreePrinter};
 use scene::SceneProperties;
 use smallvec::SmallVec;
 use spatial_node::{ScrollFrameInfo, SpatialNode, SpatialNodeType, StickyFrameInfo, ScrollFrameKind};
 use util::{LayoutToWorldFastTransform, ScaleOffset};
 
 pub type ScrollStates = FastHashMap<ExternalScrollId, ScrollFrameInfo>;
 
 /// An id that identifies coordinate systems in the ClipScrollTree. Each
@@ -437,22 +437,16 @@ impl ClipScrollTree {
     #[allow(dead_code)]
     pub fn print(&self) {
         if !self.spatial_nodes.is_empty() {
             let mut pt = PrintTree::new("clip_scroll tree");
             self.print_with(&mut pt);
         }
     }
 
-    pub fn print_with<T: PrintTreePrinter>(&self, pt: &mut T) {
-        if !self.spatial_nodes.is_empty() {
-            self.print_node(self.root_reference_frame_index(), pt);
-        }
-    }
-
     /// Return true if this is a guaranteed identity transform. This
     /// is conservative, it assumes not identity if a property
     /// binding animation, or scroll frame is found, for example.
     pub fn node_is_identity(
         &self,
         spatial_node_index: SpatialNodeIndex,
     ) -> bool {
         let mut current = spatial_node_index;
@@ -491,16 +485,24 @@ impl ClipScrollTree {
             }
             current = node.parent.unwrap();
         }
 
         true
     }
 }
 
+impl PrintableTree for ClipScrollTree {
+    fn print_with<T: PrintTreePrinter>(&self, pt: &mut T) {
+        if !self.spatial_nodes.is_empty() {
+            self.print_node(self.root_reference_frame_index(), pt);
+        }
+    }
+}
+
 #[cfg(test)]
 fn add_reference_frame(
     cst: &mut ClipScrollTree,
     parent: Option<SpatialNodeIndex>,
     transform: LayoutTransform,
     origin_in_parent_reference_frame: LayoutVector2D,
 ) -> SpatialNodeIndex {
     cst.add_reference_frame(
--- a/gfx/wr/webrender/src/frame_builder.rs
+++ b/gfx/wr/webrender/src/frame_builder.rs
@@ -387,17 +387,17 @@ impl FrameBuilder {
         );
 
         let mut profile_counters = FrameProfileCounters::new();
         profile_counters
             .total_primitives
             .set(self.prim_store.prim_count());
 
         resource_cache.begin_frame(stamp);
-        gpu_cache.begin_frame(stamp.frame_id());
+        gpu_cache.begin_frame(stamp);
 
         let mut transform_palette = TransformPalette::new();
         clip_scroll_tree.update_tree(
             pan,
             scene_properties,
             Some(&mut transform_palette),
         );
         self.clip_store.clear_old_instances();
@@ -493,21 +493,21 @@ impl FrameBuilder {
                 }
                 RenderPassKind::OffScreen { ref texture_cache, ref color, .. } => {
                     has_texture_cache_tasks |= !texture_cache.is_empty();
                     has_texture_cache_tasks |= color.must_be_drawn();
                 }
             }
         }
 
-        let gpu_cache_frame_id = gpu_cache.end_frame(gpu_cache_profile);
+        let gpu_cache_frame_id = gpu_cache.end_frame(gpu_cache_profile).frame_id();
 
         render_tasks.write_task_data(device_pixel_scale);
 
-        resource_cache.end_frame();
+        resource_cache.end_frame(texture_cache_profile);
 
         Frame {
             window_size: self.window_size,
             inner_rect: self.screen_rect,
             device_pixel_ratio: device_pixel_scale.0,
             background_color: self.background_color,
             layer,
             profile_counters,
--- a/gfx/wr/webrender/src/glyph_rasterizer/mod.rs
+++ b/gfx/wr/webrender/src/glyph_rasterizer/mod.rs
@@ -726,17 +726,17 @@ mod test_glyph_rasterizer {
             .thread_name(|idx|{ format!("WRWorker#{}", idx) })
             .start_handler(move |idx| {
                 register_thread_with_profiler(format!("WRWorker#{}", idx));
             })
             .build();
         let workers = Arc::new(worker.unwrap());
         let mut glyph_rasterizer = GlyphRasterizer::new(workers).unwrap();
         let mut glyph_cache = GlyphCache::new();
-        let mut gpu_cache = GpuCache::new();
+        let mut gpu_cache = GpuCache::new_for_testing();
         let mut texture_cache = TextureCache::new_for_testing(2048, 1024);
         let mut render_task_cache = RenderTaskCache::new();
         let mut render_task_tree = RenderTaskTree::new(FrameId::INVALID);
         let mut font_file =
             File::open("../wrench/reftests/text/VeraBd.ttf").expect("Couldn't open font file");
         let mut font_data = vec![];
         font_file
             .read_to_end(&mut font_data)
--- a/gfx/wr/webrender/src/gpu_cache.rs
+++ b/gfx/wr/webrender/src/gpu_cache.rs
@@ -19,21 +19,22 @@
 //! data is not in the cache, the user provided closure
 //! will be invoked to build the data.
 //!
 //! After ```end_frame``` has occurred, callers can
 //! use the ```get_address``` API to get the allocated
 //! address in the GPU cache of a given resource slot
 //! for this frame.
 
-use api::{DebugFlags, PremultipliedColorF, TexelRect};
+use api::{DebugFlags, DocumentId, PremultipliedColorF, IdNamespace, TexelRect};
 use api::{VoidPtrToSizeFn};
 use euclid::TypedRect;
+use internal_types::{FastHashMap};
 use profiler::GpuCacheProfileCounters;
-use render_backend::FrameId;
+use render_backend::{FrameStamp, FrameId};
 use renderer::MAX_VERTEX_TEXTURE_WIDTH;
 use std::{mem, u16, u32};
 use std::num::NonZeroU32;
 use std::ops::Add;
 use std::os::raw::c_void;
 use std::time::{Duration, Instant};
 
 
@@ -406,17 +407,17 @@ struct Texture {
     max_epoch: Epoch,
     // Free lists of available blocks for each supported
     // block size in the texture. These are intrusive
     // linked lists.
     free_lists: FreeBlockLists,
     // Linked list of currently occupied blocks. This
     // makes it faster to iterate blocks looking for
     // candidates to be evicted from the cache.
-    occupied_list_head: Option<BlockIndex>,
+    occupied_list_heads: FastHashMap<DocumentId, BlockIndex>,
     // Pending blocks that have been written this frame
     // and will need to be sent to the GPU.
     pending_blocks: Vec<GpuBlockData>,
     // Pending update commands.
     updates: Vec<GpuCacheUpdate>,
     // Profile stats
     allocated_block_count: usize,
     // The stamp at which we first reached our threshold for reclaiming `GpuCache`
@@ -442,17 +443,17 @@ impl Texture {
             height: GPU_CACHE_INITIAL_HEIGHT,
             blocks,
             rows: Vec::new(),
             base_epoch,
             max_epoch: base_epoch,
             free_lists: FreeBlockLists::new(),
             pending_blocks: Vec::new(),
             updates: Vec::new(),
-            occupied_list_head: None,
+            occupied_list_heads: FastHashMap::default(),
             allocated_block_count: 0,
             reached_reclaim_threshold: None,
             debug_commands: Vec::new(),
             debug_flags,
         }
     }
 
     // Reports the CPU heap usage of this Texture struct.
@@ -469,18 +470,19 @@ impl Texture {
 
     // Push new data into the cache. The ```pending_block_index``` field represents
     // where the data was pushed into the texture ```pending_blocks``` array.
     // Return the allocated address for this data.
     fn push_data(
         &mut self,
         pending_block_index: Option<usize>,
         block_count: usize,
-        frame_id: FrameId,
+        frame_stamp: FrameStamp
     ) -> CacheLocation {
+        debug_assert!(frame_stamp.is_valid());
         // Find the appropriate free list to use based on the block size.
         let (alloc_size, free_list) = self.free_lists
             .get_actual_block_count_and_free_list(block_count);
 
         // See if we need a new row (if free-list has nothing available)
         if free_list.is_none() {
             if self.rows.len() as i32 == self.height {
                 self.height += NEW_ROWS_PER_RESIZE;
@@ -493,35 +495,35 @@ impl Texture {
 
             // Create a ```Block``` for each possible allocation address
             // in this row, and link it in to the free-list for this
             // block size.
             let mut prev_block_index = None;
             for i in 0 .. items_per_row {
                 let address = GpuCacheAddress::new(i * alloc_size, row_index);
                 let block_index = BlockIndex::new(self.blocks.len());
-                let block = Block::new(address, prev_block_index, frame_id, self.base_epoch);
+                let block = Block::new(address, prev_block_index, frame_stamp.frame_id(), self.base_epoch);
                 self.blocks.push(block);
                 prev_block_index = Some(block_index);
             }
 
             *free_list = prev_block_index;
         }
 
         // Given the code above, it's now guaranteed that there is a block
         // available in the appropriate free-list. Pull a block from the
         // head of the list.
         let free_block_index = free_list.take().unwrap();
         let block = &mut self.blocks[free_block_index.get()];
         *free_list = block.next;
 
         // Add the block to the occupied linked list.
-        block.next = self.occupied_list_head;
-        block.last_access_time = frame_id;
-        self.occupied_list_head = Some(free_block_index);
+        block.next = self.occupied_list_heads.get(&frame_stamp.document_id()).cloned();
+        block.last_access_time = frame_stamp.frame_id();
+        self.occupied_list_heads.insert(frame_stamp.document_id(), free_block_index);
         self.allocated_block_count += alloc_size;
 
         if let Some(pending_block_index) = pending_block_index {
             // Add this update to the pending list of blocks that need
             // to be updated on the GPU.
             self.updates.push(GpuCacheUpdate::Copy {
                 block_index: pending_block_index,
                 block_count,
@@ -544,34 +546,35 @@ impl Texture {
         CacheLocation {
             block_index: free_block_index,
             epoch: block.epoch,
         }
     }
 
     // Run through the list of occupied cache blocks and evict
     // any old blocks that haven't been referenced for a while.
-    fn evict_old_blocks(&mut self, frame_id: FrameId) {
+    fn evict_old_blocks(&mut self, frame_stamp: FrameStamp) {
+        debug_assert!(frame_stamp.is_valid());
         // Prune any old items from the list to make room.
         // Traverse the occupied linked list and see
         // which items have not been used for a long time.
-        let mut current_block = self.occupied_list_head;
+        let mut current_block = self.occupied_list_heads.get(&frame_stamp.document_id()).map(|x| *x);
         let mut prev_block: Option<BlockIndex> = None;
 
         while let Some(index) = current_block {
             let (next_block, should_unlink) = {
                 let block = &mut self.blocks[index.get()];
 
                 let next_block = block.next;
                 let mut should_unlink = false;
 
                 // If this resource has not been used in the last
                 // few frames, free it from the texture and mark
                 // as empty.
-                if block.last_access_time + FRAMES_BEFORE_EVICTION < frame_id {
+                if block.last_access_time + FRAMES_BEFORE_EVICTION < frame_stamp.frame_id() {
                     should_unlink = true;
 
                     // Get the row metadata from the address.
                     let row = &mut self.rows[block.address.v as usize];
 
                     // Use the row metadata to determine which free-list
                     // this block belongs to.
                     let (_, free_list) = self.free_lists
@@ -595,17 +598,24 @@ impl Texture {
             // If the block was released, we will need to remove it
             // from the occupied linked list.
             if should_unlink {
                 match prev_block {
                     Some(prev_block) => {
                         self.blocks[prev_block.get()].next = next_block;
                     }
                     None => {
-                        self.occupied_list_head = next_block;
+                        match next_block {
+                            Some(next_block) => {
+                                self.occupied_list_heads.insert(frame_stamp.document_id(), next_block);
+                            }
+                            None => {
+                                self.occupied_list_heads.remove(&frame_stamp.document_id());
+                            }
+                        }
                     }
                 }
             } else {
                 prev_block = current_block;
             }
 
             current_block = next_block;
         }
@@ -622,17 +632,17 @@ impl Texture {
 }
 
 
 /// A wrapper object for GPU data requests,
 /// works as a container that can only grow.
 #[must_use]
 pub struct GpuDataRequest<'a> {
     handle: &'a mut GpuCacheHandle,
-    frame_id: FrameId,
+    frame_stamp: FrameStamp,
     start_index: usize,
     max_block_count: usize,
     texture: &'a mut Texture,
 }
 
 impl<'a> GpuDataRequest<'a> {
     pub fn push<B>(&mut self, block: B)
     where
@@ -648,68 +658,80 @@ impl<'a> GpuDataRequest<'a> {
 
 impl<'a> Drop for GpuDataRequest<'a> {
     fn drop(&mut self) {
         // Push the data to the texture pending updates list.
         let block_count = self.current_used_block_num();
         debug_assert!(block_count <= self.max_block_count);
 
         let location = self.texture
-            .push_data(Some(self.start_index), block_count, self.frame_id);
+            .push_data(Some(self.start_index), block_count, self.frame_stamp);
         self.handle.location = Some(location);
     }
 }
 
 
 /// The main LRU cache interface.
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub struct GpuCache {
-    /// Current frame ID.
-    frame_id: FrameId,
+    /// Current FrameId.
+    now: FrameStamp,
     /// CPU-side texture allocator.
     texture: Texture,
     /// Number of blocks requested this frame that don't
     /// need to be re-uploaded.
     saved_block_count: usize,
     /// The current debug flags for the system.
     debug_flags: DebugFlags,
     /// Whether there is a pending clear to send with the
     /// next update.
     pending_clear: bool,
 }
 
 impl GpuCache {
     pub fn new() -> Self {
         let debug_flags = DebugFlags::empty();
         GpuCache {
-            frame_id: FrameId::INVALID,
+            now: FrameStamp::INVALID,
             texture: Texture::new(Epoch(0), debug_flags),
             saved_block_count: 0,
             debug_flags,
             pending_clear: false,
         }
     }
 
+    /// Creates a GpuCache and sets it up with a valid `FrameStamp`, which
+    /// is useful for avoiding panics when instantiating the `GpuCache`
+    /// directly from unit test code.
+    #[allow(dead_code)]
+    pub fn new_for_testing() -> Self {
+        let mut cache = Self::new();
+        let mut now = FrameStamp::first(DocumentId(IdNamespace(1), 1));
+        now.advance();
+        cache.begin_frame(now);
+        cache
+    }
+
     /// Drops everything in the GPU cache. Must not be called once gpu cache entries
     /// for the next frame have already been requested.
     pub fn clear(&mut self) {
         assert!(self.texture.updates.is_empty(), "Clearing with pending updates");
         let mut next_base_epoch = self.texture.max_epoch;
         next_base_epoch.next();
         self.texture = Texture::new(next_base_epoch, self.debug_flags);
         self.saved_block_count = 0;
         self.pending_clear = true;
     }
 
     /// Begin a new frame.
-    pub fn begin_frame(&mut self, frame_id: FrameId) {
+    pub fn begin_frame(&mut self, stamp: FrameStamp) {
         debug_assert!(self.texture.pending_blocks.is_empty());
-        self.frame_id = frame_id;
-        self.texture.evict_old_blocks(self.frame_id);
+        self.now = stamp;
+        self.texture.evict_old_blocks(self.now);
         self.saved_block_count = 0;
     }
 
     // Invalidate a (possibly) existing block in the cache.
     // This means the next call to request() for this location
     // will rebuild the data and upload it to the GPU.
     pub fn invalidate(&mut self, handle: &GpuCacheHandle) {
         if let Some(ref location) = handle.location {
@@ -726,67 +748,68 @@ impl GpuCache {
     /// is already in the cache, `None` will be returned.
     pub fn request<'a>(&'a mut self, handle: &'a mut GpuCacheHandle) -> Option<GpuDataRequest<'a>> {
         let mut max_block_count = MAX_VERTEX_TEXTURE_WIDTH;
         // Check if the allocation for this handle is still valid.
         if let Some(ref location) = handle.location {
             if let Some(block) = self.texture.blocks.get_mut(location.block_index.get()) {
                 if block.epoch == location.epoch {
                     max_block_count = self.texture.rows[block.address.v as usize].block_count_per_item;
-                    if block.last_access_time != self.frame_id {
+                    if block.last_access_time != self.now.frame_id() {
                         // Mark last access time to avoid evicting this block.
-                        block.last_access_time = self.frame_id;
+                        block.last_access_time = self.now.frame_id();
                         self.saved_block_count += max_block_count;
                     }
                     return None;
                 }
             }
         }
 
+        debug_assert!(self.now.is_valid());
         Some(GpuDataRequest {
             handle,
-            frame_id: self.frame_id,
+            frame_stamp: self.now,
             start_index: self.texture.pending_blocks.len(),
             texture: &mut self.texture,
             max_block_count,
         })
     }
 
     // Push an array of data blocks to be uploaded to the GPU
     // unconditionally for this frame. The cache handle will
     // assert if the caller tries to retrieve the address
     // of this handle on a subsequent frame. This is typically
     // used for uploading data that changes every frame, and
     // therefore makes no sense to try and cache.
     pub fn push_per_frame_blocks(&mut self, blocks: &[GpuBlockData]) -> GpuCacheHandle {
         let start_index = self.texture.pending_blocks.len();
         self.texture.pending_blocks.extend_from_slice(blocks);
         let location = self.texture
-            .push_data(Some(start_index), blocks.len(), self.frame_id);
+            .push_data(Some(start_index), blocks.len(), self.now);
         GpuCacheHandle {
             location: Some(location),
         }
     }
 
     // Reserve space in the cache for per-frame blocks that
     // will be resolved by the render thread via the
     // external image callback.
     pub fn push_deferred_per_frame_blocks(&mut self, block_count: usize) -> GpuCacheHandle {
-        let location = self.texture.push_data(None, block_count, self.frame_id);
+        let location = self.texture.push_data(None, block_count, self.now);
         GpuCacheHandle {
             location: Some(location),
         }
     }
 
     /// End the frame. Return the list of updates to apply to the
     /// device specific cache texture.
     pub fn end_frame(
         &mut self,
         profile_counters: &mut GpuCacheProfileCounters,
-    ) -> FrameId {
+    ) -> FrameStamp {
         profile_counters
             .allocated_rows
             .set(self.texture.rows.len());
         profile_counters
             .allocated_blocks
             .set(self.texture.allocated_block_count);
         profile_counters
             .saved_blocks
@@ -796,32 +819,32 @@ impl GpuCache {
             self.texture.rows.len() > (GPU_CACHE_INITIAL_HEIGHT as usize) &&
             self.texture.utilization() < RECLAIM_THRESHOLD;
         if reached_threshold {
             self.texture.reached_reclaim_threshold.get_or_insert_with(Instant::now);
         } else {
             self.texture.reached_reclaim_threshold = None;
         }
 
-        self.frame_id
+        self.now
     }
 
     /// Returns true if utilization has been low enough for long enough that we
     /// should blow the cache away and rebuild it.
     pub fn should_reclaim_memory(&self) -> bool {
         self.texture.reached_reclaim_threshold
             .map_or(false, |t| t.elapsed() > Duration::from_secs(RECLAIM_DELAY_S))
     }
 
     /// Extract the pending updates from the cache.
     pub fn extract_updates(&mut self) -> GpuCacheUpdateList {
         let clear = self.pending_clear;
         self.pending_clear = false;
         GpuCacheUpdateList {
-            frame_id: self.frame_id,
+            frame_id: self.now.frame_id(),
             clear,
             height: self.texture.height,
             debug_commands: mem::replace(&mut self.texture.debug_commands, Vec::new()),
             updates: mem::replace(&mut self.texture.updates, Vec::new()),
             blocks: mem::replace(&mut self.texture.pending_blocks, Vec::new()),
         }
     }
 
@@ -834,17 +857,17 @@ impl GpuCache {
     /// Get the actual GPU address in the texture for a given slot ID.
     /// It's assumed at this point that the given slot has been requested
     /// and built for this frame. Attempting to get the address for a
     /// freed or pending slot will panic!
     pub fn get_address(&self, id: &GpuCacheHandle) -> GpuCacheAddress {
         let location = id.location.expect("handle not requested or allocated!");
         let block = &self.texture.blocks[location.block_index.get()];
         debug_assert_eq!(block.epoch, location.epoch);
-        debug_assert_eq!(block.last_access_time, self.frame_id);
+        debug_assert_eq!(block.last_access_time, self.now.frame_id());
         block.address
     }
 
     /// Reports the CPU heap usage of this GpuCache struct.
     pub fn malloc_size_of(&self, op: VoidPtrToSizeFn) -> usize {
         self.texture.malloc_size_of(op)
     }
 }
--- a/gfx/wr/webrender/src/print_tree.rs
+++ b/gfx/wr/webrender/src/print_tree.rs
@@ -1,63 +1,84 @@
 /* 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 std::io::{stdout, Stdout, Write};
+
 /// A struct that makes it easier to print out a pretty tree of data, which
 /// can be visually scanned more easily.
-pub struct PrintTree {
+pub struct PrintTree<W>
+where
+    W: Write
+{
     /// The current level of recursion.
     level: u32,
 
     /// An item which is queued up, so that we can determine if we need
     /// a mid-tree prefix or a branch ending prefix.
     queued_item: Option<String>,
+
+    /// The sink to print to.
+    sink: W,
 }
 
 /// A trait that makes it easy to describe a pretty tree of data,
 /// regardless of the printing destination, to either print it
 /// directly to stdout, or serialize it as in the debugger
 pub trait PrintTreePrinter {
     fn new_level(&mut self, title: String);
     fn end_level(&mut self);
     fn add_item(&mut self, text: String);
 }
 
-impl PrintTree {
-    pub fn new(title: &str) -> PrintTree {
-        println!("\u{250c} {}", title);
+impl PrintTree<Stdout> {
+    pub fn new(title: &str) -> Self {
+        PrintTree::new_with_sink(title, stdout())
+    }
+}
+
+impl<W> PrintTree<W>
+where
+    W: Write
+{
+    pub fn new_with_sink(title: &str, mut sink: W) -> Self {
+        writeln!(sink, "\u{250c} {}", title).unwrap();
         PrintTree {
             level: 1,
             queued_item: None,
+            sink,
         }
     }
 
-    fn print_level_prefix(&self) {
+    fn print_level_prefix(&mut self) {
         for _ in 0 .. self.level {
-            print!("\u{2502}  ");
+            write!(self.sink, "\u{2502}  ").unwrap();
         }
     }
 
     fn flush_queued_item(&mut self, prefix: &str) {
         if let Some(queued_item) = self.queued_item.take() {
             self.print_level_prefix();
-            println!("{} {}", prefix, queued_item);
+            writeln!(self.sink, "{} {}", prefix, queued_item).unwrap();
         }
     }
 }
 
 // The default `println!` based printer
-impl PrintTreePrinter for PrintTree {
+impl<W> PrintTreePrinter for PrintTree<W>
+where
+    W: Write
+{
     /// Descend one level in the tree with the given title.
     fn new_level(&mut self, title: String) {
         self.flush_queued_item("\u{251C}\u{2500}");
 
         self.print_level_prefix();
-        println!("\u{251C}\u{2500} {}", title);
+        writeln!(self.sink, "\u{251C}\u{2500} {}", title).unwrap();
 
         self.level = self.level + 1;
     }
 
     /// Ascend one level in the tree.
     fn end_level(&mut self) {
         self.flush_queued_item("\u{2514}\u{2500}");
         self.level = self.level - 1;
@@ -65,13 +86,20 @@ impl PrintTreePrinter for PrintTree {
 
     /// Add an item to the current level in the tree.
     fn add_item(&mut self, text: String) {
         self.flush_queued_item("\u{251C}\u{2500}");
         self.queued_item = Some(text);
     }
 }
 
-impl Drop for PrintTree {
+impl<W> Drop for PrintTree<W>
+where
+    W: Write
+{
     fn drop(&mut self) {
         self.flush_queued_item("\u{9492}\u{9472}");
     }
 }
+
+pub trait PrintableTree {
+    fn print_with<T: PrintTreePrinter>(&self, pt: &mut T);
+}
--- a/gfx/wr/webrender/src/render_backend.rs
+++ b/gfx/wr/webrender/src/render_backend.rs
@@ -143,22 +143,25 @@ impl ::std::ops::Sub<usize> for FrameId 
 /// we should never have two `FrameStamps` with the same id but different
 /// timestamps.
 #[derive(Copy, Clone, Debug)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub struct FrameStamp {
     id: FrameId,
     time: SystemTime,
+    document_id: DocumentId,
 }
 
 impl Eq for FrameStamp {}
 
 impl PartialEq for FrameStamp {
     fn eq(&self, other: &Self) -> bool {
+        // We should not be checking equality unless the documents are the same
+        debug_assert!(self.document_id == other.document_id);
         self.id == other.id
     }
 }
 
 impl PartialOrd for FrameStamp {
     fn partial_cmp(&self, other: &Self) -> Option<::std::cmp::Ordering> {
         self.id.partial_cmp(&other.id)
     }
@@ -170,34 +173,48 @@ impl FrameStamp {
         self.id
     }
 
     /// Gets the time associated with this FrameStamp.
     pub fn time(&self) -> SystemTime {
         self.time
     }
 
+    /// Gets the DocumentId in this stamp.
+    pub fn document_id(&self) -> DocumentId {
+        self.document_id
+    }
+
+    pub fn is_valid(&self) -> bool {
+        // If any fields are their default values, the whole struct should equal INVALID
+        debug_assert!((self.time != UNIX_EPOCH && self.id != FrameId(0) && self.document_id != DocumentId::INVALID) ||
+                      *self == Self::INVALID);
+        self.document_id != DocumentId::INVALID
+    }
+
     /// Returns a FrameStamp corresponding to the first frame.
-    pub fn first() -> Self {
+    pub fn first(document_id: DocumentId) -> Self {
         FrameStamp {
             id: FrameId::first(),
             time: SystemTime::now(),
+            document_id: document_id,
         }
     }
 
     /// Advances to a new frame.
     pub fn advance(&mut self) {
         self.id.advance();
         self.time = SystemTime::now();
     }
 
     /// An invalid sentinel FrameStamp.
     pub const INVALID: FrameStamp = FrameStamp {
         id: FrameId(0),
         time: UNIX_EPOCH,
+        document_id: DocumentId::INVALID,
     };
 }
 
 // A collection of resources that are shared by clips, primitives
 // between display lists.
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 #[derive(Default)]
@@ -327,16 +344,17 @@ struct Document {
     /// Contains various vecs of data that is used only during frame building,
     /// where we want to recycle the memory each new display list, to avoid constantly
     /// re-allocating and moving memory around.
     scratch: PrimitiveScratchBuffer,
 }
 
 impl Document {
     pub fn new(
+        id: DocumentId,
         window_size: DeviceIntSize,
         layer: DocumentLayer,
         default_device_pixel_ratio: f32,
     ) -> Self {
         Document {
             scene: Scene::new(),
             removed_pipelines: Vec::new(),
             view: DocumentView {
@@ -344,17 +362,17 @@ impl Document {
                 inner_rect: DeviceIntRect::new(DeviceIntPoint::zero(), window_size),
                 layer,
                 pan: DeviceIntPoint::zero(),
                 page_zoom_factor: 1.0,
                 pinch_zoom_factor: 1.0,
                 device_pixel_ratio: default_device_pixel_ratio,
             },
             clip_scroll_tree: ClipScrollTree::new(),
-            stamp: FrameStamp::first(),
+            stamp: FrameStamp::first(id),
             frame_builder: None,
             output_pipelines: FastHashSet::default(),
             hit_tester: None,
             dynamic_properties: SceneProperties::new(),
             frame_is_valid: false,
             hit_tester_is_valid: false,
             rendered_frame_is_valid: false,
             has_built_scene: false,
@@ -977,16 +995,17 @@ impl RenderBackend {
                 sender.send(self.next_namespace_id()).unwrap();
             }
             ApiMsg::CloneApiByClient(namespace_id) => {
                 assert!(self.namespace_alloc_by_client);
                 debug_assert!(!self.documents.iter().any(|(did, _doc)| did.0 == namespace_id));
             }
             ApiMsg::AddDocument(document_id, initial_size, layer) => {
                 let document = Document::new(
+                    document_id,
                     initial_size,
                     layer,
                     self.default_device_pixel_ratio,
                 );
                 self.documents.insert(document_id, document);
             }
             ApiMsg::DeleteDocument(document_id) => {
                 self.documents.remove(&document_id);
@@ -1512,16 +1531,18 @@ impl RenderBackend {
 
     #[cfg(not(feature = "debugger"))]
     fn get_clip_scroll_tree_for_debugger(&self) -> String {
         String::new()
     }
 
     #[cfg(feature = "debugger")]
     fn get_clip_scroll_tree_for_debugger(&self) -> String {
+        use print_tree::PrintableTree;
+
         let mut debug_root = debug_server::ClipScrollTreeList::new();
 
         for (_, doc) in &self.documents {
             let debug_node = debug_server::TreeNode::new("document clip-scroll tree");
             let mut builder = debug_server::TreeNodeBuilder::new(debug_node);
 
             doc.clip_scroll_tree.print_with(&mut builder);
 
@@ -1642,16 +1663,18 @@ impl RenderBackend {
                     &mut profile_counters.resources,
                     self.debug_flags,
                 );
                 //TODO: write down doc's pipeline info?
                 // it has `pipeline_epoch_map`,
                 // which may capture necessary details for some cases.
                 let file_name = format!("frame-{}-{}", (id.0).0, id.1);
                 config.serialize(&rendered_document.frame, file_name);
+                let file_name = format!("clip-scroll-{}-{}", (id.0).0, id.1);
+                config.serialize_tree(&doc.clip_scroll_tree, file_name);
             }
 
             let frame_resources_name = format!("frame-resources-{}-{}", (id.0).0, id.1);
             config.serialize(&doc.resources, frame_resources_name);
         }
 
         debug!("\tscene builder");
         self.scene_tx.send(SceneBuilderRequest::SaveScene(config.clone())).unwrap();
@@ -1742,17 +1765,17 @@ impl RenderBackend {
             let frame_resources = CaptureConfig::deserialize::<FrameResources, _>(root, &frame_resources_name)
                 .expect(&format!("Unable to open {}.ron", frame_resources_name));
 
             let mut doc = Document {
                 scene: scene.clone(),
                 removed_pipelines: Vec::new(),
                 view: view.clone(),
                 clip_scroll_tree: ClipScrollTree::new(),
-                stamp: FrameStamp::first(),
+                stamp: FrameStamp::first(id),
                 frame_builder: Some(FrameBuilder::empty()),
                 output_pipelines: FastHashSet::default(),
                 dynamic_properties: SceneProperties::new(),
                 hit_tester: None,
                 frame_is_valid: false,
                 hit_tester_is_valid: false,
                 rendered_frame_is_valid: false,
                 has_built_scene: false,
--- a/gfx/wr/webrender/src/resource_cache.rs
+++ b/gfx/wr/webrender/src/resource_cache.rs
@@ -1605,17 +1605,16 @@ impl ResourceCache {
         // Apply any updates of new / updated images (incl. blobs) to the texture cache.
         self.update_texture_cache(gpu_cache);
         render_tasks.prepare_for_render();
         self.cached_render_tasks.update(
             gpu_cache,
             &mut self.texture_cache,
             render_tasks,
         );
-        self.texture_cache.end_frame(texture_cache_profile);
     }
 
     fn rasterize_missing_blob_images(&mut self) {
         if self.missing_blob_images.is_empty() {
             return;
         }
 
         self.blob_image_handler
@@ -1762,19 +1761,20 @@ impl ResourceCache {
                     None,
                     UvRectKind::Rect,
                     eviction,
                 );
             }
         }
     }
 
-    pub fn end_frame(&mut self) {
+    pub fn end_frame(&mut self, texture_cache_profile: &mut TextureCacheProfileCounters) {
         debug_assert_eq!(self.state, State::QueryResources);
         self.state = State::Idle;
+        self.texture_cache.end_frame(texture_cache_profile);
     }
 
     pub fn set_debug_flags(&mut self, flags: DebugFlags) {
         self.texture_cache.set_debug_flags(flags);
     }
 
     pub fn clear(&mut self, what: ClearCache) {
         if what.contains(ClearCache::IMAGES) {
--- a/gfx/wr/webrender/src/texture_cache.rs
+++ b/gfx/wr/webrender/src/texture_cache.rs
@@ -1,20 +1,20 @@
 /* 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 api::{DebugFlags, DeviceIntPoint, DeviceIntRect, DeviceIntSize, DirtyRect, ImageDirtyRect};
-use api::{ExternalImageType, ImageFormat};
-use api::ImageDescriptor;
+use api::{DebugFlags, DeviceIntPoint, DeviceIntRect, DeviceIntSize};
+use api::{DirtyRect, ImageDirtyRect, DocumentId, ExternalImageType, ImageFormat};
+use api::{IdNamespace, ImageDescriptor};
 use device::{TextureFilter, total_gpu_bytes_allocated};
 use freelist::{FreeList, FreeListHandle, UpsertResult, WeakFreeListHandle};
 use gpu_cache::{GpuCache, GpuCacheHandle};
 use gpu_types::{ImageSource, UvRectKind};
-use internal_types::{CacheTextureId, LayerIndex, TextureUpdateList, TextureUpdateSource};
+use internal_types::{CacheTextureId, FastHashMap, LayerIndex, TextureUpdateList, TextureUpdateSource};
 use internal_types::{TextureSource, TextureCacheAllocInfo, TextureCacheUpdate};
 use profiler::{ResourceProfileCounter, TextureCacheProfileCounters};
 use render_backend::{FrameId, FrameStamp};
 use resource_cache::{CacheItem, CachedImageData};
 use std::cell::Cell;
 use std::cmp;
 use std::mem;
 use std::time::{Duration, SystemTime};
@@ -277,17 +277,17 @@ impl SharedTextures {
             (_, _) => unreachable!(),
         }
     }
 }
 
 /// Lists of strong handles owned by the texture cache. There is only one strong
 /// handle for each entry, but unlimited weak handles. Consumers receive the weak
 /// handles, and `TextureCache` owns the strong handles internally.
-#[derive(Default)]
+#[derive(Default, Debug)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 struct EntryHandles {
     /// Handles for each standalone texture cache entry.
     standalone: Vec<FreeListHandle<CacheEntryMarker>>,
     /// Handles for each shared texture cache entry.
     shared: Vec<FreeListHandle<CacheEntryMarker>>,
 }
@@ -405,16 +405,37 @@ impl EvictionThresholdBuilder {
 
         EvictionThreshold {
             id: self.now.frame_id() - max_frames,
             time: self.now.time() - Duration::from_millis(max_time_ms),
         }
     }
 }
 
+#[cfg_attr(feature = "capture", derive(Serialize))]
+#[cfg_attr(feature = "replay", derive(Deserialize))]
+pub struct PerDocumentData {
+    /// The last `FrameStamp` in which we expired the shared cache for
+    /// this document.
+    last_shared_cache_expiration: FrameStamp,
+
+    /// Strong handles for all entries that this document has allocated
+    /// from the shared FreeList.
+    handles: EntryHandles,
+}
+
+impl PerDocumentData {
+    pub fn new() -> Self {
+        PerDocumentData {
+            last_shared_cache_expiration: FrameStamp::INVALID,
+            handles: EntryHandles::default(),
+        }
+    }
+}
+
 /// General-purpose manager for images in GPU memory. This includes images,
 /// rasterized glyphs, rasterized blobs, cached render tasks, etc.
 ///
 /// The texture cache is owned and managed by the RenderBackend thread, and
 /// produces a series of commands to manipulate the textures on the Renderer
 /// thread. These commands are executed before any rendering is performed for
 /// a given frame.
 ///
@@ -448,28 +469,33 @@ pub struct TextureCache {
     /// A list of allocations and updates that need to be applied to the texture
     /// cache in the rendering thread this frame.
     #[cfg_attr(all(feature = "serde", any(feature = "capture", feature = "replay")), serde(skip))]
     pending_updates: TextureUpdateList,
 
     /// The current `FrameStamp`. Used for cache eviction policies.
     now: FrameStamp,
 
-    /// The last `FrameStamp` in which we expired the shared cache.
-    last_shared_cache_expiration: FrameStamp,
-
     /// The time at which we first reached the byte threshold for reclaiming
     /// cache memory. `None if we haven't reached the threshold.
     reached_reclaim_threshold: Option<SystemTime>,
 
     /// Maintains the list of all current items in the texture cache.
     entries: FreeList<CacheEntry, CacheEntryMarker>,
 
-    /// Strong handles for all entries allocated from the above `FreeList`.
-    handles: EntryHandles,
+    /// Holds items that need to be maintained on a per-document basis. If we
+    /// modify this data for a document without also building a frame for that
+    /// document, then we might end up erroneously evicting items out from
+    /// under that document.
+    per_doc_data: FastHashMap<DocumentId, PerDocumentData>,
+
+    /// The current document's data. This is moved out of per_doc_data in
+    /// begin_frame and moved back in end_frame to solve borrow checker issues.
+    /// We should try removing this when we require a rustc with NLL.
+    doc_data: PerDocumentData,
 }
 
 impl TextureCache {
     pub fn new(max_texture_size: i32, mut max_texture_layers: usize) -> Self {
         if cfg!(target_os = "macos") {
             // On MBP integrated Intel GPUs, texture arrays appear to be
             // implemented as a single texture of stacked layers, and that
             // texture appears to be subject to the texture size limit. As such,
@@ -492,133 +518,177 @@ impl TextureCache {
             //     with the same bug but a lower max texture size, we might need
             //     to rethink our strategy anyway, since a limit below 32MB might
             //     start to introduce performance issues.
             max_texture_layers = max_texture_layers.min(32);
         }
 
         TextureCache {
             shared_textures: SharedTextures::new(),
+            reached_reclaim_threshold: None,
+            entries: FreeList::new(),
             max_texture_size,
             max_texture_layers,
             debug_flags: DebugFlags::empty(),
             next_id: CacheTextureId(1),
             pending_updates: TextureUpdateList::new(),
             now: FrameStamp::INVALID,
-            last_shared_cache_expiration: FrameStamp::INVALID,
-            reached_reclaim_threshold: None,
-            entries: FreeList::new(),
-            handles: EntryHandles::default(),
+            per_doc_data: FastHashMap::default(),
+            doc_data: PerDocumentData::new(),
         }
     }
 
     /// Creates a TextureCache and sets it up with a valid `FrameStamp`, which
     /// is useful for avoiding panics when instantiating the `TextureCache`
     /// directly from unit test code.
     #[allow(dead_code)]
     pub fn new_for_testing(max_texture_size: i32, max_texture_layers: usize) -> Self {
         let mut cache = Self::new(max_texture_size, max_texture_layers);
-        let mut now = FrameStamp::first();
+        let mut now = FrameStamp::first(DocumentId(IdNamespace(1), 1));
         now.advance();
         cache.begin_frame(now);
         cache
     }
 
     pub fn set_debug_flags(&mut self, flags: DebugFlags) {
         self.debug_flags = flags;
     }
 
     /// Clear all standalone textures in the cache.
     pub fn clear_standalone(&mut self) {
-        let standalone_entry_handles = mem::replace(
-            &mut self.handles.standalone,
-            Vec::new(),
-        );
+        debug_assert!(!self.now.is_valid());
+        // This pref just helps us avoid crashes when we begin using multiple documents.
+        // What we need to do for clear to work correctly with multiple documents is
+        // to ensure that we generate frames for all documents whenever we do this.
+        if self.debug_flags.contains(DebugFlags::TEXTURE_CACHE_DBG_DISABLE_SHRINK) {
+            return;
+        }
 
-        for handle in standalone_entry_handles {
-            let entry = self.entries.free(handle);
-            entry.evict();
-            self.free(entry);
+        let mut per_doc_data = mem::replace(&mut self.per_doc_data, FastHashMap::default());
+        for (&_, doc_data) in per_doc_data.iter_mut() {
+            let standalone_entry_handles = mem::replace(
+                &mut doc_data.handles.standalone,
+                Vec::new(),
+            );
+
+            for handle in standalone_entry_handles {
+                let entry = self.entries.free(handle);
+                entry.evict();
+                self.free(entry);
+            }
         }
+        self.per_doc_data = per_doc_data;
     }
 
     /// Clear all shared textures in the cache.
     pub fn clear_shared(&mut self) {
-        let shared_entry_handles = mem::replace(
-            &mut self.handles.shared,
-            Vec::new(),
-        );
+        // This pref just helps us avoid crashes when we begin using multiple documents.
+        // What we need to do for clear to work correctly with multiple documents is
+        // to ensure that we generate frames for all documents whenever we do this.
+        if self.debug_flags.contains(DebugFlags::TEXTURE_CACHE_DBG_DISABLE_SHRINK) {
+            return;
+        }
 
-        for handle in shared_entry_handles {
-            let entry = self.entries.free(handle);
-            entry.evict();
-            self.free(entry);
+        self.unset_doc_data();
+        let mut per_doc_data = mem::replace(&mut self.per_doc_data, FastHashMap::default());
+        for (&_, doc_data) in per_doc_data.iter_mut() {
+            let shared_entry_handles = mem::replace(
+                &mut doc_data.handles.shared,
+                Vec::new(),
+            );
+
+            for handle in shared_entry_handles {
+                let entry = self.entries.free(handle);
+                entry.evict();
+                self.free(entry);
+            }
         }
 
         self.shared_textures.clear(&mut self.pending_updates);
+        self.per_doc_data = per_doc_data;
+        self.set_doc_data();
     }
 
     /// Clear all entries in the texture cache. This is a fairly drastic
     /// step that should only be called very rarely.
     pub fn clear(&mut self) {
         self.clear_standalone();
         self.clear_shared();
     }
 
+    fn set_doc_data(&mut self) {
+        let document_id = self.now.document_id();
+        self.doc_data = self.per_doc_data
+                            .remove(&document_id)
+                            .unwrap_or_else(|| PerDocumentData::new());
+    }
+
+    fn unset_doc_data(&mut self) {
+        self.per_doc_data.insert(self.now.document_id(),
+                                 mem::replace(&mut self.doc_data, PerDocumentData::new()));
+    }
+
     /// Called at the beginning of each frame.
     pub fn begin_frame(&mut self, stamp: FrameStamp) {
+        debug_assert!(!self.now.is_valid());
         self.now = stamp;
+        self.set_doc_data();
         self.maybe_reclaim_shared_cache_memory();
     }
 
     /// Called at the beginning of each frame to periodically GC and reclaim
     /// storage if the cache has grown too large.
     fn maybe_reclaim_shared_cache_memory(&mut self) {
+        debug_assert!(self.now.is_valid());
         // The minimum number of bytes that we must be able to reclaim in order
         // to justify clearing the entire shared cache in order to shrink it.
         const RECLAIM_THRESHOLD_BYTES: usize = 5 * 1024 * 1024;
 
         // Normally the shared cache only gets GCed when we fail to allocate.
         // However, we also perform a periodic, conservative GC to ensure that
         // we recover unused memory in bounded time, rather than having it
         // depend on allocation patterns of subsequent content.
         let time_since_last_gc = self.now.time()
-            .duration_since(self.last_shared_cache_expiration.time())
+            .duration_since(self.doc_data.last_shared_cache_expiration.time())
             .unwrap_or(Duration::default());
         let do_periodic_gc = time_since_last_gc >= Duration::from_secs(5) &&
             self.shared_textures.size_in_bytes() >= RECLAIM_THRESHOLD_BYTES * 2;
         if do_periodic_gc {
             let threshold = EvictionThresholdBuilder::new(self.now)
                 .max_frames(1)
                 .max_time_s(10)
                 .build();
             self.maybe_expire_old_shared_entries(threshold);
         }
 
         // If we've had a sufficient number of unused layers for a sufficiently
         // long time, just blow the whole cache away to shrink it.
         //
         // We could do this more intelligently with a resize+blit, but that would
         // add complexity for a rare case.
+        //
+        // This block of code is broken with multiple documents, and should be
+        // moved out into a section that runs before building any frames in a
+        // group of documents.
         if self.shared_textures.empty_region_bytes() >= RECLAIM_THRESHOLD_BYTES {
             self.reached_reclaim_threshold.get_or_insert(self.now.time());
         } else {
             self.reached_reclaim_threshold = None;
         }
         if let Some(t) = self.reached_reclaim_threshold {
             let dur = self.now.time().duration_since(t).unwrap_or(Duration::default());
             if dur >= Duration::from_secs(5) {
                 self.clear_shared();
                 self.reached_reclaim_threshold = None;
             }
         }
-
     }
 
     pub fn end_frame(&mut self, texture_cache_profile: &mut TextureCacheProfileCounters) {
+        debug_assert!(self.now.is_valid());
         // Expire standalone entries.
         //
         // Most of the time, standalone cache entries correspond to images whose
         // width or height is greater than the region size in the shared cache, i.e.
         // 512 pixels. Cached render tasks also frequently get standalone entries,
         // but those use the Eviction::Eager policy (for now). So the tradeoff there
         // is largely around reducing texture upload jank while keeping memory usage
         // at an acceptable level.
@@ -628,16 +698,19 @@ impl TextureCache {
         self.shared_textures.array_a8_linear
             .update_profile(&mut texture_cache_profile.pages_a8_linear);
         self.shared_textures.array_a16_linear
             .update_profile(&mut texture_cache_profile.pages_a16_linear);
         self.shared_textures.array_rgba8_linear
             .update_profile(&mut texture_cache_profile.pages_rgba8_linear);
         self.shared_textures.array_rgba8_nearest
             .update_profile(&mut texture_cache_profile.pages_rgba8_nearest);
+
+        self.unset_doc_data();
+        self.now = FrameStamp::INVALID;
     }
 
     // Request an item in the texture cache. All images that will
     // be used on a frame *must* have request() called on their
     // handle, to update the last used timestamp and ensure
     // that resources are not flushed from the cache too early.
     //
     // Returns true if the image needs to be uploaded to the
@@ -685,16 +758,18 @@ impl TextureCache {
         data: Option<CachedImageData>,
         user_data: [f32; 3],
         mut dirty_rect: ImageDirtyRect,
         gpu_cache: &mut GpuCache,
         eviction_notice: Option<&EvictionNotice>,
         uv_rect_kind: UvRectKind,
         eviction: Eviction,
     ) {
+        debug_assert!(self.now.is_valid());
+
         // Determine if we need to allocate texture cache memory
         // for this item. We need to reallocate if any of the following
         // is true:
         // - Never been in the cache
         // - Has been in the cache but was evicted.
         // - Exists in the cache but dimensions / format have changed.
         let realloc = match self.entries.get_opt(handle) {
             Some(entry) => {
@@ -848,22 +923,23 @@ impl TextureCache {
             .scale_by_pressure()
             .build()
     }
 
     /// Shared eviction code for standalone and shared entries.
     ///
     /// See `EvictionThreshold` for more details on policy.
     fn expire_old_entries(&mut self, kind: EntryKind, threshold: EvictionThreshold) {
+        debug_assert!(self.now.is_valid());
         // Iterate over the entries in reverse order, evicting the ones older than
         // the frame age threshold. Reverse order avoids iterator invalidation when
         // removing entries.
-        for i in (0..self.handles.select(kind).len()).rev() {
+        for i in (0..self.doc_data.handles.select(kind).len()).rev() {
             let evict = {
-                let entry = self.entries.get(&self.handles.select(kind)[i]);
+                let entry = self.entries.get(&self.doc_data.handles.select(kind)[i]);
                 match entry.eviction {
                     Eviction::Manual => false,
                     Eviction::Auto => threshold.should_evict(entry.last_access),
                     Eviction::Eager => {
                         // Texture cache entries can be evicted at the start of
                         // a frame, or at any time during the frame when a cache
                         // allocation is occurring. This means that entries tagged
                         // with eager eviction may get evicted before they have a
@@ -875,34 +951,35 @@ impl TextureCache {
                         let mut entry_frame_id = entry.last_access.frame_id();
                         entry_frame_id.advance();
 
                         entry_frame_id < self.now.frame_id()
                     }
                 }
             };
             if evict {
-                let handle = self.handles.select(kind).swap_remove(i);
+                let handle = self.doc_data.handles.select(kind).swap_remove(i);
                 let entry = self.entries.free(handle);
                 entry.evict();
                 self.free(entry);
             }
         }
     }
 
     /// Expires old shared entries, if we haven't done so this frame.
     ///
     /// Returns true if any entries were expired.
     fn maybe_expire_old_shared_entries(&mut self, threshold: EvictionThreshold) -> bool {
-        let old_len = self.handles.shared.len();
-        if self.last_shared_cache_expiration.frame_id() < self.now.frame_id() {
+        debug_assert!(self.now.is_valid());
+        let old_len = self.doc_data.handles.shared.len();
+        if self.doc_data.last_shared_cache_expiration.frame_id() < self.now.frame_id() {
             self.expire_old_entries(EntryKind::Shared, threshold);
-            self.last_shared_cache_expiration = self.now;
+            self.doc_data.last_shared_cache_expiration = self.now;
         }
-        self.handles.shared.len() != old_len
+        self.doc_data.handles.shared.len() != old_len
     }
 
     // Free a cache entry from the standalone list or shared cache.
     fn free(&mut self, entry: CacheEntry) {
         match entry.details {
             EntryDetails::Standalone { .. } => {
                 // This is a standalone texture allocation. Free it directly.
                 self.pending_updates.push_free(entry.texture_id);
@@ -1101,16 +1178,17 @@ impl TextureCache {
         } else {
             self.allocate_standalone_entry(params)
         }
     }
 
     /// Allocates a cache entry for the given parameters, and updates the
     /// provided handle to point to the new entry.
     fn allocate(&mut self, params: &CacheAllocParams, handle: &mut TextureCacheHandle) {
+        debug_assert!(self.now.is_valid());
         let new_cache_entry = self.allocate_cache_entry(params);
         let new_kind = new_cache_entry.details.kind();
 
         // If the handle points to a valid cache entry, we want to replace the
         // cache entry with our newly updated location. We also need to ensure
         // that the storage (region or standalone) associated with the previous
         // entry here gets freed.
         //
@@ -1120,28 +1198,28 @@ impl TextureCache {
         // This is managed with a database style upsert operation.
         match self.entries.upsert(handle, new_cache_entry) {
             UpsertResult::Updated(old_entry) => {
                 if new_kind != old_entry.details.kind() {
                     // Handle the rare case than an update moves an entry from
                     // shared to standalone or vice versa. This involves a linear
                     // search, but should be rare enough not to matter.
                     let (from, to) = if new_kind == EntryKind::Standalone {
-                        (&mut self.handles.shared, &mut self.handles.standalone)
+                        (&mut self.doc_data.handles.shared, &mut self.doc_data.handles.standalone)
                     } else {
-                        (&mut self.handles.standalone, &mut self.handles.shared)
+                        (&mut self.doc_data.handles.standalone, &mut self.doc_data.handles.shared)
                     };
                     let idx = from.iter().position(|h| h.weak() == *handle).unwrap();
                     to.push(from.remove(idx));
                 }
                 self.free(old_entry);
             }
             UpsertResult::Inserted(new_handle) => {
                 *handle = new_handle.weak();
-                self.handles.select(new_kind).push(new_handle);
+                self.doc_data.handles.select(new_kind).push(new_handle);
             }
         }
     }
 }
 
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 #[derive(Copy, Clone, PartialEq)]
--- a/gfx/wr/webrender_api/src/api.rs
+++ b/gfx/wr/webrender_api/src/api.rs
@@ -789,16 +789,20 @@ impl Epoch {
 #[repr(C)]
 #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize)]
 pub struct IdNamespace(pub u32);
 
 #[repr(C)]
 #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
 pub struct DocumentId(pub IdNamespace, pub u32);
 
+impl DocumentId {
+    pub const INVALID: DocumentId = DocumentId(IdNamespace(0), 0);
+}
+
 /// This type carries no valuable semantics for WR. However, it reflects the fact that
 /// clients (Servo) may generate pipelines by different semi-independent sources.
 /// These pipelines still belong to the same `IdNamespace` and the same `DocumentId`.
 /// Having this extra Id field enables them to generate `PipelineId` without collision.
 pub type PipelineSourceId = u32;
 
 /// From the point of view of WR, `PipelineId` is completely opaque and generic as long as
 /// it's clonable, serializable, comparable, and hashable.
@@ -981,16 +985,17 @@ bitflags! {
         /// Show an overlay displaying overdraw amount.
         const SHOW_OVERDRAW         = 1 << 11;
         /// Display the contents of GPU cache.
         const GPU_CACHE_DBG         = 1 << 12;
         const SLOW_FRAME_INDICATOR  = 1 << 13;
         const TEXTURE_CACHE_DBG_CLEAR_EVICTED = 1 << 14;
         /// Show picture caching debug overlay
         const PICTURE_CACHING_DBG   = 1 << 15;
+        const TEXTURE_CACHE_DBG_DISABLE_SHRINK = 1 << 16;
     }
 }
 
 pub struct RenderApi {
     api_sender: MsgSender<ApiMsg>,
     payload_sender: PayloadSender,
     namespace_id: IdNamespace,
     next_id: Cell<ResourceId>,
--- a/ipc/glue/MessageChannel.cpp
+++ b/ipc/glue/MessageChannel.cpp
@@ -5,16 +5,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/ipc/MessageChannel.h"
 
 #include "mozilla/Assertions.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/ipc/ProcessChild.h"
 #include "mozilla/ipc/ProtocolUtils.h"
 #include "mozilla/Logging.h"
 #include "mozilla/Move.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/ScopeExit.h"
 #include "mozilla/Sprintf.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/TimeStamp.h"
@@ -2519,17 +2520,26 @@ void MessageChannel::OnChannelErrorFromL
   IPC_LOG("OnChannelErrorFromLink");
 
   if (InterruptStackDepth() > 0) NotifyWorkerThread();
 
   if (AwaitingSyncReply() || AwaitingIncomingMessage()) NotifyWorkerThread();
 
   if (ChannelClosing != mChannelState) {
     if (mAbortOnError) {
-      MOZ_CRASH("Aborting on channel error.");
+      // mAbortOnError is set by main actors (e.g., ContentChild) to ensure
+      // that the process terminates even if normal shutdown is prevented.
+      // A MOZ_CRASH() here is not helpful because crash reporting relies
+      // on the parent process which we know is dead or otherwise unusable.
+      //
+      // Additionally, the parent process can (and often is) killed on Android
+      // when apps are backgrounded. We don't need to report a crash for
+      // normal behavior in that case.
+      printf_stderr("Exiting due to channel error.\n");
+      ProcessChild::QuickExit();
     }
     mChannelState = ChannelError;
     mMonitor->Notify();
   }
 
   PostErrorNotifyTask();
 }
 
--- a/js/ffi.configure
+++ b/js/ffi.configure
@@ -18,17 +18,16 @@ js_option('--with-system-ffi',
 use_system_ffi = depends_if('--with-system-ffi')(lambda _: True)
 
 system_ffi = pkg_check_modules('MOZ_FFI', 'libffi > 3.0.9',
                                when=use_system_ffi)
 
 building_ffi = depends(system_ffi)(lambda v: v is None)
 
 set_config('MOZ_SYSTEM_FFI', depends_if(system_ffi)(lambda _: True))
-add_old_configure_assignment('MOZ_SYSTEM_FFI', depends_if(system_ffi)(lambda _: True))
 
 # Target selection, based on ffi/configure.ac.
 @depends(target, when=building_ffi)
 def ffi_target(target):
     if target.cpu not in ('x86', 'x86_64', 'arm', 'aarch64'):
         die('Building libffi from the tree is not supported on this platform. '
             'Use --with-system-ffi instead.')
 
--- a/js/moz.configure
+++ b/js/moz.configure
@@ -408,26 +408,24 @@ def ctypes_default(building_js):
 js_option('--enable-ctypes',
           default=ctypes_default,
           help='{Enable|Disable} js-ctypes')
 
 build_ctypes = depends_if('--enable-ctypes')(lambda _: True)
 
 set_config('BUILD_CTYPES', build_ctypes)
 set_define('BUILD_CTYPES', build_ctypes)
-add_old_configure_assignment('BUILD_CTYPES', build_ctypes)
 
 @depends(build_ctypes, building_js)
 def js_has_ctypes(ctypes, js):
     if ctypes and js:
         return True
 
 set_config('JS_HAS_CTYPES', js_has_ctypes)
 set_define('JS_HAS_CTYPES', js_has_ctypes)
-add_old_configure_assignment('JS_HAS_CTYPES', js_has_ctypes)
 
 @depends('--enable-ctypes', '--enable-compile-environment')
 def ctypes_and_compile_environment(ctypes, compile_environment):
     return ctypes and compile_environment
 
 include('ffi.configure', when=ctypes_and_compile_environment)
 
 
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -9344,39 +9344,16 @@ bool BytecodeEmitter::newSrcNote3(SrcNot
     return false;
   }
   if (indexp) {
     *indexp = index;
   }
   return true;
 }
 
-bool BytecodeEmitter::addToSrcNoteDelta(jssrcnote* sn, ptrdiff_t delta) {
-  /*
-   * Called only from finishTakingSrcNotes to add to main script note
-   * deltas, and only by a small positive amount.
-   */
-  MOZ_ASSERT(current == &main);
-  MOZ_ASSERT((unsigned)delta < (unsigned)SN_XDELTA_LIMIT);
-
-  ptrdiff_t base = SN_DELTA(sn);
-  ptrdiff_t limit = SN_IS_XDELTA(sn) ? SN_XDELTA_LIMIT : SN_DELTA_LIMIT;
-  ptrdiff_t newdelta = base + delta;
-  if (newdelta < limit) {
-    SN_SET_DELTA(sn, newdelta);
-  } else {
-    jssrcnote xdelta;
-    SN_MAKE_XDELTA(&xdelta, delta);
-    if (!main.notes.insert(sn, xdelta)) {
-      return false;
-    }
-  }
-  return true;
-}
-
 bool BytecodeEmitter::setSrcNoteOffset(unsigned index, unsigned which,
                                        ptrdiff_t offset) {
   if (!SN_REPRESENTABLE_OFFSET(offset)) {
     reportError(nullptr, JSMSG_NEED_DIET, js_script_str);
     return false;
   }
 
   SrcNotesVector& notes = this->notes();
@@ -9417,55 +9394,56 @@ bool BytecodeEmitter::setSrcNoteOffset(u
 }
 
 bool BytecodeEmitter::finishTakingSrcNotes(uint32_t* out) {
   MOZ_ASSERT(current == &main);
 
   MOZ_ASSERT(prologue.notes.length() == 0);
   MOZ_ASSERT(prologue.lastNoteOffset == 0);
 
-  // We may need to adjust the offset of the first main note, by adding to
-  // its delta and possibly even prepending SRC_XDELTA notes to it to account
-  // for prologue bytecodes.
+  // We may need to adjust the offset of the first main note to account for
+  // prologue bytecodes. If it can be done by adjusting the delta of the first
+  // main note, we do so. Otherwise, we insert xdeltas into the prologue.
   ptrdiff_t offset = prologueOffset();
   MOZ_ASSERT(offset >= 0);
   if (offset > 0 && main.notes.length() != 0) {
-    // NB: Use as much of the first main note's delta as we can.
+    // Use the first main note's delta if we can.
     jssrcnote* sn = main.notes.begin();
-    ptrdiff_t delta = SN_IS_XDELTA(sn) ? SN_XDELTA_MASK - (*sn & SN_XDELTA_MASK)
-                                       : SN_DELTA_MASK - (*sn & SN_DELTA_MASK);
-    if (offset < delta) {
-      delta = offset;
-    }
-    for (;;) {
-      if (!addToSrcNoteDelta(sn, delta)) {
-        return false;
-      }
-      offset -= delta;
-      if (offset == 0) {
-        break;
-      }
-      delta = Min(offset, SN_XDELTA_MASK);
-      sn = main.notes.begin();
+    ptrdiff_t newDelta = SN_DELTA(sn) + offset;
+    ptrdiff_t deltaLimit = SN_IS_XDELTA(sn) ? SN_XDELTA_LIMIT : SN_DELTA_LIMIT;
+    if (newDelta < deltaLimit) {
+      SN_SET_DELTA(sn, newDelta);
+    } else {
+      // Otherwise, add xdeltas to the prologue.
+      while (offset > 0) {
+        jssrcnote xdelta;
+        ptrdiff_t xdelta_size = Min(offset, SN_XDELTA_MASK);
+        SN_MAKE_XDELTA(&xdelta, xdelta_size);
+        if (!prologue.notes.append(xdelta)) {
+          return false;
+        }
+        offset -= xdelta_size;
+      }
     }
   }
 
   // The + 1 is to account for the final SN_MAKE_TERMINATOR that is appended
   // when the notes are copied to their final destination by copySrcNotes.
-  *out = main.notes.length() + 1;
+  *out = prologue.notes.length() + main.notes.length() + 1;
   return true;
 }
 
 void BytecodeEmitter::copySrcNotes(jssrcnote* destination, uint32_t nsrcnotes) {
-  MOZ_ASSERT(prologue.notes.length() == 0);
+  unsigned prologueCount = prologue.notes.length();
   unsigned mainCount = main.notes.length();
-  // nsrcnotes includes SN_MAKE_TERMINATOR in addition to main.notes.
-  MOZ_ASSERT(mainCount == nsrcnotes - 1);
-  PodCopy(destination, main.notes.begin(), mainCount);
-  SN_MAKE_TERMINATOR(&destination[mainCount]);
+  // nsrcnotes includes SN_MAKE_TERMINATOR in addition to the srcnotes.
+  MOZ_ASSERT(nsrcnotes == prologueCount + mainCount + 1);
+  PodCopy(destination, prologue.notes.begin(), prologueCount);
+  PodCopy(destination + prologueCount, main.notes.begin(), mainCount);
+  SN_MAKE_TERMINATOR(&destination[prologueCount + mainCount]);
 }
 
 void CGNumberList::finish(mozilla::Span<GCPtrValue> array) {
   MOZ_ASSERT(length() == array.size());
 
   for (unsigned i = 0; i < length(); i++) {
     array[i].init(vector[i]);
   }
--- a/js/src/frontend/FoldConstants.cpp
+++ b/js/src/frontend/FoldConstants.cpp
@@ -1438,16 +1438,59 @@ class FoldVisitor : public ParseNodeVisi
     // Don't constant-fold inside "use asm" code, as this could create a parse
     // tree that doesn't type-check as asm.js.
     if (node.funbox()->useAsmOrInsideUseAsm()) {
       return true;
     }
 
     return Base::visitFunction(pn);
   }
+
+  bool visitArrayExpr(ParseNode*& pn) {
+    if (!Base::visitArrayExpr(pn)) {
+      return false;
+    }
+
+    ListNode* list = &pn->as<ListNode>();
+    // Empty arrays are non-constant, since we cannot easily determine their
+    // type.
+    if (list->hasNonConstInitializer() && list->count() > 0) {
+      for (ParseNode* node : list->contents()) {
+        if (!node->isConstant()) {
+          return true;
+        }
+      }
+      list->unsetHasNonConstInitializer();
+    }
+    return true;
+  }
+
+  bool visitObjectExpr(ParseNode*& pn) {
+    if (!Base::visitObjectExpr(pn)) {
+      return false;
+    }
+
+    ListNode* list = &pn->as<ListNode>();
+    if (list->hasNonConstInitializer()) {
+      for (ParseNode* node : list->contents()) {
+        if (node->getKind() != ParseNodeKind::Colon) {
+          return true;
+        }
+        BinaryNode* binary = &node->as<BinaryNode>();
+        if (binary->left()->isKind(ParseNodeKind::ComputedName)) {
+          return true;
+        }
+        if (!binary->right()->isConstant()) {
+          return true;
+        }
+      }
+      list->unsetHasNonConstInitializer();
+    }
+    return true;
+  }
 };
 
 bool Fold(JSContext* cx, ParseNode** pnp) {
   FoldVisitor visitor(cx);
   return visitor.visit(*pnp);
 }
 
 bool frontend::FoldConstants(JSContext* cx, ParseNode** pnp,
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -1229,38 +1229,42 @@ class ListNode : public ParseNode {
 
   MOZ_MUST_USE bool hasArrayHoleOrSpread() const {
     MOZ_ASSERT(isKind(ParseNodeKind::ArrayExpr));
     return pn_u.list.xflags & hasArrayHoleOrSpreadBit;
   }
 
   MOZ_MUST_USE bool hasNonConstInitializer() const {
     MOZ_ASSERT(isKind(ParseNodeKind::ArrayExpr) ||
-               isKind(ParseNodeKind::ObjectExpr) ||
-               isKind(ParseNodeKind::ClassMemberList));
+               isKind(ParseNodeKind::ObjectExpr));
     return pn_u.list.xflags & hasNonConstInitializerBit;
   }
 
   void setHasTopLevelFunctionDeclarations() {
     MOZ_ASSERT(isKind(ParseNodeKind::StatementList));
     pn_u.list.xflags |= hasTopLevelFunctionDeclarationsBit;
   }
 
   void setHasArrayHoleOrSpread() {
     MOZ_ASSERT(isKind(ParseNodeKind::ArrayExpr));
     pn_u.list.xflags |= hasArrayHoleOrSpreadBit;
   }
 
   void setHasNonConstInitializer() {
     MOZ_ASSERT(isKind(ParseNodeKind::ArrayExpr) ||
-               isKind(ParseNodeKind::ObjectExpr) ||
-               isKind(ParseNodeKind::ClassMemberList));
+               isKind(ParseNodeKind::ObjectExpr));
     pn_u.list.xflags |= hasNonConstInitializerBit;
   }
 
+  void unsetHasNonConstInitializer() {
+    MOZ_ASSERT(isKind(ParseNodeKind::ArrayExpr) ||
+               isKind(ParseNodeKind::ObjectExpr));
+    pn_u.list.xflags &= ~hasNonConstInitializerBit;
+  }
+
   /*
    * Compute a pointer to the last element in a singly-linked list. NB: list
    * must be non-empty -- this is asserted!
    */
   ParseNode* last() const {
     MOZ_ASSERT(!empty());
     //
     // ParseNode                      ParseNode
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -3760,24 +3760,16 @@ GeneralParser<ParseHandler, Unit>::bindi
   }
 
   BinaryNodeType assign =
       handler.newAssignment(ParseNodeKind::AssignExpr, lhs, rhs);
   if (!assign) {
     return null();
   }
 
-  if (foldConstants) {
-    Node node = assign;
-    if (!FoldConstants(context, &node, this)) {
-      return null();
-    }
-    assign = handler.asBinary(node);
-  }
-
   return assign;
 }
 
 template <class ParseHandler, typename Unit>
 typename ParseHandler::NameNodeType
 GeneralParser<ParseHandler, Unit>::bindingIdentifier(
     DeclarationKind kind, YieldHandling yieldHandling) {
   RootedPropertyName name(context, bindingIdentifier(yieldHandling));
@@ -9262,19 +9254,16 @@ GeneralParser<ParseHandler, Unit>::array
                                   &possibleErrorInner);
         if (!element) {
           return null();
         }
         if (!checkDestructuringAssignmentElement(
                 element, elementPos, &possibleErrorInner, possibleError)) {
           return null();
         }
-        if (foldConstants && !FoldConstants(context, &element, this)) {
-          return null();
-        }
         handler.addArrayElement(literal, element);
       }
 
       bool matched;
       if (!tokenStream.matchToken(&matched, TokenKind::Comma,
                                   TokenStream::Operand)) {
         return null();
       }
@@ -9383,17 +9372,18 @@ typename ParseHandler::Node GeneralParse
       propName = stringLiteral();
       if (!propName) {
         return null();
       }
       break;
     }
 
     case TokenKind::LeftBracket:
-      propName = computedPropertyName(yieldHandling, maybeDecl, propList);
+      propName = computedPropertyName(yieldHandling, maybeDecl,
+                                      propertyNameContext, propList);
       if (!propName) {
         return null();
       }
       break;
 
     default: {
       if (!TokenKindIsPossibleIdentifierName(ltok)) {
         error(JSMSG_UNEXPECTED_TOKEN, "property name", TokenKindToDesc(ltok));
@@ -9448,17 +9438,18 @@ typename ParseHandler::Node GeneralParse
         if (!propAtom.get()) {
           return null();
         }
         return newNumber(anyChars.currentToken());
       }
       if (tt == TokenKind::LeftBracket) {
         tokenStream.consumeKnownToken(TokenKind::LeftBracket);
 
-        return computedPropertyName(yieldHandling, maybeDecl, propList);
+        return computedPropertyName(yieldHandling, maybeDecl,
+                                    propertyNameContext, propList);
       }
 
       // Not an accessor property after all.
       propName = handler.newObjectLiteralPropertyName(propAtom.get(), pos());
       if (!propName) {
         return null();
       }
       break;
@@ -9523,26 +9514,27 @@ typename ParseHandler::Node GeneralParse
   error(JSMSG_COLON_AFTER_ID);
   return null();
 }
 
 template <class ParseHandler, typename Unit>
 typename ParseHandler::UnaryNodeType
 GeneralParser<ParseHandler, Unit>::computedPropertyName(
     YieldHandling yieldHandling, const Maybe<DeclarationKind>& maybeDecl,
-    ListNodeType literal) {
+    PropertyNameContext propertyNameContext, ListNodeType literal) {
   MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::LeftBracket));
 
   uint32_t begin = pos().begin;
 
   if (maybeDecl) {
     if (*maybeDecl == DeclarationKind::FormalParameter) {
       pc->functionBox()->hasParameterExprs = true;
     }
-  } else {
+  } else if (propertyNameContext ==
+             PropertyNameContext::PropertyNameInLiteral) {
     handler.setListHasNonConstInitializer(literal);
   }
 
   Node assignNode = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
   if (!assignNode) {
     return null();
   }
 
@@ -9642,41 +9634,32 @@ GeneralParser<ParseHandler, Unit>::objec
 
             // Otherwise delay error reporting until we've
             // determined whether or not we're destructuring.
             possibleError->setPendingExpressionErrorAt(
                 namePos, JSMSG_DUPLICATE_PROTO_PROPERTY);
           }
           seenPrototypeMutation = true;
 
-          if (foldConstants && !FoldConstants(context, &propExpr, this)) {
-            return null();
-          }
-
           // This occurs *only* if we observe PropertyType::Normal!
           // Only |__proto__: v| mutates [[Prototype]]. Getters,
           // setters, method/generator definitions, computed
           // property name versions of all of these, and shorthands
           // do not.
           if (!handler.addPrototypeMutation(literal, namePos.begin, propExpr)) {
             return null();
           }
         } else {
-          // Use Node instead of BinaryNodeType to pass it to
-          // FoldConstants.
-          Node propDef = handler.newPropertyDefinition(propName, propExpr);
+          BinaryNodeType propDef =
+              handler.newPropertyDefinition(propName, propExpr);
           if (!propDef) {
             return null();
           }
 
-          if (foldConstants && !FoldConstants(context, &propDef, this)) {
-            return null();
-          }
-
-          handler.addPropertyDefinition(literal, handler.asBinary(propDef));
+          handler.addPropertyDefinition(literal, propDef);
         }
       } else if (propType == PropertyType::Shorthand) {
         /*
          * Support, e.g., |({x, y} = o)| as destructuring shorthand
          * for |({x: x, y: y} = o)|, and |var o = {x, y}| as
          * initializer shorthand for |var o = {x: x, y: y}|.
          */
         Rooted<PropertyName*> name(context, identifierReference(yieldHandling));
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -1343,17 +1343,18 @@ class MOZ_STACK_CLASS GeneralParser : pu
   };
   Node propertyName(YieldHandling yieldHandling,
                     PropertyNameContext propertyNameContext,
                     const mozilla::Maybe<DeclarationKind>& maybeDecl,
                     ListNodeType propList, PropertyType* propType,
                     MutableHandleAtom propAtom);
   UnaryNodeType computedPropertyName(
       YieldHandling yieldHandling,
-      const mozilla::Maybe<DeclarationKind>& maybeDecl, ListNodeType literal);
+      const mozilla::Maybe<DeclarationKind>& maybeDecl,
+      PropertyNameContext propertyNameContext, ListNodeType literal);
   ListNodeType arrayInitializer(YieldHandling yieldHandling,
                                 PossibleError* possibleError);
   inline RegExpLiteralType newRegExp();
 
   ListNodeType objectLiteral(YieldHandling yieldHandling,
                              PossibleError* possibleError);
 
   BinaryNodeType bindingInitializer(Node lhs, DeclarationKind kind,
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -4789,25 +4789,25 @@ class MOZ_STACK_CLASS Debugger::ScriptQu
   void consider(JSScript* script, const JS::AutoRequireNoGC& nogc) {
     if (oom || script->selfHosted()) {
       return;
     }
     Realm* realm = script->realm();
     if (!realms.has(realm)) {
       return;
     }
+    if (!commonFilter(script, nogc)) {
+      return;
+    }
     if (hasLine) {
       if (line < script->lineno() ||
           script->lineno() + GetScriptLineExtent(script) < line) {
         return;
       }
     }
-    if (!commonFilter(script, nogc)) {
-      return;
-    }
 
     if (innermost) {
       // For 'innermost' queries, we don't place scripts in
       // |scriptVector| right away; we may later find another script that
       // is nested inside this one. Instead, we record the innermost
       // script we've found so far for each realm in innermostForRealm,
       // and only populate |scriptVector| at the bottom of findScripts,
       // when we've traversed all the scripts.
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -6060,17 +6060,17 @@ void PresShell::Paint(nsView* aViewToPai
 
   if (layerManager->GetBackendType() == layers::LayersBackend::LAYERS_WR) {
     nsPresContext* pc = GetPresContext();
     LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(
         pc->GetVisibleArea(), pc->AppUnitsPerDevPixel());
     bgcolor = NS_ComposeColors(bgcolor, mCanvasBackgroundColor);
     WebRenderBackgroundData data(wr::ToLayoutRect(bounds),
                                  wr::ToColorF(ToDeviceColor(bgcolor)));
-    nsTArray<wr::WrFilterOp> wrFilters;
+    nsTArray<wr::FilterOp> wrFilters;
 
     MaybeSetupTransactionIdAllocator(layerManager, presContext);
     layerManager->AsWebRenderLayerManager()->EndTransactionWithoutLayer(
         nullptr, nullptr, wrFilters, &data);
     return;
   }
 
   RefPtr<ColorLayer> root = layerManager->CreateColorLayer();
@@ -6808,28 +6808,28 @@ nsresult PresShell::HandleEvent(nsIFrame
         mDelayedEvents.AppendElement(std::move(event));
       }
 
       // If there is a suppressed event listener associated with the document,
       // notify it about the suppressed mouse event. This allows devtools
       // features to continue receiving mouse events even when the devtools
       // debugger has paused execution in a page.
       RefPtr<EventListener> suppressedListener =
-        frame->PresContext()->Document()->GetSuppressedEventListener();
+          frame->PresContext()->Document()->GetSuppressedEventListener();
       if (suppressedListener &&
           aEvent->AsMouseEvent()->mReason != WidgetMouseEvent::eSynthesized) {
         nsCOMPtr<nsIContent> targetContent;
         frame->GetContentForEvent(aEvent, getter_AddRefs(targetContent));
         if (targetContent) {
           aEvent->mTarget = targetContent;
         }
 
         nsCOMPtr<EventTarget> et = aEvent->mTarget;
         RefPtr<Event> event = EventDispatcher::CreateEvent(
-                et, frame->PresContext(), aEvent, EmptyString());
+            et, frame->PresContext(), aEvent, EmptyString());
 
         suppressedListener->HandleEvent(*event);
       }
 
       return NS_OK;
     }
 
     if (!frame) {
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -1652,16 +1652,30 @@ class nsIPresShell : public nsStubDocume
 
   void SetVisualViewportOffset(const nsPoint& aScrollOffset,
                                const nsPoint& aPrevLayoutScrollPos);
 
   nsPoint GetVisualViewportOffset() const { return mVisualViewportOffset; }
 
   nsPoint GetVisualViewportOffsetRelativeToLayoutViewport() const;
 
+  // Ask APZ in the next transaction to scroll to the given visual viewport 
+  // offset (relative to the document).
+  // Use this sparingly, as it will clobber JS-driven scrolling that happens
+  // in the same frame. This is mostly intended to be used in special
+  // situations like "first paint" or session restore.
+  // Please request APZ review if adding a new call site.
+  void SetPendingVisualViewportOffset(
+      const mozilla::Maybe<nsPoint>& aPendingVisualViewportOffset) {
+    mPendingVisualViewportOffset = aPendingVisualViewportOffset;
+  }
+  const mozilla::Maybe<nsPoint>& GetPendingVisualViewportOffset() const {
+    return mPendingVisualViewportOffset;
+  }
+
   nsPoint GetLayoutViewportOffset() const;
 
   virtual void WindowSizeMoveDone() = 0;
   virtual void SysColorChanged() = 0;
   virtual void ThemeChanged() = 0;
   virtual void BackingScaleFactorChanged() = 0;
 
   /**
@@ -1733,16 +1747,21 @@ class nsIPresShell : public nsStubDocume
 
   // Count of the number of times this presshell has been painted to a window.
   uint64_t mPaintCount;
 
   nsSize mVisualViewportSize;
 
   nsPoint mVisualViewportOffset;
 
+  // A pending visual viewport offset that we will ask APZ to scroll to
+  // during the next transaction. Cleared when we send the transaction.
+  // Only applicable to the RCD pres shell.
+  mozilla::Maybe<nsPoint> mPendingVisualViewportOffset;
+
   // A list of stack weak frames. This is a pointer to the last item in the
   // list.
   AutoWeakFrame* mAutoWeakFrames;
 
   // A hash table of heap allocated weak frames.
   nsTHashtable<nsPtrHashKey<WeakFrame>> mWeakFrames;
 
   class DirtyRootsList {
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -8702,16 +8702,25 @@ static void MaybeReflowForInflationScree
   if (scrollableFrame) {
     CSSPoint scrollPosition =
         CSSPoint::FromAppUnits(scrollableFrame->GetScrollPosition());
     CSSPoint apzScrollPosition =
         CSSPoint::FromAppUnits(scrollableFrame->GetApzScrollPosition());
     metrics.SetScrollOffset(scrollPosition);
     metrics.SetBaseScrollOffset(apzScrollPosition);
 
+    if (aIsRootContent) {
+      if (const Maybe<nsPoint>& visualOffset =
+              presShell->GetPendingVisualViewportOffset()) {
+        metrics.SetVisualViewportOffset(CSSPoint::FromAppUnits(*visualOffset));
+        metrics.SetVisualScrollUpdateType(FrameMetrics::eMainThread);
+        presShell->SetPendingVisualViewportOffset(Nothing());
+      }
+    }
+
     CSSRect viewport = metrics.GetLayoutViewport();
     viewport.MoveTo(scrollPosition);
     metrics.SetLayoutViewport(viewport);
 
     nsPoint smoothScrollPosition = scrollableFrame->LastScrollDestination();
     metrics.SetSmoothScrollOffset(CSSPoint::FromAppUnits(smoothScrollPosition));
 
     // If the frame was scrolled since the last layers update, and by something
--- a/layout/generic/crashtests/crashtests.list
+++ b/layout/generic/crashtests/crashtests.list
@@ -655,20 +655,20 @@ load 1234701-1.html
 load 1234701-2.html
 load 1271765.html
 pref(layout.css.xul-box-display-values.content.enabled,true) asserts(2) asserts-if(Android,1) load 1272983-1.html # bug 586628
 pref(layout.css.xul-box-display-values.content.enabled,true) asserts(2) asserts-if(Android,1) load 1272983-2.html # bug 586628
 load 1275059.html
 load 1278007.html
 load 1278080.html
 load 1279814.html
-skip-if(webrender&&gtkWidget) load large-border-radius-dashed.html   # bug 1510076
-skip-if(webrender&&gtkWidget) load large-border-radius-dashed2.html  # bug 1510076
-skip-if(webrender&&gtkWidget) load large-border-radius-dotted.html   # bug 1510076
-skip-if(webrender&&gtkWidget) load large-border-radius-dotted2.html  # bug 1510076
+load large-border-radius-dashed.html
+load large-border-radius-dashed2.html
+load large-border-radius-dotted.html
+load large-border-radius-dotted2.html
 load 1278461-1.html
 load 1278461-2.html
 load 1281102.html
 load 1297427-non-equal-centers.html
 load 1304441.html
 load 1308876-1.html
 load 1316649.html
 load 1343552-1.html
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -2611,24 +2611,22 @@ already_AddRefed<LayerManager> nsDisplay
         }
         // This must be called even if PluginGeometryUpdates were not computed.
         rootPresContext->CollectPluginGeometryUpdates(layerManager);
       }
 
       auto* wrManager = static_cast<WebRenderLayerManager*>(layerManager.get());
 
       nsIDocShell* docShell = presContext->GetDocShell();
-      nsTArray<wr::WrFilterOp> wrFilters;
+      nsTArray<wr::FilterOp> wrFilters;
       gfx::Matrix5x4* colorMatrix =
           nsDocShell::Cast(docShell)->GetColorMatrix();
       if (colorMatrix) {
-        wr::WrFilterOp gs = {wr::WrFilterOpType::ColorMatrix};
-        MOZ_ASSERT(sizeof(gs.matrix) == sizeof(colorMatrix->components));
-        memcpy(&(gs.matrix), colorMatrix->components, sizeof(gs.matrix));
-        wrFilters.AppendElement(gs);
+        wrFilters.AppendElement(
+            wr::FilterOp::ColorMatrix(colorMatrix->components));
       }
 
       wrManager->EndTransactionWithoutLayer(this, aBuilder, wrFilters);
     }
 
     // For layers-free mode, we check the invalidation state bits in the
     // EndTransaction. So we clear the invalidation state bits after
     // EndTransaction.
@@ -6065,17 +6063,17 @@ bool nsDisplayOpacity::CreateWebRenderCo
 
   uint64_t animationsId = AddAnimationsForWebRender(
       this, eCSSProperty_opacity, aManager, aDisplayListBuilder);
   wr::WrAnimationProperty prop{
       wr::WrAnimationType::Opacity,
       animationsId,
   };
 
-  nsTArray<mozilla::wr::WrFilterOp> filters;
+  nsTArray<mozilla::wr::FilterOp> filters;
   StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
                            filters, LayoutDeviceRect(), nullptr,
                            animationsId ? &prop : nullptr, opacityForSC);
 
   aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList(
       &mList, this, aDisplayListBuilder, sc, aBuilder, aResources);
   return true;
 }
@@ -6104,17 +6102,17 @@ LayerState nsDisplayBlendMode::GetLayerS
 }
 
 bool nsDisplayBlendMode::CreateWebRenderCommands(
     mozilla::wr::DisplayListBuilder& aBuilder,
     mozilla::wr::IpcResourceUpdateQueue& aResources,
     const StackingContextHelper& aSc,
     mozilla::layers::RenderRootStateManager* aManager,
     nsDisplayListBuilder* aDisplayListBuilder) {
-  nsTArray<mozilla::wr::WrFilterOp> filters;
+  nsTArray<mozilla::wr::FilterOp> filters;
   StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
                            filters, LayoutDeviceRect(), nullptr, nullptr,
                            nullptr, nullptr, nullptr,
                            nsCSSRendering::GetGFXBlendMode(mBlendMode));
 
   return nsDisplayWrapList::CreateWebRenderCommands(
       aBuilder, aResources, sc, aManager, aDisplayListBuilder);
 }
@@ -6336,17 +6334,17 @@ bool nsDisplayOwnLayer::CreateWebRenderC
   animationInfo.EnsureAnimationsId();
   mWrAnimationId = animationInfo.GetCompositorAnimationsId();
 
   wr::WrAnimationProperty prop;
   prop.id = mWrAnimationId;
   prop.effect_type = wr::WrAnimationType::Transform;
 
   StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
-                           nsTArray<wr::WrFilterOp>(), LayoutDeviceRect(),
+                           nsTArray<wr::FilterOp>(), LayoutDeviceRect(),
                            nullptr, &prop);
 
   nsDisplayWrapList::CreateWebRenderCommands(aBuilder, aResources, sc, aManager,
                                              aDisplayListBuilder);
   return true;
 }
 
 bool nsDisplayOwnLayer::UpdateScrollData(
@@ -7853,17 +7851,17 @@ bool nsDisplayTransform::CreateWebRender
 
   uint64_t animationsId = AddAnimationsForWebRender(
       this, eCSSProperty_transform, aManager, aDisplayListBuilder);
   wr::WrAnimationProperty prop{
       wr::WrAnimationType::Transform,
       animationsId,
   };
 
-  nsTArray<mozilla::wr::WrFilterOp> filters;
+  nsTArray<mozilla::wr::FilterOp> filters;
   Maybe<nsDisplayTransform*> deferredTransformItem;
   if (!mFrame->ChildrenHavePerspective()) {
     // If it has perspective, we create a new scroll data via the
     // UpdateScrollData call because that scenario is more complex. Otherwise
     // we can just stash the transform on the StackingContextHelper and
     // apply it to any scroll data that are created inside this
     // nsDisplayTransform.
     deferredTransformItem = Some(this);
@@ -8460,17 +8458,17 @@ bool nsDisplayPerspective::CreateWebRend
               0.0f);
   Point3D roundedOrigin(NS_round(newOrigin.x), NS_round(newOrigin.y), 0);
 
   gfx::Matrix4x4 transformForSC = gfx::Matrix4x4::Translation(roundedOrigin);
 
   nsIFrame* perspectiveFrame =
       mFrame->GetContainingBlock(nsIFrame::SKIP_SCROLLED_FRAME);
 
-  nsTArray<mozilla::wr::WrFilterOp> filters;
+  nsTArray<mozilla::wr::FilterOp> filters;
   StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
                            filters, LayoutDeviceRect(), nullptr, nullptr,
                            nullptr, &transformForSC, &perspectiveMatrix,
                            gfx::CompositionOp::OP_OVER, !BackfaceIsHidden(),
                            perspectiveFrame->Extend3DContext());
 
   return mList.CreateWebRenderCommands(aBuilder, aResources, sc, aManager,
                                        aDisplayListBuilder);
@@ -9057,17 +9055,17 @@ bool nsDisplayMasksAndClipPaths::CreateW
 
     wr::WrClipId clipId = clip->first();
 
     Maybe<float> opacity = clip->second() == HandleOpacity::Yes
                                ? Some(mFrame->StyleEffects()->mOpacity)
                                : Nothing();
 
     layer.emplace(aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder,
-                  /*aFilters: */ nsTArray<wr::WrFilterOp>(),
+                  /*aFilters: */ nsTArray<wr::FilterOp>(),
                   /*aBounds: */ bounds,
                   /*aBoundTransform: */ nullptr,
                   /*aAnimation: */ nullptr,
                   /*aOpacity: */ opacity.ptrOr(nullptr),
                   /*aTransform: */ nullptr,
                   /*aPerspective: */ nullptr,
                   /*aMixBlendMode: */ gfx::CompositionOp::OP_OVER,
                   /*aBackfaceVisible: */ true,
@@ -9236,83 +9234,92 @@ void nsDisplayFilters::PaintAsLayer(nsDi
 }
 
 static float ClampStdDeviation(float aStdDeviation) {
   // Cap software blur radius for performance reasons.
   return std::min(std::max(0.0f, aStdDeviation), 100.0f);
 }
 
 bool nsDisplayFilters::CreateWebRenderCSSFilters(
-    nsTArray<mozilla::wr::WrFilterOp>& wrFilters) {
+    nsTArray<mozilla::wr::FilterOp>& wrFilters) {
   // All CSS filters are supported by WebRender. SVG filters are not fully
   // supported, those use NS_STYLE_FILTER_URL and are handled separately.
   const nsTArray<nsStyleFilter>& filters = mFrame->StyleEffects()->mFilters;
   for (const nsStyleFilter& filter : filters) {
     switch (filter.GetType()) {
       case NS_STYLE_FILTER_BRIGHTNESS:
+        wrFilters.AppendElement(wr::FilterOp::Brightness(
+            filter.GetFilterParameter().GetFactorOrPercentValue()));
+        break;
       case NS_STYLE_FILTER_CONTRAST:
+        wrFilters.AppendElement(wr::FilterOp::Contrast(
+            filter.GetFilterParameter().GetFactorOrPercentValue()));
+        break;
       case NS_STYLE_FILTER_GRAYSCALE:
+        wrFilters.AppendElement(wr::FilterOp::Grayscale(
+            filter.GetFilterParameter().GetFactorOrPercentValue()));
+        break;
       case NS_STYLE_FILTER_INVERT:
-      case NS_STYLE_FILTER_OPACITY:
+        wrFilters.AppendElement(wr::FilterOp::Invert(
+            filter.GetFilterParameter().GetFactorOrPercentValue()));
+        break;
+      case NS_STYLE_FILTER_OPACITY: {
+        float opacity = filter.GetFilterParameter().GetFactorOrPercentValue();
+        wrFilters.AppendElement(wr::FilterOp::Opacity(
+            wr::PropertyBinding<float>::Value(opacity), opacity));
+        break;
+      }
       case NS_STYLE_FILTER_SATURATE:
+        wrFilters.AppendElement(wr::FilterOp::Saturate(
+            filter.GetFilterParameter().GetFactorOrPercentValue()));
+        break;
       case NS_STYLE_FILTER_SEPIA: {
-        mozilla::wr::WrFilterOp filterOp = {
-            wr::ToWrFilterOpType(filter.GetType()),
-            filter.GetFilterParameter().GetFactorOrPercentValue(),
-        };
-        wrFilters.AppendElement(filterOp);
+        wrFilters.AppendElement(wr::FilterOp::Sepia(
+            filter.GetFilterParameter().GetFactorOrPercentValue()));
         break;
       }
       case NS_STYLE_FILTER_HUE_ROTATE: {
-        mozilla::wr::WrFilterOp filterOp = {
-            wr::ToWrFilterOpType(filter.GetType()),
-            (float)filter.GetFilterParameter().GetAngleValueInDegrees(),
-        };
-        wrFilters.AppendElement(filterOp);
+        wrFilters.AppendElement(wr::FilterOp::HueRotate(
+            (float)filter.GetFilterParameter().GetAngleValueInDegrees()));
         break;
       }
       case NS_STYLE_FILTER_BLUR: {
         float appUnitsPerDevPixel =
             mFrame->PresContext()->AppUnitsPerDevPixel();
-        mozilla::wr::WrFilterOp filterOp = {
-            wr::ToWrFilterOpType(filter.GetType()),
-            ClampStdDeviation(NSAppUnitsToFloatPixels(
-                filter.GetFilterParameter().GetCoordValue(),
-                appUnitsPerDevPixel)),
-        };
-        wrFilters.AppendElement(filterOp);
+        wrFilters.AppendElement(mozilla::wr::FilterOp::Blur(ClampStdDeviation(
+            NSAppUnitsToFloatPixels(filter.GetFilterParameter().GetCoordValue(),
+                                    appUnitsPerDevPixel))));
         break;
       }
       case NS_STYLE_FILTER_DROP_SHADOW: {
         float appUnitsPerDevPixel =
             mFrame->PresContext()->AppUnitsPerDevPixel();
         nsCSSShadowArray* shadows = filter.GetDropShadow();
         if (!shadows || shadows->Length() != 1) {
           MOZ_ASSERT_UNREACHABLE(
               "Exactly one drop shadow should have been "
               "parsed.");
           return false;
         }
 
         nsCSSShadowItem* shadow = shadows->ShadowAt(0);
         nscolor color = shadow->mColor.CalcColor(mFrame);
 
-        mozilla::wr::WrFilterOp filterOp = {
-            wr::ToWrFilterOpType(filter.GetType()),
-            NSAppUnitsToFloatPixels(shadow->mRadius, appUnitsPerDevPixel),
+        auto filterOp = wr::FilterOp::DropShadow(
             {
                 NSAppUnitsToFloatPixels(shadow->mXOffset, appUnitsPerDevPixel),
                 NSAppUnitsToFloatPixels(shadow->mYOffset, appUnitsPerDevPixel),
             },
+            NSAppUnitsToFloatPixels(shadow->mRadius, appUnitsPerDevPixel),
             {
                 NS_GET_R(color) / 255.0f,
                 NS_GET_G(color) / 255.0f,
                 NS_GET_B(color) / 255.0f,
                 NS_GET_A(color) / 255.0f,
-            }};
+            });
 
         wrFilters.AppendElement(filterOp);
         break;
       }
       default:
         return false;
     }
   }
@@ -9329,17 +9336,17 @@ bool nsDisplayFilters::CreateWebRenderCo
   bool snap;
   float auPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
   nsRect displayBounds = GetBounds(aDisplayListBuilder, &snap);
   auto postFilterBounds = LayoutDeviceIntRect::Round(
       LayoutDeviceRect::FromAppUnits(displayBounds, auPerDevPixel));
   auto preFilterBounds = LayoutDeviceIntRect::Round(
       LayoutDeviceRect::FromAppUnits(mBounds, auPerDevPixel));
 
-  nsTArray<mozilla::wr::WrFilterOp> wrFilters;
+  nsTArray<mozilla::wr::FilterOp> wrFilters;
   if (!CreateWebRenderCSSFilters(wrFilters) &&
       !nsSVGIntegrationUtils::BuildWebRenderFilters(
           mFrame, preFilterBounds, wrFilters, postFilterBounds)) {
     return false;
   }
 
   wr::WrClipId clipId =
       aBuilder.DefineClip(Nothing(), wr::ToLayoutRect(postFilterBounds));
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -6347,17 +6347,17 @@ class nsDisplayFilters : public nsDispla
 
   bool CreateWebRenderCommands(
       mozilla::wr::DisplayListBuilder& aBuilder,
       mozilla::wr::IpcResourceUpdateQueue& aResources,
       const StackingContextHelper& aSc,
       mozilla::layers::RenderRootStateManager* aManager,
       nsDisplayListBuilder* aDisplayListBuilder) override;
 
-  bool CreateWebRenderCSSFilters(nsTArray<mozilla::wr::WrFilterOp>& wrFilters);
+  bool CreateWebRenderCSSFilters(nsTArray<mozilla::wr::FilterOp>& wrFilters);
 
  private:
   // relative to mFrame
   nsRect mEffectsBounds;
 };
 
 /* A display item that applies a transformation to all of its descendant
  * elements.  This wrapper should only be used if there is a transform applied
--- a/layout/reftests/css-scrollbars/reftest.list
+++ b/layout/reftests/css-scrollbars/reftest.list
@@ -1,3 +1,4 @@
 default-preferences pref(layout.css.scrollbar-color.enabled,true)
 
 == viewport-scrollbar-color-change.html viewport-scrollbar-color-change-ref.html
+== scrollbar-thin-overflow-change.html scrollbar-thin-overflow-change-ref.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-scrollbars/scrollbar-thin-overflow-change-ref.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<style>
+#outer {
+  scrollbar-width: thin;
+  overflow: auto;
+  border: 5px solid black;
+  background: black;
+  width: 200px;
+  height: 200px;
+}
+#inner {
+  height: 300px;
+}
+</style>
+<div id="outer">
+  <div id="inner"></div>
+</div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-scrollbars/scrollbar-thin-overflow-change.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<style>
+#outer {
+  scrollbar-width: thin;
+  overflow: auto;
+  border: 5px solid black;
+  background: black;
+  width: 200px;
+  height: 400px;
+}
+#inner {
+  height: 300px;
+}
+</style>
+<div id="outer">
+  <div id="inner"></div>
+</div>
+<script>
+// Force a reflow
+document.body.offsetHeight;
+document.getElementById('outer').style.height = '200px';
+</script>
--- a/layout/svg/nsFilterInstance.cpp
+++ b/layout/svg/nsFilterInstance.cpp
@@ -90,17 +90,17 @@ void nsFilterInstance::PaintFilteredFram
                             nullptr, nullptr, nullptr);
   if (instance.IsInitialized()) {
     instance.Render(aCtx, aImgParams, aOpacity);
   }
 }
 
 bool nsFilterInstance::BuildWebRenderFilters(
     nsIFrame* aFilteredFrame, const LayoutDeviceIntRect& aPreFilterBounds,
-    nsTArray<wr::WrFilterOp>& aWrFilters,
+    nsTArray<wr::FilterOp>& aWrFilters,
     LayoutDeviceIntRect& aPostFilterBounds) {
   aWrFilters.Clear();
 
   auto& filterChain = aFilteredFrame->StyleEffects()->mFilters;
   UniquePtr<UserSpaceMetrics> metrics =
       UserSpaceMetricsForFrame(aFilteredFrame);
 
   // TODO: simply using an identity matrix here, was pulling the scale from a
@@ -140,22 +140,20 @@ bool nsFilterInstance::BuildWebRenderFil
   bool filterIsAffectedByPrimSubregion = false;
 
   for (const auto& primitive : instance.mFilterDescription.mPrimitives) {
     chainIsAffectedByPrimSubregion |= filterIsAffectedByPrimSubregion;
     filterIsAffectedByPrimSubregion = false;
 
     bool primIsSrgb = primitive.OutputColorSpace() == gfx::ColorSpace::SRGB;
     if (srgb && !primIsSrgb) {
-      wr::WrFilterOp filterOp = {wr::WrFilterOpType::SrgbToLinear};
-      aWrFilters.AppendElement(filterOp);
+      aWrFilters.AppendElement(wr::FilterOp::SrgbToLinear());
       srgb = false;
     } else if (!srgb && primIsSrgb) {
-      wr::WrFilterOp filterOp = {wr::WrFilterOpType::LinearToSrgb};
-      aWrFilters.AppendElement(filterOp);
+      aWrFilters.AppendElement(wr::FilterOp::LinearToSrgb());
       srgb = true;
     }
 
     const PrimitiveAttributes& attr = primitive.Attributes();
     auto subregion = LayoutDeviceIntRect::FromUnknownRect(
         primitive.PrimitiveSubregion() +
         aPreFilterBounds.TopLeft().ToUnknownPoint());
 
@@ -175,18 +173,18 @@ bool nsFilterInstance::BuildWebRenderFil
         return false;
       }
     }
 
     bool filterIsNoop = false;
 
     if (attr.is<OpacityAttributes>()) {
       float opacity = attr.as<OpacityAttributes>().mOpacity;
-      wr::WrFilterOp filterOp = {wr::WrFilterOpType::Opacity, opacity};
-      aWrFilters.AppendElement(filterOp);
+      aWrFilters.AppendElement(wr::FilterOp::Opacity(
+          wr::PropertyBinding<float>::Value(opacity), opacity));
     } else if (attr.is<ColorMatrixAttributes>()) {
       const ColorMatrixAttributes& attributes =
           attr.as<ColorMatrixAttributes>();
 
       float transposed[20];
       if (!gfx::ComputeColorMatrix(attributes, transposed)) {
         filterIsNoop = true;
         continue;
@@ -208,19 +206,17 @@ bool nsFilterInstance::BuildWebRenderFil
 
       float matrix[20] = {
           transposed[0], transposed[5], transposed[10], transposed[15],
           transposed[1], transposed[6], transposed[11], transposed[16],
           transposed[2], transposed[7], transposed[12], transposed[17],
           transposed[3], transposed[8], transposed[13], transposed[18],
           transposed[4], transposed[9], transposed[14], transposed[19]};
 
-      wr::WrFilterOp filterOp = {wr::WrFilterOpType::ColorMatrix};
-      PodCopy(filterOp.matrix, matrix, 20);
-      aWrFilters.AppendElement(filterOp);
+      aWrFilters.AppendElement(wr::FilterOp::ColorMatrix(matrix));
     } else if (attr.is<GaussianBlurAttributes>()) {
       if (chainIsAffectedByPrimSubregion) {
         // There's a clip that needs to apply before the blur filter, but
         // WebRender only lets us apply the clip at the end of the filter
         // chain. Clipping after a blur is not equivalent to clipping before
         // a blur, so bail out.
         return false;
       }
@@ -229,66 +225,61 @@ bool nsFilterInstance::BuildWebRenderFil
 
       const Size& stdDev = blur.mStdDeviation;
       if (stdDev.width != stdDev.height) {
         return false;
       }
 
       float radius = stdDev.width;
       if (radius != 0.0) {
-        wr::WrFilterOp filterOp = {wr::WrFilterOpType::Blur, radius};
-        aWrFilters.AppendElement(filterOp);
+        aWrFilters.AppendElement(wr::FilterOp::Blur(radius));
       } else {
         filterIsNoop = true;
       }
     } else if (attr.is<DropShadowAttributes>()) {
       if (chainIsAffectedByPrimSubregion) {
         // We have to bail out for the same reason we would with a blur filter.
         return false;
       }
 
       const DropShadowAttributes& shadow = attr.as<DropShadowAttributes>();
 
       const Size& stdDev = shadow.mStdDeviation;
       if (stdDev.width != stdDev.height) {
         return false;
       }
 
+      wr::LayoutVector2D offset = {(float)shadow.mOffset.x,
+                                   (float)shadow.mOffset.y};
       float radius = stdDev.width;
-      wr::WrFilterOp filterOp = {
-          wr::WrFilterOpType::DropShadow,
-          radius,
-          {(float)shadow.mOffset.x, (float)shadow.mOffset.y},
-          wr::ToColorF(shadow.mColor)};
+      wr::FilterOp filterOp =
+          wr::FilterOp::DropShadow(offset, radius, wr::ToColorF(shadow.mColor));
 
       aWrFilters.AppendElement(filterOp);
     } else {
       return false;
     }
 
     if (filterIsNoop && aWrFilters.Length() > 0 &&
-        (aWrFilters.LastElement().filter_type ==
-             wr::WrFilterOpType::SrgbToLinear ||
-         aWrFilters.LastElement().filter_type ==
-             wr::WrFilterOpType::LinearToSrgb)) {
+        (aWrFilters.LastElement().tag == wr::FilterOp::Tag::SrgbToLinear ||
+         aWrFilters.LastElement().tag == wr::FilterOp::Tag::LinearToSrgb)) {
       // We pushed a color space conversion filter in prevision of applying
       // another filter which turned out to be a no-op, so the conversion is
       // unnecessary. Remove it from the filter list.
       // This is both an optimization and a way to pass the wptest
       // css/filter-effects/filter-scale-001.html for which the needless
       // sRGB->linear->no-op->sRGB roundtrip introduces a slight error and we
       // cannot add fuzziness to the test.
       Unused << aWrFilters.PopLastElement();
       srgb = !srgb;
     }
   }
 
   if (!srgb) {
-    wr::WrFilterOp filterOp = {wr::WrFilterOpType::LinearToSrgb};
-    aWrFilters.AppendElement(filterOp);
+    aWrFilters.AppendElement(wr::FilterOp::LinearToSrgb());
   }
 
   // Only adjust the post filter clip if we are able to render this without
   // fallback.
   if (finalClip.isSome()) {
     aPostFilterBounds = finalClip.value();
   }
 
--- a/layout/svg/nsFilterInstance.h
+++ b/layout/svg/nsFilterInstance.h
@@ -120,17 +120,17 @@ class nsFilterInstance {
 
   /**
    * Try to build WebRender filters for a frame if the filters applied to it are
    * supported.
    */
   static bool BuildWebRenderFilters(
       nsIFrame* aFilteredFrame,
       const mozilla::LayoutDeviceIntRect& aPreFilterBounds,
-      nsTArray<mozilla::wr::WrFilterOp>& aWrFilters,
+      nsTArray<mozilla::wr::FilterOp>& aWrFilters,
       mozilla::LayoutDeviceIntRect& aPostFilterBounds);
 
  private:
   /**
    * @param aTargetFrame The frame of the filtered element under consideration,
    *   may be null.
    * @param aTargetContent The filtered element itself.
    * @param aMetrics The metrics to resolve SVG lengths against.
--- a/layout/svg/nsSVGIntegrationUtils.cpp
+++ b/layout/svg/nsSVGIntegrationUtils.cpp
@@ -1091,17 +1091,17 @@ void nsSVGIntegrationUtils::PaintFilter(
 
   nsFilterInstance::PaintFilteredFrame(frame, &context, &callback, &dirtyRegion,
                                        aParams.imgParams, opacity);
 }
 
 bool nsSVGIntegrationUtils::BuildWebRenderFilters(
     nsIFrame* aFilteredFrame,
     const mozilla::LayoutDeviceIntRect& aPreFilterBounds,
-    nsTArray<mozilla::wr::WrFilterOp>& aWrFilters,
+    nsTArray<mozilla::wr::FilterOp>& aWrFilters,
     mozilla::LayoutDeviceIntRect& aPostFilterBounds) {
   return nsFilterInstance::BuildWebRenderFilters(
       aFilteredFrame, aPreFilterBounds, aWrFilters, aPostFilterBounds);
 }
 
 class PaintFrameCallback : public gfxDrawingCallback {
  public:
   PaintFrameCallback(nsIFrame* aFrame, const nsSize aPaintServerSize,
--- a/layout/svg/nsSVGIntegrationUtils.h
+++ b/layout/svg/nsSVGIntegrationUtils.h
@@ -193,17 +193,17 @@ class nsSVGIntegrationUtils final {
 
   /**
    * Try to build WebRender filters for a frame if the filters applied to it are
    * supported.
    */
   static bool BuildWebRenderFilters(
       nsIFrame* aFilteredFrame,
       const mozilla::LayoutDeviceIntRect& aPreFilterBounds,
-      nsTArray<mozilla::wr::WrFilterOp>& aWrFilters,
+      nsTArray<mozilla::wr::FilterOp>& aWrFilters,
       mozilla::LayoutDeviceIntRect& aPostFilterBounds);
 
   /**
    * @param aRenderingContext the target rendering context in which the paint
    * server will be rendered
    * @param aTarget the target frame onto which the paint server will be
    * rendered
    * @param aPaintServer a first-continuation frame to use as the source
--- a/mobile/android/base/java/org/mozilla/gecko/notifications/NotificationReceiver.java
+++ b/mobile/android/base/java/org/mozilla/gecko/notifications/NotificationReceiver.java
@@ -90,21 +90,16 @@ public class NotificationReceiver extend
         if (persistentIntent != null) {
             // Go through GeckoService for persistent notifications.
             GeckoServicesCreatorService.enqueueWork(context, intent);
         }
 
         if (NotificationClient.CLICK_ACTION.equals(action)) {
             GeckoAppShell.onNotificationClick(name, cookie);
 
-            if (persistentIntent != null) {
-                // Don't launch GeckoApp if it's a background persistent notification.
-                return;
-            }
-
             final Intent appIntent = new Intent(GeckoApp.ACTION_ALERT_CALLBACK);
             appIntent.setComponent(new ComponentName(
                     data.getAuthority(), data.getPath().substring(1))); // exclude leading slash.
             appIntent.setData(data);
             appIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
             context.startActivity(appIntent);
 
         } else if (NotificationClient.CLOSE_ACTION.equals(action)) {
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -936,16 +936,17 @@ pref("gfx.compositor.glcontext.opaque", 
 pref("gfx.webrender.highlight-painted-layers", false);
 pref("gfx.webrender.blob-images", true);
 pref("gfx.webrender.blob.invalidation", true);
 pref("gfx.webrender.blob.paint-flashing", false);
 
 // WebRender debugging utilities.
 pref("gfx.webrender.debug.texture-cache", false);
 pref("gfx.webrender.debug.texture-cache.clear-evicted", true);
+pref("gfx.webrender.debug.texture-cache.disable-shrink", false);
 pref("gfx.webrender.debug.render-targets", false);
 pref("gfx.webrender.debug.gpu-cache", false);
 pref("gfx.webrender.debug.alpha-primitives", false);
 pref("gfx.webrender.debug.profiler", false);
 pref("gfx.webrender.debug.gpu-time-queries", false);
 pref("gfx.webrender.debug.gpu-sample-queries", false);
 pref("gfx.webrender.debug.disable-batching", false);
 pref("gfx.webrender.debug.epochs", false);
--- a/moz.configure
+++ b/moz.configure
@@ -114,17 +114,20 @@ js_option('--enable-rust-debug',
 
 @depends(when='--enable-rust-debug')
 def debug_rust():
     return True
 
 set_config('MOZ_DEBUG_RUST', debug_rust)
 set_define('MOZ_DEBUG_RUST', debug_rust)
 
-include('build/moz.configure/pgo.configure')
+js_option(env='MOZ_PGO', help='Build with profile guided optimizations')
+
+set_config('MOZ_PGO', depends('MOZ_PGO')(lambda x: bool(x)))
+
 include('build/moz.configure/pkg.configure')
 # Make this assignment here rather than in pkg.configure to avoid
 # requiring this file in unit tests.
 add_old_configure_assignment('PKG_CONFIG', pkg_config)
 
 include('build/moz.configure/toolchain.configure',
         when='--enable-compile-environment')
 include('build/moz.configure/memory.configure',
--- a/old-configure.in
+++ b/old-configure.in
@@ -2344,83 +2344,16 @@ if test -n "$MOZ_CRASHREPORTER"; then
     if test -z "$HAVE_64BIT_BUILD" -a -n "$COMPILE_ENVIRONMENT"; then
       MOZ_CRASHREPORTER_INJECTOR=1
       AC_DEFINE(MOZ_CRASHREPORTER_INJECTOR)
     fi
   fi
 fi
 
 dnl ========================================================
-dnl = libav-fft configuration
-dnl ========================================================
-
-MOZ_LIBAV_FFT=
-
-dnl Turn on libav-fft for 32-bit windows, and all 64-bit supported platforms.
-dnl 32-bit linux/os x have text relocation issues.
-
-case "$OS_ARCH:$CPU_ARCH" in
-  WINNT:x86)
-      MOZ_LIBAV_FFT=1
-  ;;
-  *:x86_64)
-      MOZ_LIBAV_FFT=1
-  ;;
-esac
-
-dnl Detect if we can use yasm to compile libav's assembly
-
-if test -n "$MOZ_LIBAV_FFT" -a -n "$COMPILE_ENVIRONMENT"; then
-  AC_DEFINE(MOZ_LIBAV_FFT)
-  dnl Do we support libav-fft on this platform?
-  case "$OS_ARCH:$CPU_ARCH" in
-  Darwin:x86_64)
-    LIBAV_FFT_ASFLAGS="-D__x86_64__ -DPIC -DMACHO"
-  ;;
-  WINNT:x86)
-    LIBAV_FFT_ASFLAGS="-DPIC -DWIN32"
-  ;;
-  WINNT:x86_64)
-    LIBAV_FFT_ASFLAGS="-D__x86_64__ -DPIC -DWIN64 -DMSVC"
-  ;;
-  *:x86_64)
-    if $CC -E -dM -</dev/null | grep -q __ELF__; then
-      LIBAV_FFT_ASFLAGS="-D__x86_64__ -DPIC -DELF"
-    fi
-  ;;
-  *)
-    AC_MSG_ERROR([libav's FFT routines are only available for 32-bit windows or 64-bit x86 based platforms.])
-  ;;
-  esac
-fi
-
-if test -n "$LIBAV_FFT_ASFLAGS"; then
-  dnl If we're on an x86 or x64 system which supports libav-fft's asm routines
-  dnl check for Yasm, and error out if it doesn't exist or we have too old of a
-  dnl version.
-  if test -z "$YASM" ; then
-    AC_MSG_ERROR([Yasm is required to build with libav's optimized FFT routines, but you do not appear to have Yasm installed. See https://developer.mozilla.org/en/YASM for more details.])
-  fi
-  dnl Check that we have the right yasm version.  We require 1.0.1 or newer
-  dnl on Linux and 1.1 or newer everywhere else.
-  if test "$OS_ARCH" = "Linux" ; then
-    if test "$_YASM_MAJOR_VERSION" -lt "1" -o \( "$_YASM_MAJOR_VERSION" -eq "1" -a "$_YASM_MINOR_VERSION" -eq "0" -a "$_YASM_RELEASE" -lt "1" \) ; then
-      AC_MSG_ERROR([Yasm 1.0.1 or greater is required to build with libav's optimized FFT routines, but you do not appear to have Yasm installed.  See https://developer.mozilla.org/en/YASM for more details.])
-    fi
-  else
-    if test "$_YASM_MAJOR_VERSION" -lt "1" -o \( "$_YASM_MAJOR_VERSION" -eq "1" -a "$_YASM_MINOR_VERSION" -lt "1" \) ; then
-      AC_MSG_ERROR([Yasm 1.1 or greater is required to build with libav's optimized FFT routines, but you do not appear to have Yasm installed.  See https://developer.mozilla.org/en/YASM for more details.])
-    fi
-  fi
-elif test -n "$MOZ_LIBAV_FFT" -a "${CPU_ARCH}" != "arm"; then
-  dnl Warn if we're not building either libav or opendl-max optimized routines.
-  AC_MSG_WARN([No assembler or assembly support for libav-fft.  Using unoptimized C routines.])
-fi
-
-dnl ========================================================
 dnl = FFmpeg's ffvpx configuration
 dnl ========================================================
 
 MOZ_FFVPX=
 MOZ_FFVPX_FLACONLY=
 case "$CPU_ARCH" in
   x86|x86_64)
       MOZ_FFVPX=1
@@ -3789,18 +3722,16 @@ AC_SUBST(USE_N32)
 AC_SUBST(CC_VERSION)
 AC_SUBST(NS_ENABLE_TSF)
 AC_SUBST(WIN32_CONSOLE_EXE_LDFLAGS)
 AC_SUBST(WIN32_GUI_EXE_LDFLAGS)
 
 AC_SUBST(MOZ_FFVPX)
 AC_SUBST(MOZ_FFVPX_FLACONLY)
 AC_SUBST_LIST(FFVPX_ASFLAGS)
-AC_SUBST(MOZ_LIBAV_FFT)
-AC_SUBST_LIST(LIBAV_FFT_ASFLAGS)
 AC_SUBST(MOZ_DEVTOOLS)
 
 AC_SUBST(MOZ_PACKAGE_JSSHELL)
 AC_SUBST(MOZ_FOLD_LIBS)
 AC_SUBST_LIST(MOZ_FOLD_LIBS_FLAGS)
 
 AC_SUBST(DMG_TOOL)
 
--- a/python/mach/mach/test/test_telemetry.py
+++ b/python/mach/mach/test/test_telemetry.py
@@ -149,10 +149,17 @@ def test_registrar_dispatch(run_mach):
     # from within the same interpreter as the `mach python` command.
     data = run_mach('python', '--exec-file',
                     os.path.join(os.path.dirname(__file__), 'registrar_dispatch.py'))
     assert len(data) == 1
     d = data[0]
     assert d['command'] == 'python'
 
 
+def test_zero_microseconds(run_mach):
+    data = run_mach('python', '--exec-file',
+                    os.path.join(os.path.dirname(__file__), 'zero_microseconds.py'))
+    d = data[0]
+    assert d['command'] == 'python'
+
+
 if __name__ == '__main__':
     mozunit.main()
new file mode 100644
--- /dev/null
+++ b/python/mach/mach/test/zero_microseconds.py
@@ -0,0 +1,14 @@
+# This code is loaded via `mach python --exec-file`, so it runs in the scope of
+# the `mach python` command.
+old = self._mach_context.post_dispatch_handler  # noqa: F821
+
+
+def handler(context, handler, instance, result,
+            start_time, end_time, depth, args):
+    global old
+    # Round off sub-second precision.
+    old(context, handler, instance, result,
+        int(start_time), end_time, depth, args)
+
+
+self._mach_context.post_dispatch_handler = handler  # noqa: F821
--- a/python/mozbuild/mozbuild/action/dumpsymbols.py
+++ b/python/mozbuild/mozbuild/action/dumpsymbols.py
@@ -33,17 +33,20 @@ def dump_symbols(target, tracking_file, 
     dump_syms_bin = '%s%s' % (dump_syms_bin, buildconfig.substs['BIN_SUFFIX'])
 
     os_arch = buildconfig.substs['OS_ARCH']
     if os_arch == 'WINNT':
         sym_store_args.extend(['-c', '--vcs-info'])
         if os.environ.get('PDBSTR_PATH'):
             sym_store_args.append('-i')
     elif os_arch == 'Darwin':
-        sym_store_args.extend(['-c', '-a', buildconfig.substs['OS_TEST'], '--vcs-info'])
+        cpu = {
+            'x86': 'i386',
+        }.get(buildconfig.substs['TARGET_CPU'], buildconfig.substs['TARGET_CPU'])
+        sym_store_args.extend(['-c', '-a', cpu, '--vcs-info'])
     elif os_arch == 'Linux':
         sym_store_args.extend(['-c', '--vcs-info'])
 
     sym_store_args.append('--install-manifest=%s,%s' % (os.path.join(buildconfig.topobjdir,
                                                                      '_build_manifests',
                                                                      'install',
                                                                      'dist_include'),
                                                         os.path.join(buildconfig.topobjdir,
--- a/python/mozbuild/mozbuild/telemetry.py
+++ b/python/mozbuild/mozbuild/telemetry.py
@@ -253,18 +253,18 @@ def gather_telemetry(command='', success
     `paths` is a dict whose keys are pathnames and values are sigils that should be used to
     replace those pathnames.
 
     Any absolute paths on the command line will be made relative to `paths` or replaced
     with a placeholder to avoid including paths from developer's machines.
     '''
     data = {
         'client_id': get_client_id(mach_context.state_dir),
-        # Simplest way to get an rfc3339 datetime string, AFAICT.
-        'time': datetime.utcfromtimestamp(start_time).isoformat(b'T') + 'Z',
+        # Get an rfc3339 datetime string.
+        'time': datetime.utcfromtimestamp(start_time).strftime('%Y-%m-%dT%H:%M:%S.%fZ'),
         'command': command,
         'argv': filter_args(command, sys.argv, paths),
         'success': success,
         # TODO: use a monotonic clock: https://bugzilla.mozilla.org/show_bug.cgi?id=1481624
         'duration_ms': int((end_time - start_time) * 1000),
         'build_opts': get_build_opts(substs),
         'system': get_system_info(),
         # TODO: exception: https://bugzilla.mozilla.org/show_bug.cgi?id=1481617
--- a/python/mozbuild/mozbuild/test/configure/test_toolkit_moz_configure.py
+++ b/python/mozbuild/mozbuild/test/configure/test_toolkit_moz_configure.py
@@ -43,61 +43,70 @@ class TestToolkitMozConfigure(BaseConfig
         self.test_developer_options('42.0')
 
     def test_valid_yasm_version(self):
         out = StringIO()
         sandbox = self.get_sandbox({}, {}, out=out)
         func = sandbox._depends[sandbox['valid_yasm_version']]._func
 
         # Missing yasm is not an error when nothing requires it.
-        func(None, False, False)
+        func(None, False, False, False)
 
         # Any version of yasm works when nothing requires it.
-        func(Version('1.0'), False, False)
+        func(Version('1.0'), False, False, False)
 
         # Any version of yasm works when something requires any version.
-        func(Version('1.0'), True, False)
-        func(Version('1.0'), True, True)
-        func(Version('1.0'), False, True)
+        func(Version('1.0'), True, False, False)
+        func(Version('1.0'), True, True, False)
+        func(Version('1.0'), False, True, False)
 
         # A version of yasm greater than any requirement works.
-        func(Version('1.5'), Version('1.0'), True)
-        func(Version('1.5'), True, Version('1.0'))
-        func(Version('1.5'), Version('1.1'), Version('1.0'))
+        func(Version('1.5'), Version('1.0'), True, False)
+        func(Version('1.5'), True, Version('1.0'), False)
+        func(Version('1.5'), Version('1.1'), Version('1.0'), False)
 
         out.truncate(0)
         with self.assertRaises(SystemExit):
-            func(None, Version('1.0'), False)
+            func(None, Version('1.0'), False, False)
 
         self.assertEqual(
             out.getvalue(),
             'ERROR: Yasm is required to build with vpx, but you do not appear to have Yasm installed.\n'
         )
 
         out.truncate(0)
         with self.assertRaises(SystemExit):
-            func(None, Version('1.0'), Version('1.0'))
+            func(None, Version('1.0'), Version('1.0'), False)
 
         self.assertEqual(
             out.getvalue(),
             'ERROR: Yasm is required to build with jpeg and vpx, but you do not appear to have Yasm installed.\n'
         )
 
         out.truncate(0)
         with self.assertRaises(SystemExit):
-            func(Version('1.0'), Version('1.1'), Version('1.0'))
+            func(None, Version('1.0'), Version('1.0'), Version('1.0'))
+
+        self.assertEqual(
+            out.getvalue(),
+            'ERROR: Yasm is required to build with jpeg, libav and vpx, but you do not appear to have Yasm installed.\n'
+        )
+
+        out.truncate(0)
+        with self.assertRaises(SystemExit):
+            func(Version('1.0'), Version('1.1'), Version('1.0'), False)
 
         self.assertEqual(
             out.getvalue(),
             'ERROR: Yasm version 1.1 or greater is required to build with vpx.\n'
         )
 
         out.truncate(0)
         with self.assertRaises(SystemExit):
-            func(Version('1.0'), True, Version('1.0.1'))
+            func(Version('1.0'), True, Version('1.0.1'), False)
 
         self.assertEqual(
             out.getvalue(),
             'ERROR: Yasm version 1.0.1 or greater is required to build with jpeg.\n'
         )
 
 
 if __name__ == '__main__':
--- a/servo/components/style/invalidation/media_queries.rs
+++ b/servo/components/style/invalidation/media_queries.rs
@@ -33,20 +33,18 @@ impl MediaListKey {
     }
 }
 
 /// A trait to get a given `MediaListKey` for a given item that can hold a
 /// `MediaList`.
 pub trait ToMediaListKey: Sized {
     /// Get a `MediaListKey` for this item. This key needs to uniquely identify
     /// the item.
-    #[allow(unsafe_code)]
     fn to_media_list_key(&self) -> MediaListKey {
-        use std::mem;
-        MediaListKey(unsafe { mem::transmute(self as *const Self) })
+        MediaListKey(self as *const Self as usize)
     }
 }
 
 impl ToMediaListKey for Stylesheet {}
 impl ToMediaListKey for ImportRule {}
 impl ToMediaListKey for MediaRule {}
 
 /// A struct that holds the result of a media query evaluation pass for the
--- a/taskcluster/ci/test/raptor.yml
+++ b/taskcluster/ci/test/raptor.yml
@@ -49,16 +49,17 @@ raptor-tp6-1-firefox:
             - --test=raptor-tp6-1
 
 raptor-tp6-1-firefox-profiling:
     description: "Raptor tp6-1 on Firefox with Gecko Profiling"
     try-name: raptor-tp6-1-firefox-profiling
     treeherder-symbol: Rap-Prof(tp6-1)
     run-on-projects: ['mozilla-central', 'try']
     max-run-time: 900
+    tier: 2
     mozharness:
         extra-options:
             - --test=raptor-tp6-1
             - --gecko-profile
 
 raptor-tp6-1-chrome:
     description: "Raptor tp6-1 on Chrome"
     try-name: raptor-tp6-1-chrome
@@ -84,16 +85,17 @@ raptor-tp6-2-firefox:
             - --test=raptor-tp6-2
 
 raptor-tp6-2-firefox-profiling:
     description: "Raptor tp6-2 on Firefox with Gecko Profiling"
     try-name: raptor-tp6-2-firefox-profiling
     treeherder-symbol: Rap-Prof(tp6-2)
     run-on-projects: ['mozilla-central', 'try']
     max-run-time: 900
+    tier: 2
     mozharness:
         extra-options:
             - --test=raptor-tp6-2
             - --gecko-profile
 
 raptor-tp6-2-chrome:
     description: "Raptor tp6-2 on Chrome"
     try-name: raptor-tp6-2-chrome
@@ -118,16 +120,17 @@ raptor-tp6-3-firefox:
             - --test=raptor-tp6-3
 
 raptor-tp6-3-firefox-profiling:
     description: "Raptor tp6-3 on Firefox with Gecko Profiling"
     try-name: raptor-tp6-3-firefox-profiling
     treeherder-symbol: Rap-Prof(tp6-3)
     run-on-projects: ['mozilla-central', 'try']
     max-run-time: 900
+    tier: 2
     mozharness:
         extra-options:
             - --test=raptor-tp6-3
             - --gecko-profile
 
 raptor-tp6-3-chrome:
     description: "Raptor tp6-3 on Chrome"
     try-name: raptor-tp6-3-chrome
@@ -153,16 +156,17 @@ raptor-tp6-4-firefox:
             - --test=raptor-tp6-4
 
 raptor-tp6-4-firefox-profiling:
     description: "Raptor tp6-4 on Firefox with Gecko Profiling"
     try-name: raptor-tp6-4-firefox-profiling
     treeherder-symbol: Rap-Prof(tp6-4)
     run-on-projects: ['mozilla-central', 'try']
     max-run-time: 900
+    tier: 2
     mozharness:
         extra-options:
             - --test=raptor-tp6-4
             - --gecko-profile
 
 raptor-tp6-4-chrome:
     description: "Raptor tp6-4 on Chrome"
     try-name: raptor-tp6-4-chrome
@@ -187,16 +191,17 @@ raptor-tp6-5-firefox:
             - --test=raptor-tp6-5
 
 raptor-tp6-5-firefox-profiling:
     description: "Raptor tp6-5 on Firefox with Gecko Profiling"
     try-name: raptor-tp6-5-firefox-profiling
     treeherder-symbol: Rap-Prof(tp6-5)
     run-on-projects: ['mozilla-central', 'try']
     max-run-time: 900
+    tier: 2
     mozharness:
         extra-options:
             - --test=raptor-tp6-5
             - --gecko-profile
 
 raptor-tp6-5-chrome:
     description: "Raptor tp6-5 on Chrome"
     try-name: raptor-tp6-5-chrome
@@ -221,16 +226,17 @@ raptor-tp6-6-firefox:
             - --test=raptor-tp6-6
 
 raptor-tp6-6-firefox-profiling:
     description: "Raptor tp6-6 on Firefox with Gecko Profiling"
     try-name: raptor-tp6-6-firefox-profiling
     treeherder-symbol: Rap-Prof(tp6-6)
     run-on-projects: ['mozilla-central', 'try']
     max-run-time: 900
+    tier: 2
     mozharness:
         extra-options:
             - --test=raptor-tp6-6
             - --gecko-profile
 
 raptor-tp6-6-chrome:
     description: "Raptor tp6-6 on Chrome"
     try-name: raptor-tp6-6-chrome
@@ -306,16 +312,17 @@ raptor-speedometer-firefox:
             - --test=raptor-speedometer
 
 raptor-speedometer-firefox-profiling:
     description: "Raptor Speedometer on Firefox with Gecko Profiling"
     try-name: raptor-speedometer-firefox-profiling
     treeherder-symbol: Rap-Prof(sp)
     run-on-projects: ['mozilla-central', 'try']
     max-run-time: 900
+    tier: 2
     mozharness:
         extra-options:
             - --test=raptor-speedometer
             - --gecko-profile
 
 raptor-speedometer-geckoview:
     description: "Raptor Speedometer on Geckoview"
     try-name: raptor-speedometer-geckoview
@@ -390,16 +397,17 @@ raptor-stylebench-firefox:
             - --test=raptor-stylebench
 
 raptor-stylebench-firefox-profiling:
     description: "Raptor StyleBench on Firefox with Gecko Profiling"
     try-name: raptor-stylebench-firefox-profiling
     treeherder-symbol: Rap-Prof(sb)
     run-on-projects: ['mozilla-central', 'try']
     max-run-time: 900
+    tier: 2
     mozharness:
         extra-options:
             - --test=raptor-stylebench
             - --gecko-profile
 
 raptor-stylebench-chrome:
     description: "Raptor StyleBench on Chrome"
     try-name: raptor-stylebench-chrome
@@ -424,16 +432,17 @@ raptor-motionmark-htmlsuite-firefox:
             - --test=raptor-motionmark-htmlsuite
 
 raptor-motionmark-htmlsuite-firefox-profiling:
     description: "Raptor MotionMark HtmlSuite on Firefox with Gecko Profiling"
     try-name: raptor-motionmark-htmlsuite-firefox-profiling
     treeherder-symbol: Rap-Prof(mm-h)
     run-on-projects: ['mozilla-central', 'try']
     max-run-time: 900
+    tier: 2
     mozharness:
         extra-options:
             - --test=raptor-motionmark-htmlsuite
             - --gecko-profile
 
 raptor-motionmark-htmlsuite-chrome:
     description: "Raptor MotionMark HtmlSuite on Chrome"
     try-name: raptor-motionmark-htmlsuite-chrome
@@ -458,16 +467,17 @@ raptor-motionmark-animometer-firefox:
             - --test=raptor-motionmark-animometer
 
 raptor-motionmark-animometer-firefox-profiling:
     description: "Raptor MotionMark Animometer on Firefox with Gecko Profiling"
     try-name: raptor-motionmark-animometer-firefox-profiling
     treeherder-symbol: Rap-Prof(mm-a)
     run-on-projects: ['mozilla-central', 'try']
     max-run-time: 900
+    tier: 2
     mozharness:
         extra-options:
             - --test=raptor-motionmark-animometer
             - --gecko-profile
 
 raptor-motionmark-animometer-chrome:
     description: "Raptor MotionMark Animometer on Chrome"
     try-name: raptor-motionmark-animometer-chrome
@@ -492,16 +502,17 @@ raptor-webaudio-firefox:
             - --test=raptor-webaudio
 
 raptor-webaudio-firefox-profiling:
     description: "Raptor WebAudio on Firefox with Gecko Profiling"
     try-name: raptor-webaudio-firefox
     treeherder-symbol: Rap-Prof(wa)
     run-on-projects: ['mozilla-central', 'try']
     max-run-time: 900
+    tier: 2
     mozharness:
         extra-options:
             - --test=raptor-webaudio
             - --gecko-profile
 
 raptor-webaudio-chrome:
     description: "Raptor WebAudio on Chrome"
     try-name: raptor-webaudio-chrome
@@ -529,16 +540,17 @@ raptor-sunspider-firefox:
             - --test=raptor-sunspider
 
 raptor-sunspider-firefox-profiling:
     description: "Raptor SunSpider on Firefox with Gecko Profiling"
     try-name: raptor-sunspider-firefox-profiling
     treeherder-symbol: Rap-Prof(ss)
     run-on-projects: ['mozilla-central', 'try']
     max-run-time: 900
+    tier: 2
     mozharness:
         extra-options:
             - --test=raptor-sunspider
             - --gecko-profile
 
 raptor-sunspider-chrome:
     description: "Raptor SunSpider on Chrome"
     try-name: raptor-sunspider-chrome
@@ -566,16 +578,17 @@ raptor-unity-webgl-firefox:
             - unity-webgl
 
 raptor-unity-webgl-firefox-profiling:
     description: "Raptor Unity WebGL on Firefox with Gecko Profiling"
     try-name: raptor-unity-webgl-firefox-profiling
     treeherder-symbol: Rap-Prof(ugl)
     run-on-projects: ['mozilla-central', 'try']
     max-run-time: 900
+    tier: 2
     mozharness:
         extra-options:
             - --test=raptor-unity-webgl
             - --gecko-profile
     fetches:
         fetch:
             - unity-webgl
 
@@ -627,16 +640,17 @@ raptor-wasm-misc-firefox:
             - wasm-misc
 
 raptor-wasm-misc-firefox-profiling:
     description: "Raptor WASM Misc on Firefox with Gecko Profiling"
     try-name: raptor-wasm-misc-firefox-profiling
     treeherder-symbol: Rap-Prof(wm)
     run-on-projects: ['mozilla-central', 'try']
     max-run-time: 900
+    tier: 2
     mozharness:
         extra-options:
             - --test=raptor-wasm-misc
             - --gecko-profile
     fetches:
         fetch:
             - wasm-misc
 
@@ -656,16 +670,17 @@ raptor-wasm-misc-baseline-firefox:
             - wasm-misc
 
 raptor-wasm-misc-baseline-firefox-profiling:
     description: "Raptor WASM Misc on Firefox with baseline JIT and Gecko Profiling"
     try-name: raptor-wasm-misc-baseline-firefox-profiling
     treeherder-symbol: Rap-Prof(wm-b)
     run-on-projects: ['mozilla-central', 'try']
     max-run-time: 900
+    tier: 2
     mozharness:
         extra-options:
             - --test=raptor-wasm-misc-baseline
             - --gecko-profile
     fetches:
         fetch:
             - wasm-misc
 
@@ -685,16 +700,17 @@ raptor-wasm-misc-ion-firefox:
             - wasm-misc
 
 raptor-wasm-misc-ion-firefox-profiling:
     description: "Raptor WASM Misc on Firefox with ION Monkey and Gecko Profiling"
     try-name: raptor-wasm-misc-ion-firefox-profiling
     treeherder-symbol: Rap-Prof(wm-i)
     run-on-projects: ['mozilla-central', 'try']
     max-run-time: 900
+    tier: 2
     mozharness:
         extra-options:
             - --test=raptor-wasm-misc-ion
             - --gecko-profile
     fetches:
         fetch:
             - wasm-misc
 
@@ -733,16 +749,17 @@ raptor-assorted-dom-firefox:
             - assorted-dom
 
 raptor-assorted-dom-firefox-profiling:
     description: "Raptor Assorted-Dom on Firefox with Gecko Profiling"
     try-name: raptor-assorted-dom-firefox-profiling
     treeherder-symbol: Rap-Prof(dom)
     run-on-projects: ['mozilla-central', 'try']
     max-run-time: 900
+    tier: 2
     mozharness:
         extra-options:
             - --test=raptor-assorted-dom
             - --gecko-profile
     fetches:
         fetch:
             - assorted-dom
 
@@ -779,16 +796,17 @@ raptor-wasm-godot-firefox:
             - --test=raptor-wasm-godot
 
 raptor-wasm-godot-firefox-profiling:
     description: "Raptor Wasm Godot on Firefox with Gecko Profiling"
     try-name: raptor-wasm-godot-firefox-profiling
     treeherder-symbol: Rap-Prof(godot)
     run-on-projects: ['mozilla-central', 'try']
     max-run-time: 900
+    tier: 2
     mozharness:
         extra-options:
             - --test=raptor-wasm-godot
             - --gecko-profile
 
 raptor-wasm-godot-chrome:
     description: "Raptor Wasm Godot on Chrome"
     try-name: raptor-wasm-godot-chrome
@@ -819,16 +837,17 @@ raptor-wasm-godot-baseline-firefox:
             - --test=raptor-wasm-godot-baseline
 
 raptor-wasm-godot-baseline-firefox-profiling:
     description: "Raptor Wasm Godot on Firefox with baseline JIT and Gecko Profiling"
     try-name: raptor-wasm-godot-baseline-firefox-profiling
     treeherder-symbol: Rap-Prof(godot-b)
     run-on-projects: ['mozilla-central', 'try']
     max-run-time: 900
+    tier: 2
     mozharness:
         extra-options:
             - --test=raptor-wasm-godot-baseline
             - --gecko-profile
 
 raptor-wasm-godot-ion-firefox:
     description: "Raptor WASM godot on Firefox with ION Monkey"
     try-name: raptor-wasm-godot-ion-firefox
@@ -847,12 +866,13 @@ raptor-wasm-godot-ion-firefox:
             - --test=raptor-wasm-godot-ion
 
 raptor-wasm-godot-ion-firefox-profiling:
     description: "Raptor WASM godot on Firefox with ION Monkey and Gecko Profiling"
     try-name: raptor-wasm-godot-ion-firefox-profiling
     treeherder-symbol: Rap-Prof(godot-i)
     run-on-projects: ['mozilla-central', 'try']
     max-run-time: 900
+    tier: 2
     mozharness:
         extra-options:
             - --test=raptor-wasm-godot-ion
             - --gecko-profile
--- a/taskcluster/taskgraph/transforms/task.py
+++ b/taskcluster/taskgraph/transforms/task.py
@@ -139,18 +139,16 @@ task_description_schema = Schema({
             # sure a task is last in the index).
             int,
 
             # Rank is equal to the timestamp of the build_date.  This
             # option can be used to override the 'by-tier' behavior
             # for non-tier-1 tasks.
             'build_date',
         ),
-
-        'channel': optionally_keyed_by('project', basestring),
     },
 
     # The `run_on_projects` attribute, defaulting to "all".  This dictates the
     # projects on which this task should be included in the target task set.
     # See the attributes documentation for details.
     Optional('run-on-projects'): optionally_keyed_by('build-platform', [basestring]),
 
     # Like `run_on_projects`, `run-on-hg-branches` defaults to "all".
@@ -1552,21 +1550,16 @@ def add_release_index_routes(config, tas
     subs = config.params.copy()
     subs['build_number'] = str(release_config['build_number'])
     subs['revision'] = subs['head_rev']
     subs['underscore_version'] = release_config['version'].replace('.', '_')
     subs['product'] = index['product']
     subs['trust-domain'] = config.graph_config['trust-domain']
     subs['branch_rev'] = get_branch_rev(config)
     subs['branch'] = subs['project']
-    if 'channel' in index:
-        resolve_keyed_by(
-            index, 'channel', item_name=task['label'], project=config.params['project']
-        )
-        subs['channel'] = index['channel']
 
     for rt in task.get('routes', []):
         routes.append(rt.format(**subs))
 
     task['routes'] = routes
 
     return task
 
--- a/testing/marionette/dom.js
+++ b/testing/marionette/dom.js
@@ -1,14 +1,20 @@
 /* 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";
 
+ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+const {Log} = ChromeUtils.import("chrome://marionette/content/log.js", {});
+
+XPCOMUtils.defineLazyGetter(this, "logger", Log.get);
+
 this.EXPORTED_SYMBOLS = [
   "ContentEventObserverService",
   "WebElementEventTarget",
 ];
 
 /**
  * The ``EventTarget`` for web elements can be used to observe DOM
  * events in the content document.
@@ -96,17 +102,21 @@ class WebElementEventTarget {
     if (!(event.type in this.listeners)) {
       return;
     }
 
     event.target = this;
 
     let stack = this.listeners[event.type].slice(0);
     stack.forEach(listener => {
-      listener.call(this, event);
+      if (typeof listener.handleEvent == "function") {
+        listener.handleEvent(event);
+      } else {
+        listener(event);
+      }
 
       if (listener.once) {
         this.removeEventListener(event.type, listener);
       }
     });
   }
 
   receiveMessage({name, data, objects}) {
@@ -188,12 +198,13 @@ class ContentEventObserverService {
 
   * [Symbol.iterator]() {
     for (let ev of this.events) {
       yield ev;
     }
   }
 
   handleEvent({type, target}) {
+    logger.trace(`Received DOM event ${type}`);
     this.sendAsyncMessage("Marionette:DOM:OnEvent", {type}, {target});
   }
 }
 this.ContentEventObserverService = ContentEventObserverService;
--- a/testing/marionette/driver.js
+++ b/testing/marionette/driver.js
@@ -24,16 +24,19 @@ const {
 } = ChromeUtils.import("chrome://marionette/content/capabilities.js", {});
 ChromeUtils.import("chrome://marionette/content/capture.js");
 const {
   CertificateOverrideManager,
   InsecureSweepingOverride,
 } = ChromeUtils.import("chrome://marionette/content/cert.js", {});
 ChromeUtils.import("chrome://marionette/content/cookie.js");
 const {
+  WebElementEventTarget,
+} = ChromeUtils.import("chrome://marionette/content/dom.js", {});
+const {
   ChromeWebElement,
   element,
   WebElement,
 } = ChromeUtils.import("chrome://marionette/content/element.js", {});
 const {
   InsecureCertificateError,
   InvalidArgumentError,
   InvalidCookieDomainError,
@@ -3037,20 +3040,25 @@ GeckoDriver.prototype.minimizeWindow = a
   const win = assert.open(this.getCurrentWindow());
   await this._handleUserPrompts();
 
   if (WindowState.from(win.windowState) != WindowState.Minimized) {
     if (WindowState.from(win.windowState) == WindowState.Fullscreen) {
       await exitFullscreen(win);
     }
 
+    let cb;
+    let observer = new WebElementEventTarget(this.curBrowser.messageManager);
     await new TimedPromise(resolve => {
-      win.addEventListener("visibilitychange", resolve, {once: true});
+      cb = new DebounceCallback(resolve);
+      observer.addEventListener("visibilitychange", cb);
       win.minimize();
     }, {throws: null});
+    observer.removeEventListener("visibilitychange", cb);
+
     await new IdlePromise(win);
   }
 
   return this.curBrowser.rect;
 };
 
 /**
  * Synchronously maximizes the user agent window as if the user pressed
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_visibility.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_visibility.py
@@ -69,46 +69,51 @@ class TestVisibility(MarionetteTestCase)
     def testHiddenInputElementsAreNeverVisible(self):
         test_html = self.marionette.absolute_url("visibility.html")
         self.marionette.navigate(test_html)
 
         shown = self.marionette.find_element(By.NAME, "hidden")
 
         self.assertFalse(shown.is_displayed())
 
-    def testShouldSayElementsWithNegativeTransformAreNotDisplayed(self):
-        test_html = self.marionette.absolute_url("cssTransform.html")
-        self.marionette.navigate(test_html)
+    def test_elements_not_displayed_with_negative_transform(self):
+        self.marionette.navigate(inline("""
+            <div id="y" style="transform: translateY(-200%);">hidden</div>
+            <div id="x" style="transform: translateX(-200%);">hidden</div>
+        """))
 
-        elementX = self.marionette.find_element(By.ID, 'parentX')
-        self.assertFalse(elementX.is_displayed())
-        elementY = self.marionette.find_element(By.ID, 'parentY')
-        self.assertFalse(elementY.is_displayed())
+        element_x = self.marionette.find_element(By.ID, 'x')
+        self.assertFalse(element_x.is_displayed())
+        element_y = self.marionette.find_element(By.ID, 'y')
+        self.assertFalse(element_y.is_displayed())
 
-    def testShouldSayElementsWithParentWithNegativeTransformAreNotDisplayed(self):
-        test_html = self.marionette.absolute_url("cssTransform.html")
-        self.marionette.navigate(test_html)
+    def test_elements_not_displayed_with_parents_having_negative_transform(self):
+        self.marionette.navigate(inline("""
+            <div style="transform: translateY(-200%);"><p id="y">hidden</p></div>
+            <div style="transform: translateX(-200%);"><p id="x">hidden</p></div>
+        """))
 
-        elementX = self.marionette.find_element(By.ID, 'childX')
-        self.assertFalse(elementX.is_displayed())
-        elementY = self.marionette.find_element(By.ID, 'childY')
-        self.assertFalse(elementY.is_displayed())
-
-    def testShouldSayElementWithZeroTransformIsVisible(self):
-        test_html = self.marionette.absolute_url("cssTransform.html")
-        self.marionette.navigate(test_html)
+        element_x = self.marionette.find_element(By.ID, 'x')
+        self.assertFalse(element_x.is_displayed())
+        element_y = self.marionette.find_element(By.ID, 'y')
+        self.assertFalse(element_y.is_displayed())
 
-        zero_tranform = self.marionette.find_element(By.ID, 'zero-tranform')
-        self.assertTrue(zero_tranform.is_displayed())
+    def test_element_displayed_with_zero_transform(self):
+        self.marionette.navigate(inline("""
+            <div style="transform: translate(0px, 0px);">not hidden</div>
+        """))
+        element = self.marionette.find_element(By.TAG_NAME, 'div')
+        self.assertTrue(element.is_displayed())
 
-    def testShouldSayElementIsVisibleWhenItHasNegativeTransformButElementisntInANegativeSpace(self):
-        test_html = self.marionette.absolute_url("cssTransform2.html")
-        self.marionette.navigate(test_html)
-        negative_percent__tranform = self.marionette.find_element(By.ID, 'negative-percentage-transformY')
-        self.assertTrue(negative_percent__tranform.is_displayed())
+    def test_element_displayed_with_negative_transform_but_in_viewport(self):
+        self.marionette.navigate(inline("""
+            <div style="margin-top: 1em; transform: translateY(-75%);">not hidden</div>
+            """))
+        element = self.marionette.find_element(By.TAG_NAME, "div")
+        self.assertTrue(element.is_displayed())
 
     def testShouldSayElementIsInvisibleWhenOverflowXIsHiddenAndOutOfViewport(self):
         test_html = self.marionette.absolute_url("bug814037.html")
         self.marionette.navigate(test_html)
         overflow_x = self.marionette.find_element(By.ID, "assertMe2")
         self.assertFalse(overflow_x.is_displayed())
 
     def testShouldShowElementNotVisibleWithHiddenAttribute(self):
deleted file mode 100644
--- a/testing/marionette/harness/marionette_harness/www/cssTransform.html
+++ /dev/null
@@ -1,61 +0,0 @@
-<!DOCTYPE html>
-<style>
-#parentY {
-  transform: translateY(-10000px);
-  -webkit-transform: translateY(-10000px);
-  -o-transform: translateY(-10000px);
-  -ms-transform: translateY(-10000px);
-  -moz-transform: translateY(-10000px);
-}
-#parentX {
-  transform: translateX(-10000px);
-  -webkit-transform: translateX(-10000px);
-  -o-transform: translateX(-10000px);
-  -ms-transform: translateX(-10000px);
-  -moz-transform: translateX(-10000px);
-}
-#transformX {
-  transform: translateX(-10000px);
-  -webkit-transform: translateX(-10000px);
-  -o-transform: translateX(-10000px);
-  -ms-transform: translateX(-10000px);
-  -moz-transform: translateX(-10000px);
-}
-#transformY {
-  transform: translateY(-10000px);
-  -webkit-transform: translateY(-10000px);
-  -o-transform: translateY(-10000px);
-  -ms-transform: translateY(-10000px);
-  -moz-transform: translateY(-10000px);
-}
-
-#zero-transform {
-  transform: translateY(0px);
-  -webkit-transform: translateY(0px);
-  -o-transform: translateY(0px);
-  -ms-transform: translateY(0px);
-  -moz-transform: translateY(0px);
-  transform: translateX(0px);
-  -webkit-transform: translateX(0px);
-  -o-transform: translateX(0px);
-  -ms-transform: translateX(0px);
-  -moz-transform: translateX(0px);
-}
-</style>
-<div id='zero-tranform'>
-You shouldn't see anything other than this sentence on the page
-</div>
-<div id='parentY'>
-  I have a hidden child
-  <div id='childY'>
-    I am a hidden child
-  </div>
-</div>
-<div id='parentX'>
-  I have a hidden child
-  <div id='childX'>
-    I am a hidden child
-  </div>
-</div>
-<div id='transformX'>I am a hidden element </div>
-<div id='transformY'>I am a hidden element </div>
deleted file mode 100644
--- a/testing/marionette/harness/marionette_harness/www/cssTransform2.html
+++ /dev/null
@@ -1,20 +0,0 @@
-<!DOCTYPE html>
-<style>
-#negative-percentage-transformY{
-  transform: translateY(-75px);
-  -webkit-transform: translateY(-75%);
-  -o-transform: translateY(-75%);
-  -ms-transform: translateY(-75%);
-  -moz-transform: translateY(-75%);
-}
-.block {
-  display = block;
-}
-</style>
-<div class='block'>
-  <br/>
-</div>
-  <br/>
-<div class='block'>
-</div>
-<div id='negative-percentage-transformY'>I am not a hidden element </div>
--- a/testing/web-platform/meta/2dcontext/building-paths/__dir__.ini
+++ b/testing/web-platform/meta/2dcontext/building-paths/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Init, nsHostResolver::ResolveHost]
--- a/testing/web-platform/meta/2dcontext/drawing-rectangles-to-the-canvas/__dir__.ini
+++ b/testing/web-platform/meta/2dcontext/drawing-rectangles-to-the-canvas/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Init, nsHostResolver::ResolveHost]
--- a/testing/web-platform/meta/2dcontext/fill-and-stroke-styles/__dir__.ini
+++ b/testing/web-platform/meta/2dcontext/fill-and-stroke-styles/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: []
--- a/testing/web-platform/meta/2dcontext/path-objects/__dir__.ini
+++ b/testing/web-platform/meta/2dcontext/path-objects/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [mozilla::net::AddrInfo::AddrInfo, nsHostResolver::ResolveHost]
--- a/testing/web-platform/meta/2dcontext/shadows/__dir__.ini
+++ b/testing/web-platform/meta/2dcontext/shadows/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Init, PORT_Alloc_Util, nsHostResolver::ResolveHost]
--- a/testing/web-platform/meta/2dcontext/the-canvas-state/__dir__.ini
+++ b/testing/web-platform/meta/2dcontext/the-canvas-state/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Init, nsHostResolver::ResolveHost]
deleted file mode 100644
--- a/testing/web-platform/meta/FileAPI/file/__dir__.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-lsan-allowed: [Alloc, NewEmptyScopeData, __rdl_alloc, __rdl_realloc, js_pod_calloc, js_pod_malloc, js_pod_realloc, mozilla::dom::ChromeUtils::GenerateQI, mozilla::dom::Performance::CreateForMainThread]
-leak-threshold: [default:409600, tab:819200]
--- a/testing/web-platform/meta/IndexedDB/__dir__.ini
+++ b/testing/web-platform/meta/IndexedDB/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: []
--- a/testing/web-platform/meta/WebCryptoAPI/derive_bits_keys/__dir__.ini
+++ b/testing/web-platform/meta/WebCryptoAPI/derive_bits_keys/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: []
--- a/testing/web-platform/meta/content-security-policy/frame-ancestors/__dir__.ini
+++ b/testing/web-platform/meta/content-security-policy/frame-ancestors/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: []
--- a/testing/web-platform/meta/content-security-policy/img-src/__dir__.ini
+++ b/testing/web-platform/meta/content-security-policy/img-src/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: []
--- a/testing/web-platform/meta/content-security-policy/reporting/__dir__.ini
+++ b/testing/web-platform/meta/content-security-policy/reporting/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: []
--- a/testing/web-platform/meta/content-security-policy/script-src/__dir__.ini
+++ b/testing/web-platform/meta/content-security-policy/script-src/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: []
--- a/testing/web-platform/meta/content-security-policy/securitypolicyviolation/__dir__.ini
+++ b/testing/web-platform/meta/content-security-policy/securitypolicyviolation/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: []
--- a/testing/web-platform/meta/content-security-policy/style-src/__dir__.ini
+++ b/testing/web-platform/meta/content-security-policy/style-src/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: []
--- a/testing/web-platform/meta/css/CSS2/normal-flow/__dir__.ini
+++ b/testing/web-platform/meta/css/CSS2/normal-flow/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: []
--- a/testing/web-platform/meta/css/CSS2/selectors/__dir__.ini
+++ b/testing/web-platform/meta/css/CSS2/selectors/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: []
--- a/testing/web-platform/meta/css/css-fonts/font-display/__dir__.ini
+++ b/testing/web-platform/meta/css/css-fonts/font-display/__dir__.ini
@@ -1,2 +1,1 @@
-lsan-allowed: [Alloc, AllocateProtoAndIfaceCache, EntrySlotOrCreate, Realloc, alloc_system::platform::_$LT$impl$u20$core..alloc..GlobalAlloc$u20$for$u20$alloc_system..System$GT$::alloc, alloc_system::platform::_$LT$impl$u20$core..alloc..GlobalAlloc$u20$for$u20$alloc_system..System$GT$::realloc, mozilla::dom::ChromeUtils::GenerateQI, mozilla::dom::Performance::CreateForMainThread, mozilla::dom::PerformanceMainThread::CreateNavigationTimingEntry, mozilla::net::nsStandardURL::TemplatedMutator]
 leak-threshold: [tab:358400]
--- a/testing/web-platform/meta/css/css-grid/alignment/__dir__.ini
+++ b/testing/web-platform/meta/css/css-grid/alignment/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: []
--- a/testing/web-platform/meta/css/css-text/i18n/__dir__.ini
+++ b/testing/web-platform/meta/css/css-text/i18n/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: []
--- a/testing/web-platform/meta/css/css-typed-om/the-stylepropertymap/properties/__dir__.ini
+++ b/testing/web-platform/meta/css/css-typed-om/the-stylepropertymap/properties/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: []
--- a/testing/web-platform/meta/css/css-variables/__dir__.ini
+++ b/testing/web-platform/meta/css/css-variables/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: []
--- a/testing/web-platform/meta/css/css-writing-modes/__dir__.ini
+++ b/testing/web-platform/meta/css/css-writing-modes/__dir__.ini
@@ -1,2 +1,1 @@
-lsan-allowed: []
 leak-threshold: [default:51200]
--- a/testing/web-platform/meta/css/cssom-view/__dir__.ini
+++ b/testing/web-platform/meta/css/cssom-view/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: []
--- a/testing/web-platform/meta/custom-elements/__dir__.ini
+++ b/testing/web-platform/meta/custom-elements/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [EntrySlotOrCreate, Malloc, NewEmptyScopeData, js_new, js_pod_calloc, js_pod_malloc, js_pod_realloc, mozilla::BackgroundHangThread::ReportHang]
--- a/testing/web-platform/meta/custom-elements/reactions/__dir__.ini
+++ b/testing/web-platform/meta/custom-elements/reactions/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [EntrySlotOrCreate, Malloc, NewEmptyScopeData, js_new, js_pod_calloc, js_pod_malloc, js_pod_realloc, mozilla::BackgroundHangThread::ReportHang]
--- a/testing/web-platform/meta/dom/nodes/__dir__.ini
+++ b/testing/web-platform/meta/dom/nodes/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: []
--- a/testing/web-platform/meta/dom/ranges/__dir__.ini
+++ b/testing/web-platform/meta/dom/ranges/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: []
--- a/testing/web-platform/meta/editing/run/__dir__.ini
+++ b/testing/web-platform/meta/editing/run/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: []
--- a/testing/web-platform/meta/encoding/legacy-mb-japanese/euc-jp/__dir__.ini
+++ b/testing/web-platform/meta/encoding/legacy-mb-japanese/euc-jp/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: []
--- a/testing/web-platform/meta/encoding/legacy-mb-japanese/iso-2022-jp/__dir__.ini
+++ b/testing/web-platform/meta/encoding/legacy-mb-japanese/iso-2022-jp/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: []
--- a/testing/web-platform/meta/encoding/legacy-mb-japanese/shift_jis/__dir__.ini
+++ b/testing/web-platform/meta/encoding/legacy-mb-japanese/shift_jis/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: []
--- a/testing/web-platform/meta/encoding/legacy-mb-korean/euc-kr/__dir__.ini
+++ b/testing/web-platform/meta/encoding/legacy-mb-korean/euc-kr/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: []
--- a/testing/web-platform/meta/encoding/legacy-mb-tchinese/big5/__dir__.ini
+++ b/testing/web-platform/meta/encoding/legacy-mb-tchinese/big5/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: []
--- a/testing/web-platform/meta/feature-policy/__dir__.ini
+++ b/testing/web-platform/meta/feature-policy/__dir__.ini
@@ -1,2 +1,1 @@
 prefs: [dom.security.featurePolicy.enabled:true, dom.payments.request.enabled:true, dom.reporting.enabled:true, dom.reporting.featurePolicy.enabled:true, dom.security.featurePolicy.header.enabled:true, dom.security.featurePolicy.webidl.enabled:true, dom.webmidi.enabled:true, dom.vr.enabled:true]
-lsan-allowed: []
--- a/testing/web-platform/meta/fetch/api/abort/__dir__.ini
+++ b/testing/web-platform/meta/fetch/api/abort/__dir__.ini
@@ -1,3 +1,3 @@
 prefs: [javascript.options.streams:true]
-lsan-allowed: [Alloc, AllocateProtoAndIfaceCache, NewEmptyScopeData, __rdl_alloc, __rdl_realloc, js_new, js_pod_calloc, js_pod_malloc, js_pod_realloc, mozilla::dom::ChromeUtils::GenerateQI, mozilla::dom::DOMException::Create, mozilla::dom::FetchStream::Create, mozilla::dom::Performance::CreateForMainThread]
+lsan-allowed: [nsTSubstring<char>::StartBulkWriteImpl, Gecko_StartBulkWriteCString, nsstring::nsACString::start_bulk_write_impl]
 leak-threshold: [default:51200]
--- a/testing/web-platform/meta/fetch/range/__dir__.ini
+++ b/testing/web-platform/meta/fetch/range/__dir__.ini
@@ -1,2 +1,1 @@
-lsan-allowed: [Alloc, Create, CreateInner, FetchDriverObserver, Malloc, NewPage, PLDHashTable::Add, Realloc, allocate, mozilla::ThrottledEventQueue::Create, mozilla::dom::InternalRequest::GetRequestConstructorCopy, mozilla::dom::PerformanceStorageWorker::Create, mozilla::dom::PromiseWorkerProxy::Create, mozilla::dom::WorkerCSPEventListener::Create, mozilla::dom::WorkerFetchResolver::Create, mozilla::dom::WorkerLoadInfo::InterfaceRequestor::InterfaceRequestor, nsSegmentedBuffer::AppendNewSegment, nsSupportsWeakReference::GetWeakReference]
 leak-threshold: [default:51200, tab:51200]
--- a/testing/web-platform/meta/geolocation-API/__dir__.ini
+++ b/testing/web-platform/meta/geolocation-API/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: []
--- a/testing/web-platform/meta/html/browsers/history/the-history-interface/__dir__.ini
+++ b/testing/web-platform/meta/html/browsers/history/the-history-interface/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: []
--- a/testing/web-platform/meta/html/browsers/history/the-location-interface/__dir__.ini
+++ b/testing/web-platform/meta/html/browsers/history/the-location-interface/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: []
--- a/testing/web-platform/meta/html/browsers/the-window-object/apis-for-creating-and-navigating-browsing-contexts-by-name/__dir__.ini
+++ b/testing/web-platform/meta/html/browsers/the-window-object/apis-for-creating-and-navigating-browsing-contexts-by-name/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: []
--- a/testing/web-platform/meta/html/dom/__dir__.ini
+++ b/testing/web-platform/meta/html/dom/__dir__.ini
@@ -1,2 +1,1 @@
-lsan-allowed: []
 leak-threshold: [default:51200]
--- a/testing/web-platform/meta/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/__dir__.ini
+++ b/testing/web-platform/meta/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: []
--- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/__dir__.ini
+++ b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/__dir__.ini
@@ -1,2 +1,1 @@
 prefs: [dom.security.featurePolicy.enabled:true, dom.security.featurePolicy.header.enabled:true, dom.security.featurePolicy.webidl.enabled:true]
-lsan-allowed: []
--- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/loading-the-media-resource/__dir__.ini
+++ b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/loading-the-media-resource/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: []
--- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/__dir__.ini
+++ b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: []
--- a/testing/web-platform/meta/html/semantics/forms/constraints/__dir__.ini
+++ b/testing/web-platform/meta/html/semantics/forms/constraints/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, XPCWrappedNative::GetNewOrUsed, js_new, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
deleted file mode 100644
--- a/testing/web-platform/meta/html/semantics/forms/form-submission-0/__dir__.ini
+++ /dev/null
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, NewEmptyScopeData, __rdl_alloc, __rdl_realloc, js_new, js_pod_calloc, js_pod_malloc, js_pod_realloc, mozilla::dom::ChromeUtils::GenerateQI, mozilla::dom::Performance::CreateForMainThread]
--- a/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/__dir__.ini
+++ b/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: []
--- a/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/execution-timing/__dir__.ini
+++ b/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/execution-timing/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: []
--- a/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/__dir__.ini
+++ b/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/__dir__.ini
@@ -1,2 +1,1 @@
-lsan-allowed: [Init, nsHostResolver::ResolveHost]
 prefs: [javascript.options.dynamicImport:true, security.csp.experimentalEnabled:true]
--- a/testing/web-platform/meta/html/syntax/parsing/__dir__.ini
+++ b/testing/web-platform/meta/html/syntax/parsing/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: []
--- a/testing/web-platform/meta/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/__dir__.ini
+++ b/testing/web-platform/meta/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: []
--- a/testing/web-platform/meta/infrastructure/assumptions/__dir__.ini
+++ b/testing/web-platform/meta/infrastructure/assumptions/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, XPCWrappedNative::GetNewOrUsed, js_new, js_pod_calloc, js_pod_malloc, js_pod_realloc, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/js/builtins/__dir__.ini
+++ b/testing/web-platform/meta/js/builtins/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, XPCWrappedNative::GetNewOrUsed, js_new, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/keyboard-lock/__dir__.ini
+++ b/testing/web-platform/meta/keyboard-lock/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: []
--- a/testing/web-platform/meta/longtask-timing/__dir__.ini
+++ b/testing/web-platform/meta/longtask-timing/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, XPCWrappedNative::GetNewOrUsed, js_new, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/mathml/relations/css-styling/__dir__.ini
+++ b/testing/web-platform/meta/mathml/relations/css-styling/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, NewEmptyScopeData, XPCWrappedNative::GetNewOrUsed, js_new, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/media-capabilities/__dir__.ini
+++ b/testing/web-platform/meta/media-capabilities/__dir__.ini
@@ -1,2 +1,1 @@
 prefs: [media.media-capabilities.enabled:true,media.media-capabilities.screen.enabled:true]
-lsan-allowed: [Alloc, XPCWrappedNative::GetNewOrUsed, js_new, js_pod_calloc, js_pod_malloc, js_pod_realloc, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/media-source/__dir__.ini
+++ b/testing/web-platform/meta/media-source/__dir__.ini
@@ -1,3 +1,2 @@
 prefs: [media.mediasource.experimental.enabled:true]
-lsan-allowed: [Alloc, NewEmptyScopeData, XPCWrappedNative::GetNewOrUsed, js_new, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
 leak-threshold: [default: 51200]
--- a/testing/web-platform/meta/mediacapture-streams/__dir__.ini
+++ b/testing/web-platform/meta/mediacapture-streams/__dir__.ini
@@ -1,2 +1,1 @@
 prefs: [media.navigator.permission.disabled:true, media.navigator.streams.fake:true, dom.security.featurePolicy.enabled:true, dom.security.featurePolicy.header.enabled:true, dom.security.featurePolicy.webidl.enabled:true]
-lsan-allowed: [Alloc, NewEmptyScopeData, XPCWrappedNative::GetNewOrUsed, js_new, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/mimesniff/mime-types/__dir__.ini
+++ b/testing/web-platform/meta/mimesniff/mime-types/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, XPCWrappedNative::GetNewOrUsed, js_new, js_pod_calloc, js_pod_realloc, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/mixed-content/__dir__.ini
+++ b/testing/web-platform/meta/mixed-content/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, XPCWrappedNative::GetNewOrUsed, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/offscreen-canvas/fill-and-stroke-styles/__dir__.ini
+++ b/testing/web-platform/meta/offscreen-canvas/fill-and-stroke-styles/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: []
--- a/testing/web-platform/meta/offscreen-canvas/path-objects/__dir__.ini
+++ b/testing/web-platform/meta/offscreen-canvas/path-objects/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: []
--- a/testing/web-platform/meta/offscreen-canvas/the-offscreen-canvas/__dir__.ini
+++ b/testing/web-platform/meta/offscreen-canvas/the-offscreen-canvas/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: []
--- a/testing/web-platform/meta/paint-timing/__dir__.ini
+++ b/testing/web-platform/meta/paint-timing/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: []
--- a/testing/web-platform/meta/performance-timeline/__dir__.ini
+++ b/testing/web-platform/meta/performance-timeline/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, NewEmptyScopeData, __rdl_alloc, __rdl_realloc, js_new, js_pod_calloc, js_pod_malloc, js_pod_realloc, mozilla::dom::ChromeUtils::GenerateQI, mozilla::dom::Performance::CreateForMainThread, mozilla::dom::PerformanceObserver::Constructor, xpc::CreateSandboxObject]
--- a/testing/web-platform/meta/picture-in-picture/__dir__.ini
+++ b/testing/web-platform/meta/picture-in-picture/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: []
--- a/testing/web-platform/meta/referrer-policy/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, AllocateProtoAndIfaceCache, NewEmptyScopeData, XPCWrappedNative::GetNewOrUsed, js_new, js_pod_calloc, js_pod_malloc, js_pod_realloc, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/no-referrer-when-downgrade/attr-referrer/cross-origin/http-https/iframe-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/no-referrer-when-downgrade/attr-referrer/cross-origin/http-https/iframe-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, XPCWrappedNative::GetNewOrUsed, js_new, js_pod_calloc, js_pod_malloc, js_pod_realloc, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/no-referrer-when-downgrade/attr-referrer/same-origin/http-http/iframe-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/no-referrer-when-downgrade/attr-referrer/same-origin/http-http/iframe-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, XPCWrappedNative::GetNewOrUsed, js_new, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
deleted file mode 100644
--- a/testing/web-platform/meta/referrer-policy/no-referrer-when-downgrade/attr-referrer/same-origin/http-http/img-tag/__dir__.ini
+++ /dev/null
@@ -1,1 +0,0 @@
-lsan-allowed: [AllocateProtoAndIfaceCache, NewEmptyScopeData, XPCWrappedNative::GetNewOrUsed, js_pod_calloc, js_pod_malloc, js_pod_realloc, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc, Alloc, AllocateProtoAndIfaceCache, NewEmptyScopeData, XPCWrappedNative::GetNewOrUsed, js_new, js_pod_calloc, js_pod_malloc, js_pod_realloc, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/no-referrer-when-downgrade/attr-referrer/same-origin/http-https/iframe-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/no-referrer-when-downgrade/attr-referrer/same-origin/http-https/iframe-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, XPCWrappedNative::GetNewOrUsed, js_new, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/no-referrer-when-downgrade/http-rp/cross-origin/http-http/fetch-request/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/no-referrer-when-downgrade/http-rp/cross-origin/http-http/fetch-request/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, XPCWrappedNative::GetNewOrUsed, js_new, js_pod_calloc, js_pod_malloc, js_pod_realloc, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/no-referrer-when-downgrade/http-rp/cross-origin/http-http/img-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/no-referrer-when-downgrade/http-rp/cross-origin/http-http/img-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, XPCWrappedNative::GetNewOrUsed, js_new, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/no-referrer-when-downgrade/http-rp/same-origin/http-http/script-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/no-referrer-when-downgrade/http-rp/same-origin/http-http/script-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, XPCWrappedNative::GetNewOrUsed, js_new, js_pod_calloc, js_pod_malloc, js_pod_realloc, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/no-referrer-when-downgrade/http-rp/same-origin/http-https/script-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/no-referrer-when-downgrade/http-rp/same-origin/http-https/script-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, XPCWrappedNative::GetNewOrUsed, js_new, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/no-referrer-when-downgrade/meta-referrer/cross-origin/http-http/iframe-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/no-referrer-when-downgrade/meta-referrer/cross-origin/http-http/iframe-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, XPCWrappedNative::GetNewOrUsed, js_new, js_pod_calloc, js_pod_malloc, js_pod_realloc, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/no-referrer-when-downgrade/meta-referrer/cross-origin/http-https/iframe-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/no-referrer-when-downgrade/meta-referrer/cross-origin/http-https/iframe-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [XPCWrappedNative::GetNewOrUsed, mozilla::BasePrincipal::CreateCodebasePrincipal, nsStringBuffer::Alloc, Alloc, NewEmptyScopeData, XPCWrappedNative::GetNewOrUsed, js_new, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/no-referrer-when-downgrade/meta-referrer/cross-origin/http-https/script-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/no-referrer-when-downgrade/meta-referrer/cross-origin/http-https/script-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, XPCWrappedNative::GetNewOrUsed, js_new, js_pod_calloc, js_pod_realloc, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/no-referrer-when-downgrade/meta-referrer/cross-origin/http-https/xhr-request/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/no-referrer-when-downgrade/meta-referrer/cross-origin/http-https/xhr-request/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, XPCWrappedNative::GetNewOrUsed, js_new, js_pod_calloc, js_pod_malloc, js_pod_realloc, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/no-referrer-when-downgrade/meta-referrer/same-origin/http-http/iframe-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/no-referrer-when-downgrade/meta-referrer/same-origin/http-http/iframe-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, XPCWrappedNative::GetNewOrUsed, js_new, js_pod_calloc, js_pod_realloc, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/no-referrer-when-downgrade/meta-referrer/same-origin/http-http/img-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/no-referrer-when-downgrade/meta-referrer/same-origin/http-http/img-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, XPCWrappedNative::GetNewOrUsed, js_new, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/no-referrer-when-downgrade/meta-referrer/same-origin/http-https/fetch-request/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/no-referrer-when-downgrade/meta-referrer/same-origin/http-https/fetch-request/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, XPCWrappedNative::GetNewOrUsed, js_new, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/no-referrer-when-downgrade/meta-referrer/same-origin/http-https/iframe-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/no-referrer-when-downgrade/meta-referrer/same-origin/http-https/iframe-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, NewEmptyScopeData, XPCWrappedNative::GetNewOrUsed, js_new, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/no-referrer/attr-referrer/cross-origin/http-https/iframe-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/no-referrer/attr-referrer/cross-origin/http-https/iframe-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, XPCWrappedNative::GetNewOrUsed, js_new, js_pod_calloc, js_pod_realloc, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/no-referrer/attr-referrer/same-origin/http-http/iframe-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/no-referrer/attr-referrer/same-origin/http-http/iframe-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, AllocateProtoAndIfaceCache, XPCWrappedNative::GetNewOrUsed, js_new, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/no-referrer/attr-referrer/same-origin/http-http/img-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/no-referrer/attr-referrer/same-origin/http-http/img-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, AllocateProtoAndIfaceCache, XPCWrappedNative::GetNewOrUsed, js_new, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/no-referrer/attr-referrer/same-origin/http-https/iframe-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/no-referrer/attr-referrer/same-origin/http-https/iframe-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, XPCWrappedNative::GetNewOrUsed, js_new, js_pod_calloc, js_pod_malloc, js_pod_realloc, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/no-referrer/http-rp/cross-origin/http-http/fetch-request/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/no-referrer/http-rp/cross-origin/http-http/fetch-request/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, AllocateProtoAndIfaceCache, XPCWrappedNative::GetNewOrUsed, js_new, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/no-referrer/http-rp/cross-origin/http-https/fetch-request/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/no-referrer/http-rp/cross-origin/http-https/fetch-request/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, XPCWrappedNative::GetNewOrUsed, js_new, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/no-referrer/http-rp/cross-origin/http-https/iframe-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/no-referrer/http-rp/cross-origin/http-https/iframe-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, NewEmptyScopeData, XPCWrappedNative::GetNewOrUsed, js_new, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/no-referrer/http-rp/cross-origin/http-https/img-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/no-referrer/http-rp/cross-origin/http-https/img-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, AllocateProtoAndIfaceCache, XPCWrappedNative::GetNewOrUsed, js_new, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/no-referrer/http-rp/cross-origin/http-https/script-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/no-referrer/http-rp/cross-origin/http-https/script-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [XPCWrappedNative::GetNewOrUsed, js_pod_calloc, js_pod_realloc, mozilla::dom::ChromeUtils::GenerateQI, Alloc, XPCWrappedNative::GetNewOrUsed, js_new, js_pod_calloc, js_pod_malloc, js_pod_realloc, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/no-referrer/http-rp/cross-origin/http-https/xhr-request/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/no-referrer/http-rp/cross-origin/http-https/xhr-request/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, XPCWrappedNative::GetNewOrUsed, js_new, js_pod_calloc, js_pod_realloc, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/no-referrer/http-rp/same-origin/http-http/iframe-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/no-referrer/http-rp/same-origin/http-http/iframe-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, NewEmptyScopeData, XPCWrappedNative::GetNewOrUsed, js_new, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/no-referrer/http-rp/same-origin/http-http/img-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/no-referrer/http-rp/same-origin/http-http/img-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, XPCWrappedNative::GetNewOrUsed, js_new, js_pod_calloc, js_pod_malloc, js_pod_realloc, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/no-referrer/http-rp/same-origin/http-https/img-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/no-referrer/http-rp/same-origin/http-https/img-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, mozilla::BasePrincipal::CreateCodebasePrincipal, Alloc, XPCWrappedNative::GetNewOrUsed, mozilla::BasePrincipal::CreateCodebasePrincipal, nsStringBuffer::Alloc, Alloc, NewEmptyScopeData, XPCWrappedNative::GetNewOrUsed, js_new, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/no-referrer/meta-referrer/cross-origin/http-http/iframe-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/no-referrer/meta-referrer/cross-origin/http-http/iframe-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, XPCWrappedNative::GetNewOrUsed, js_new, js_pod_calloc, js_pod_malloc, js_pod_realloc, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/no-referrer/meta-referrer/same-origin/http-http/fetch-request/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/no-referrer/meta-referrer/same-origin/http-http/fetch-request/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, XPCWrappedNative::GetNewOrUsed, js_new, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/no-referrer/meta-referrer/same-origin/http-http/iframe-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/no-referrer/meta-referrer/same-origin/http-http/iframe-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [XPCWrappedNative::GetNewOrUsed, mozilla::BasePrincipal::CreateCodebasePrincipal, nsStringBuffer::Alloc, Alloc, NewEmptyScopeData, XPCWrappedNative::GetNewOrUsed, js_new, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/no-referrer/meta-referrer/same-origin/http-http/img-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/no-referrer/meta-referrer/same-origin/http-http/img-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [js_new, mozilla::dom::ChromeUtils::GenerateQI, Alloc, js_new, mozilla::dom::ChromeUtils::GenerateQI, Alloc, XPCWrappedNative::GetNewOrUsed, js_new, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/no-referrer/meta-referrer/same-origin/http-https/iframe-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/no-referrer/meta-referrer/same-origin/http-https/iframe-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, XPCWrappedNative::GetNewOrUsed, js_new, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/no-referrer/meta-referrer/same-origin/http-https/img-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/no-referrer/meta-referrer/same-origin/http-https/img-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, AllocateProtoAndIfaceCache, XPCWrappedNative::GetNewOrUsed, js_new, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/origin-when-cross-origin/attr-referrer/cross-origin/http-http/img-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/origin-when-cross-origin/attr-referrer/cross-origin/http-http/img-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, NewEmptyScopeData, XPCWrappedNative::GetNewOrUsed, js_new, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/origin-when-cross-origin/attr-referrer/cross-origin/http-https/iframe-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/origin-when-cross-origin/attr-referrer/cross-origin/http-https/iframe-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, XPCWrappedNative::GetNewOrUsed, js_new, js_pod_calloc, js_pod_realloc, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/origin-when-cross-origin/attr-referrer/same-origin/http-http/iframe-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/origin-when-cross-origin/attr-referrer/same-origin/http-http/iframe-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, AllocateProtoAndIfaceCache, XPCWrappedNative::GetNewOrUsed, js_new, js_pod_calloc, js_pod_malloc, js_pod_realloc, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/origin-when-cross-origin/attr-referrer/same-origin/http-https/iframe-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/origin-when-cross-origin/attr-referrer/same-origin/http-https/iframe-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, XPCWrappedNative::GetNewOrUsed, js_new, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/cross-origin/http-http/iframe-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/cross-origin/http-http/iframe-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, XPCWrappedNative::GetNewOrUsed, js_new, js_pod_calloc, js_pod_malloc, js_pod_realloc, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/cross-origin/http-http/img-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/cross-origin/http-http/img-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [js_new, mozilla::dom::ChromeUtils::GenerateQI, Alloc, js_new, mozilla::dom::ChromeUtils::GenerateQI, Alloc, XPCWrappedNative::GetNewOrUsed, js_new, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/cross-origin/http-https/fetch-request/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/cross-origin/http-https/fetch-request/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [XPCWrappedNative::GetNewOrUsed, js_pod_calloc, js_pod_realloc, mozilla::dom::ChromeUtils::GenerateQI, Alloc, XPCWrappedNative::GetNewOrUsed, js_new, js_pod_calloc, js_pod_malloc, js_pod_realloc, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/cross-origin/http-https/iframe-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/cross-origin/http-https/iframe-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, NewEmptyScopeData, XPCWrappedNative::GetNewOrUsed, js_new, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/cross-origin/http-https/img-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/cross-origin/http-https/img-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, AllocateProtoAndIfaceCache, XPCWrappedNative::GetNewOrUsed, js_new, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/same-origin/http-http/iframe-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/same-origin/http-http/iframe-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, XPCWrappedNative::GetNewOrUsed, js_new, js_pod_calloc, js_pod_malloc, js_pod_realloc, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/same-origin/http-http/script-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/same-origin/http-http/script-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, XPCWrappedNative::GetNewOrUsed, js_new, js_pod_calloc, js_pod_realloc, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/same-origin/http-https/xhr-request/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/same-origin/http-https/xhr-request/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, XPCWrappedNative::GetNewOrUsed, js_new, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/origin-when-cross-origin/meta-referrer/cross-origin/http-http/iframe-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/origin-when-cross-origin/meta-referrer/cross-origin/http-http/iframe-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, XPCWrappedNative::GetNewOrUsed, js_new, js_pod_calloc, js_pod_malloc, js_pod_realloc, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/origin-when-cross-origin/meta-referrer/cross-origin/http-http/xhr-request/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/origin-when-cross-origin/meta-referrer/cross-origin/http-http/xhr-request/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, XPCWrappedNative::GetNewOrUsed, js_new, js_pod_calloc, js_pod_malloc, js_pod_realloc, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/origin-when-cross-origin/meta-referrer/cross-origin/http-https/fetch-request/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/origin-when-cross-origin/meta-referrer/cross-origin/http-https/fetch-request/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, XPCWrappedNative::GetNewOrUsed, js_new, js_pod_calloc, js_pod_malloc, js_pod_realloc, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/origin-when-cross-origin/meta-referrer/cross-origin/http-https/img-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/origin-when-cross-origin/meta-referrer/cross-origin/http-https/img-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, XPCWrappedNative::GetNewOrUsed, js_new, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/origin-when-cross-origin/meta-referrer/cross-origin/http-https/xhr-request/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/origin-when-cross-origin/meta-referrer/cross-origin/http-https/xhr-request/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, XPCWrappedNative::GetNewOrUsed, js_new, mozilla::BasePrincipal::CreateCodebasePrincipal, nsStringBuffer::Alloc, Alloc, NewEmptyScopeData, XPCWrappedNative::GetNewOrUsed, js_new, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
deleted file mode 100644
--- a/testing/web-platform/meta/referrer-policy/origin-when-cross-origin/meta-referrer/same-origin/http-http/iframe-tag/__dir__.ini
+++ /dev/null
@@ -1,1 +0,0 @@
-lsan-allowed: [mozilla::dom::ChromeUtils::GenerateQI, Alloc, XPCWrappedNative::GetNewOrUsed, js_new, js_pod_calloc, js_pod_malloc, js_pod_realloc, mozilla::dom::ChromeUtils::GenerateQI, Alloc, NewEmptyScopeData, XPCWrappedNative::GetNewOrUsed, js_new, js_pod_calloc, js_pod_malloc, js_pod_realloc, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/origin-when-cross-origin/meta-referrer/same-origin/http-http/img-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/origin-when-cross-origin/meta-referrer/same-origin/http-http/img-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [XPCWrappedNative::GetNewOrUsed, mozilla::BasePrincipal::CreateCodebasePrincipal, nsStringBuffer::Alloc, Alloc, NewEmptyScopeData, XPCWrappedNative::GetNewOrUsed, js_new, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/origin-when-cross-origin/meta-referrer/same-origin/http-https/fetch-request/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/origin-when-cross-origin/meta-referrer/same-origin/http-https/fetch-request/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, XPCWrappedNative::GetNewOrUsed, js_new, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/origin/attr-referrer/cross-origin/http-http/img-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/origin/attr-referrer/cross-origin/http-http/img-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, XPCWrappedNative::GetNewOrUsed, js_new, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/origin/attr-referrer/cross-origin/http-https/img-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/origin/attr-referrer/cross-origin/http-https/img-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, XPCWrappedNative::GetNewOrUsed, js_new, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/origin/http-rp/cross-origin/http-https/iframe-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/origin/http-rp/cross-origin/http-https/iframe-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, XPCWrappedNative::GetNewOrUsed, js_new, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/origin/http-rp/cross-origin/http-https/xhr-request/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/origin/http-rp/cross-origin/http-https/xhr-request/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, XPCWrappedNative::GetNewOrUsed, js_new, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/origin/http-rp/same-origin/http-http/fetch-request/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/origin/http-rp/same-origin/http-http/fetch-request/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, NewEmptyScopeData, XPCWrappedNative::GetNewOrUsed, js_new, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/origin/http-rp/same-origin/http-http/iframe-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/origin/http-rp/same-origin/http-http/iframe-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, XPCWrappedNative::GetNewOrUsed, js_new, js_pod_calloc, js_pod_malloc, js_pod_realloc, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/origin/http-rp/same-origin/http-http/script-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/origin/http-rp/same-origin/http-http/script-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, XPCWrappedNative::GetNewOrUsed, js_new, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/origin/http-rp/same-origin/http-https/fetch-request/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/origin/http-rp/same-origin/http-https/fetch-request/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, XPCWrappedNative::GetNewOrUsed, js_new, js_pod_calloc, js_pod_malloc, js_pod_realloc, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/origin/http-rp/same-origin/http-https/iframe-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/origin/http-rp/same-origin/http-https/iframe-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [XPCWrappedNative::GetNewOrUsed, mozilla::BasePrincipal::CreateCodebasePrincipal, nsStringBuffer::Alloc, Alloc, NewEmptyScopeData, XPCWrappedNative::GetNewOrUsed, js_new, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/origin/http-rp/same-origin/http-https/script-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/origin/http-rp/same-origin/http-https/script-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, XPCWrappedNative::GetNewOrUsed, js_new, js_pod_calloc, js_pod_malloc, js_pod_realloc, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/origin/meta-referrer/cross-origin/http-http/iframe-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/origin/meta-referrer/cross-origin/http-http/iframe-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, NewEmptyScopeData, XPCWrappedNative::GetNewOrUsed, js_new, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/origin/meta-referrer/cross-origin/http-https/script-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/origin/meta-referrer/cross-origin/http-https/script-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, NewEmptyScopeData, XPCWrappedNative::GetNewOrUsed, js_new, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/origin/meta-referrer/same-origin/http-http/img-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/origin/meta-referrer/same-origin/http-http/img-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, XPCWrappedNative::GetNewOrUsed, js_new, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/origin/meta-referrer/same-origin/http-http/script-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/origin/meta-referrer/same-origin/http-http/script-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, NewEmptyScopeData, XPCWrappedNative::GetNewOrUsed, js_new, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/origin/meta-referrer/same-origin/http-http/xhr-request/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/origin/meta-referrer/same-origin/http-http/xhr-request/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, XPCWrappedNative::GetNewOrUsed, js_new, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/origin/meta-referrer/same-origin/http-https/xhr-request/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/origin/meta-referrer/same-origin/http-https/xhr-request/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, AllocateProtoAndIfaceCache, XPCWrappedNative::GetNewOrUsed, js_new, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/same-origin/attr-referrer/cross-origin/http-http/img-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/same-origin/attr-referrer/cross-origin/http-http/img-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, AllocateProtoAndIfaceCache, XPCWrappedNative::GetNewOrUsed, js_new, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/same-origin/attr-referrer/cross-origin/http-https/iframe-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/same-origin/attr-referrer/cross-origin/http-https/iframe-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [XPCWrappedNative::GetNewOrUsed, mozilla::BasePrincipal::CreateCodebasePrincipal, nsStringBuffer::Alloc, Alloc, NewEmptyScopeData, XPCWrappedNative::GetNewOrUsed, js_new, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/same-origin/attr-referrer/same-origin/http-http/iframe-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/same-origin/attr-referrer/same-origin/http-http/iframe-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, XPCWrappedNative::GetNewOrUsed, js_new, js_pod_calloc, js_pod_malloc, js_pod_realloc, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/same-origin/attr-referrer/same-origin/http-http/img-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/same-origin/attr-referrer/same-origin/http-http/img-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, XPCWrappedNative::GetNewOrUsed, js_new, js_pod_calloc, js_pod_malloc, js_pod_realloc, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/same-origin/http-rp/cross-origin/http-http/script-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/same-origin/http-rp/cross-origin/http-http/script-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, XPCWrappedNative::GetNewOrUsed, js_new, js_pod_calloc, js_pod_malloc, js_pod_realloc, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/same-origin/http-rp/cross-origin/http-https/iframe-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/same-origin/http-rp/cross-origin/http-https/iframe-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, XPCWrappedNative::GetNewOrUsed, js_new, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/same-origin/http-rp/cross-origin/http-https/script-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/same-origin/http-rp/cross-origin/http-https/script-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, XPCWrappedNative::GetNewOrUsed, js_new, js_pod_calloc, js_pod_realloc, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/same-origin/http-rp/same-origin/http-http/img-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/same-origin/http-rp/same-origin/http-http/img-tag/__dir__.ini
@@ -1,1 +0,0 @@
-lsan-allowed: [Alloc, XPCWrappedNative::GetNewOrUsed, js_new, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/referrer-policy/same-origin/meta-referrer/cross-origin/http-http/img-tag/__dir__.ini
+++ b/testing/web-platform/meta/referrer-policy/same-origin/meta-referrer/cross-origin/http-http/img-tag/__dir__.ini