Merge autoland to mozilla-central. a=merge
authorshindli <shindli@mozilla.com>
Mon, 18 Nov 2019 18:16:42 +0200
changeset 502501 edad970978193a3e5188e74dd09640039d6e2c76
parent 502500 f78494c785f1b3264b1a17376d01520e1072ba7b (current diff)
parent 502405 a0e0c81e36237ce624a44897278ea842bb04ed0a (diff)
child 502502 f9829d8dd6387dfc7c575349f271fecb865acc61
push id114172
push userdluca@mozilla.com
push dateTue, 19 Nov 2019 11:31:10 +0000
treeherdermozilla-inbound@b5c5ba07d3db [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone72.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 autoland to mozilla-central. a=merge
--- a/browser/actors/BlockedSiteChild.jsm
+++ b/browser/actors/BlockedSiteChild.jsm
@@ -2,19 +2,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 var EXPORTED_SYMBOLS = ["BlockedSiteChild"];
 
-const { ActorChild } = ChromeUtils.import(
-  "resource://gre/modules/ActorChild.jsm"
-);
 ChromeUtils.defineModuleGetter(
   this,
   "E10SUtils",
   "resource://gre/modules/E10SUtils.jsm"
 );
 
 ChromeUtils.defineModuleGetter(
   this,
@@ -54,38 +51,36 @@ function getSiteBlockedErrorDetails(docS
         provider: classifiedChannel.matchedProvider,
         uri: reportUri.asciiSpec,
       };
     }
   }
   return blockedInfo;
 }
 
-class BlockedSiteChild extends ActorChild {
+class BlockedSiteChild extends JSWindowActorChild {
   receiveMessage(msg) {
     if (msg.name == "DeceptiveBlockedDetails") {
-      this.mm.sendAsyncMessage("DeceptiveBlockedDetails:Result", {
-        blockedInfo: getSiteBlockedErrorDetails(this.mm.docShell),
-      });
+      return getSiteBlockedErrorDetails(this.docShell);
     }
+    return null;
   }
 
   handleEvent(event) {
     if (event.type == "AboutBlockedLoaded") {
       this.onAboutBlockedLoaded(event);
     } else if (event.type == "click" && event.button == 0) {
       this.onClick(event);
     }
   }
 
   onAboutBlockedLoaded(aEvent) {
-    let global = this.mm;
     let content = aEvent.target.ownerGlobal;
 
-    let blockedInfo = getSiteBlockedErrorDetails(global.docShell);
+    let blockedInfo = getSiteBlockedErrorDetails(this.docShell);
     let provider = blockedInfo.provider || "";
 
     let doc = content.document;
 
     /**
      * Set error description link in error details.
      * For example, the "reported as a deceptive site" link for
      * blocked phishing pages.
@@ -196,17 +191,16 @@ class BlockedSiteChild extends ActorChil
     if (/e=malwareBlocked/.test(ownerDoc.documentURI)) {
       reason = "malware";
     } else if (/e=unwantedBlocked/.test(ownerDoc.documentURI)) {
       reason = "unwanted";
     } else if (/e=harmfulBlocked/.test(ownerDoc.documentURI)) {
       reason = "harmful";
     }
 
-    this.mm.sendAsyncMessage("Browser:SiteBlockedError", {
+    this.sendAsyncMessage("Browser:SiteBlockedError", {
       location: ownerDoc.location.href,
       reason,
       elementId: event.target.getAttribute("id"),
-      isTopFrame: ownerDoc.defaultView.parent === ownerDoc.defaultView,
-      blockedInfo: getSiteBlockedErrorDetails(ownerDoc.defaultView.docShell),
+      blockedInfo: getSiteBlockedErrorDetails(this.docShell),
     });
   }
 }
new file mode 100644
--- /dev/null
+++ b/browser/actors/BlockedSiteParent.jsm
@@ -0,0 +1,73 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* 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/. */
+
+var EXPORTED_SYMBOLS = ["BlockedSiteParent"];
+var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+class BlockedSiteParent extends JSWindowActorParent {
+  receiveMessage(msg) {
+    switch (msg.name) {
+      case "Browser:SiteBlockedError":
+        this._onAboutBlocked(
+          msg.data.elementId,
+          msg.data.reason,
+          this.browsingContext === this.browsingContext.top,
+          msg.data.blockedInfo
+        );
+        break;
+    }
+  }
+
+  _onAboutBlocked(elementId, reason, isTopFrame, blockedInfo) {
+    let browser = this.browsingContext.top.embedderElement;
+    if (!browser) {
+      return;
+    }
+    let { BrowserOnClick } = browser.ownerGlobal;
+    // Depending on what page we are displaying here (malware/phishing/unwanted)
+    // use the right strings and links for each.
+    let bucketName = "";
+    let sendTelemetry = false;
+    if (reason === "malware") {
+      sendTelemetry = true;
+      bucketName = "WARNING_MALWARE_PAGE_";
+    } else if (reason === "phishing") {
+      sendTelemetry = true;
+      bucketName = "WARNING_PHISHING_PAGE_";
+    } else if (reason === "unwanted") {
+      sendTelemetry = true;
+      bucketName = "WARNING_UNWANTED_PAGE_";
+    } else if (reason === "harmful") {
+      sendTelemetry = true;
+      bucketName = "WARNING_HARMFUL_PAGE_";
+    }
+    let secHistogram = Services.telemetry.getHistogramById(
+      "URLCLASSIFIER_UI_EVENTS"
+    );
+    let nsISecTel = Ci.IUrlClassifierUITelemetry;
+    bucketName += isTopFrame ? "TOP_" : "FRAME_";
+
+    switch (elementId) {
+      case "goBackButton":
+        if (sendTelemetry) {
+          secHistogram.add(nsISecTel[bucketName + "GET_ME_OUT_OF_HERE"]);
+        }
+        browser.ownerGlobal.getMeOutOfHere(this.browsingContext);
+        break;
+      case "ignore_warning_link":
+        if (Services.prefs.getBoolPref("browser.safebrowsing.allowOverride")) {
+          if (sendTelemetry) {
+            secHistogram.add(nsISecTel[bucketName + "IGNORE_WARNING"]);
+          }
+          BrowserOnClick.ignoreWarningLink(
+            reason,
+            blockedInfo,
+            this.browsingContext
+          );
+        }
+        break;
+    }
+  }
+}
--- a/browser/actors/moz.build
+++ b/browser/actors/moz.build
@@ -20,16 +20,17 @@ with Files("PluginChild.jsm"):
     BUG_COMPONENT = ("Core", "Plug-ins")
 
 with Files("WebRTCChild.jsm"):
     BUG_COMPONENT = ("Firefox", "Site Permissions")
 
 FINAL_TARGET_FILES.actors += [
     'AboutReaderChild.jsm',
     'BlockedSiteChild.jsm',
+    'BlockedSiteParent.jsm',
     'BrowserTabChild.jsm',
     'BrowserTabParent.jsm',
     'ClickHandlerChild.jsm',
     'ContentMetaChild.jsm',
     'ContentMetaParent.jsm',
     'ContentSearchChild.jsm',
     'ContextMenuChild.jsm',
     'ContextMenuParent.jsm',
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1803,17 +1803,16 @@ var gBrowserInit = {
 
     window.addEventListener("AppCommand", HandleAppCommandEvent, true);
 
     // These routines add message listeners. They must run before
     // loading the frame script to ensure that we don't miss any
     // message sent between when the frame script is loaded and when
     // the listener is registered.
     LanguageDetectionListener.init();
-    BrowserOnClick.init();
     CaptivePortalWatcher.init();
     ZoomUI.init(window);
 
     let mm = window.getGroupMessageManager("browsers");
     mm.loadFrameScript("chrome://browser/content/tab-content.js", true, true);
 
     window.messageManager.addMessageListener("Browser:LoadURI", RedirectLoad);
 
@@ -2442,18 +2441,16 @@ var gBrowserInit = {
     PlacesToolbarHelper.uninit();
 
     BookmarkingUI.uninit();
 
     TabletModeUpdater.uninit();
 
     gTabletModePageCounter.finish();
 
-    BrowserOnClick.uninit();
-
     CaptivePortalWatcher.uninit();
 
     SidebarUI.uninit();
 
     DownloadsButton.uninit();
 
     gAccessibilityServiceIndicator.uninit();
 
@@ -3493,100 +3490,34 @@ const SEC_ERROR_UNKNOWN_ISSUER = SEC_ERR
 const PREF_SSL_IMPACT_ROOTS = ["security.tls.version.", "security.ssl3."];
 
 /**
  * Handle command events bubbling up from error page content
  * or from about:newtab or from remote error pages that invoke
  * us via async messaging.
  */
 var BrowserOnClick = {
-  init() {
-    let mm = window.messageManager;
-    mm.addMessageListener("Browser:SiteBlockedError", this);
-  },
-
-  uninit() {
-    let mm = window.messageManager;
-    mm.removeMessageListener("Browser:SiteBlockedError", this);
-  },
-
-  receiveMessage(msg) {
-    switch (msg.name) {
-      case "Browser:SiteBlockedError":
-        this.onAboutBlocked(
-          msg.data.elementId,
-          msg.data.reason,
-          msg.data.isTopFrame,
-          msg.data.location,
-          msg.data.blockedInfo
-        );
-        break;
-    }
-  },
-
-  onAboutBlocked(elementId, reason, isTopFrame, location, blockedInfo) {
-    // Depending on what page we are displaying here (malware/phishing/unwanted)
-    // use the right strings and links for each.
-    let bucketName = "";
-    let sendTelemetry = false;
-    if (reason === "malware") {
-      sendTelemetry = true;
-      bucketName = "WARNING_MALWARE_PAGE_";
-    } else if (reason === "phishing") {
-      sendTelemetry = true;
-      bucketName = "WARNING_PHISHING_PAGE_";
-    } else if (reason === "unwanted") {
-      sendTelemetry = true;
-      bucketName = "WARNING_UNWANTED_PAGE_";
-    } else if (reason === "harmful") {
-      sendTelemetry = true;
-      bucketName = "WARNING_HARMFUL_PAGE_";
-    }
-    let secHistogram = Services.telemetry.getHistogramById(
-      "URLCLASSIFIER_UI_EVENTS"
-    );
-    let nsISecTel = Ci.IUrlClassifierUITelemetry;
-    bucketName += isTopFrame ? "TOP_" : "FRAME_";
-
-    switch (elementId) {
-      case "goBackButton":
-        if (sendTelemetry) {
-          secHistogram.add(nsISecTel[bucketName + "GET_ME_OUT_OF_HERE"]);
-        }
-        getMeOutOfHere();
-        break;
-      case "ignore_warning_link":
-        if (Services.prefs.getBoolPref("browser.safebrowsing.allowOverride")) {
-          if (sendTelemetry) {
-            secHistogram.add(nsISecTel[bucketName + "IGNORE_WARNING"]);
-          }
-          this.ignoreWarningLink(reason, blockedInfo);
-        }
-        break;
-    }
-  },
-
-  ignoreWarningLink(reason, blockedInfo) {
+  ignoreWarningLink(reason, blockedInfo, browsingContext) {
     let triggeringPrincipal = E10SUtils.deserializePrincipal(
       blockedInfo.triggeringPrincipal,
       _createNullPrincipalFromTabUserContextId()
     );
     // Allow users to override and continue through to the site,
     // but add a notify bar as a reminder, so that they don't lose
     // track after, e.g., tab switching.
-    gBrowser.loadURI(gBrowser.currentURI.spec, {
+    browsingContext.loadURI(blockedInfo.uri, {
       triggeringPrincipal,
       flags: Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CLASSIFIER,
     });
 
-    // We can't use gBrowser.contentPrincipal which is principal of about:blocked
+    // We can't use browser.contentPrincipal which is principal of about:blocked
     // Create one from uri with current principal origin attributes
     let principal = Services.scriptSecurityManager.createContentPrincipal(
-      gBrowser.currentURI,
-      gBrowser.contentPrincipal.originAttributes
+      Services.io.newURI(blockedInfo.uri),
+      browsingContext.currentWindowGlobal.documentPrincipal.originAttributes
     );
     Services.perms.addFromPrincipal(
       principal,
       "safe-browsing",
       Ci.nsIPermissionManager.ALLOW_ACTION,
       Ci.nsIPermissionManager.EXPIRE_SESSION
     );
 
@@ -3594,17 +3525,17 @@ var BrowserOnClick = {
       {
         label: gNavigatorBundle.getString(
           "safebrowsing.getMeOutOfHereButton.label"
         ),
         accessKey: gNavigatorBundle.getString(
           "safebrowsing.getMeOutOfHereButton.accessKey"
         ),
         callback() {
-          getMeOutOfHere();
+          getMeOutOfHere(browsingContext);
         },
       },
     ];
 
     let title;
     if (reason === "malware") {
       let reportUrl = gSafeBrowsing.getReportURL("MalwareMistake", blockedInfo);
       title = gNavigatorBundle.getString("safebrowsing.reportedAttackSite");
@@ -3657,18 +3588,18 @@ var BrowserOnClick = {
 
 /**
  * Re-direct the browser to a known-safe page.  This function is
  * used when, for example, the user browses to a known malware page
  * and is presented with about:blocked.  The "Get me out of here!"
  * button should take the user to the default start page so that even
  * when their own homepage is infected, we can get them somewhere safe.
  */
-function getMeOutOfHere() {
-  gBrowser.loadURI(getDefaultHomePage(), {
+function getMeOutOfHere(browsingContext) {
+  browsingContext.top.loadURI(getDefaultHomePage(), {
     triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(), // Also needs to load homepage
   });
 }
 
 /**
  * Return the default start page for the cases when the user's own homepage is
  * infected, so we can get them somewhere safe.
  */
@@ -8418,46 +8349,51 @@ function checkEmptyPageOrigin(
   }
   // ... so for those that don't have them, enforce that the page has the
   // system principal (this matches e.g. on about:newtab).
 
   return contentPrincipal.isSystemPrincipal;
 }
 
 function ReportFalseDeceptiveSite() {
-  let docURI = gBrowser.selectedBrowser.documentURI;
-  let isPhishingPage =
-    docURI && docURI.spec.startsWith("about:blocked?e=deceptiveBlocked");
-
-  if (isPhishingPage) {
-    let mm = gBrowser.selectedBrowser.messageManager;
-    let onMessage = message => {
-      mm.removeMessageListener("DeceptiveBlockedDetails:Result", onMessage);
-      let reportUrl = gSafeBrowsing.getReportURL(
-        "PhishMistake",
-        message.data.blockedInfo
-      );
-      if (reportUrl) {
-        openTrustedLinkIn(reportUrl, "tab");
-      } else {
-        let bundle = Services.strings.createBundle(
-          "chrome://browser/locale/safebrowsing/safebrowsing.properties"
+  let contextsToVisit = [gBrowser.selectedBrowser.browsingContext];
+  while (contextsToVisit.length) {
+    let currentContext = contextsToVisit.pop();
+    let global = currentContext.currentWindowGlobal;
+
+    if (!global) {
+      continue;
+    }
+    let docURI = global.documentURI;
+    // Ensure the page is an about:blocked pagae before handling.
+    if (docURI && docURI.spec.startsWith("about:blocked?e=deceptiveBlocked")) {
+      let actor = global.getActor("BlockedSite");
+      actor.sendQuery("DeceptiveBlockedDetails").then(data => {
+        let reportUrl = gSafeBrowsing.getReportURL(
+          "PhishMistake",
+          data.blockedInfo
         );
-        Services.prompt.alert(
-          window,
-          bundle.GetStringFromName("errorReportFalseDeceptiveTitle"),
-          bundle.formatStringFromName("errorReportFalseDeceptiveMessage", [
-            message.data.blockedInfo.provider,
-          ])
-        );
-      }
-    };
-    mm.addMessageListener("DeceptiveBlockedDetails:Result", onMessage);
-
-    mm.sendAsyncMessage("DeceptiveBlockedDetails");
+        if (reportUrl) {
+          openTrustedLinkIn(reportUrl, "tab");
+        } else {
+          let bundle = Services.strings.createBundle(
+            "chrome://browser/locale/safebrowsing/safebrowsing.properties"
+          );
+          Services.prompt.alert(
+            window,
+            bundle.GetStringFromName("errorReportFalseDeceptiveTitle"),
+            bundle.formatStringFromName("errorReportFalseDeceptiveMessage", [
+              data.blockedInfo.provider,
+            ])
+          );
+        }
+      });
+    }
+
+    contextsToVisit.push(...currentContext.getChildren());
   }
 }
 
 /**
  * Format a URL
  * eg:
  * echo formatURL("https://addons.mozilla.org/%LOCALE%/%APP%/%VERSION%/");
  * > https://addons.mozilla.org/en-US/firefox/3.0a1/
--- a/browser/components/BrowserGlue.jsm
+++ b/browser/components/BrowserGlue.jsm
@@ -36,16 +36,31 @@ XPCOMUtils.defineLazyServiceGetter(
 const PREF_PDFJS_ENABLED_CACHE_STATE = "pdfjs.enabledCache.state";
 
 /**
  * Fission-compatible JSWindowActor implementations.
  * Detailed documentation of these is in dom/docs/Fission.rst,
  * available at https://firefox-source-docs.mozilla.org/dom/Fission.html#jswindowactor
  */
 let ACTORS = {
+  BlockedSite: {
+    parent: {
+      moduleURI: "resource:///actors/BlockedSiteParent.jsm",
+    },
+    child: {
+      moduleURI: "resource:///actors/BlockedSiteChild.jsm",
+      events: {
+        AboutBlockedLoaded: { wantUntrusted: true },
+        click: {},
+      },
+    },
+    matches: ["about:blocked?*"],
+    allFrames: true,
+  },
+
   BrowserTab: {
     parent: {
       moduleURI: "resource:///actors/BrowserTabParent.jsm",
     },
     child: {
       moduleURI: "resource:///actors/BrowserTabChild.jsm",
 
       events: {
@@ -295,29 +310,16 @@ let LEGACY_ACTORS = {
         DOMContentLoaded: {},
         pageshow: { mozSystemGroup: true },
         pagehide: { mozSystemGroup: true },
       },
       messages: ["Reader:ToggleReaderMode", "Reader:PushState"],
     },
   },
 
-  BlockedSite: {
-    child: {
-      module: "resource:///actors/BlockedSiteChild.jsm",
-      events: {
-        AboutBlockedLoaded: { wantUntrusted: true },
-        click: {},
-      },
-      matches: ["about:blocked?*"],
-      allFrames: true,
-      messages: ["DeceptiveBlockedDetails"],
-    },
-  },
-
   ClickHandler: {
     child: {
       module: "resource:///actors/ClickHandlerChild.jsm",
       events: {
         click: { capture: true, mozSystemGroup: true },
         auxclick: { capture: true, mozSystemGroup: true },
       },
     },
--- a/devtools/client/whats-new/src/main.js
+++ b/devtools/client/whats-new/src/main.js
@@ -2,123 +2,134 @@
  * 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 ReactDOM = require("devtools/client/shared/vendor/react-dom");
 const Types = require("devtools/client/shared/vendor/react-prop-types");
 const { openDocLink } = require("devtools/client/shared/link");
+var Services = require("Services");
+const isMacOS = Services.appinfo.OS === "Darwin";
 
 const {
   Component,
   createFactory,
 } = require("devtools/client/shared/vendor/react");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 
+const utmParams = new URLSearchParams({
+  utm_source: "devtools",
+  utm_medium: "devtools_whatsnew",
+});
+
 const aside = {
   header: "Instantly Send Tabs to Mobile",
   content: `Test your site on mobile and other devices without having to copy, paste, or leave the browser.`,
   cta: "Learn More About Send Tabs",
-  href:
-    "https://support.mozilla.org/en-US/kb/send-tab-firefox-desktop-other-devices?utm_source=devtools_whatsnew",
+  href: `https://support.mozilla.org/kb/send-tab-firefox-desktop-other-devices?${utmParams}`,
 };
 
 const release = {
   title: "What’s New in DevTools (Firefox 70 & 71)",
   linkText: "Read more",
-  linkUrl:
-    "https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/71?utm_source=devtools&utm_medium=devtools_whatsnew",
+  linkUrl: `https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/71?${utmParams}`,
   features: [
     {
       header: `New WebSocket Messages Inspector`,
       description: `The Network panel has a new Messages tab for inspecting WebSocket frames sent and received through the selected connection.`,
       href:
-        "https://hacks.mozilla.org/2019/10/firefoxs-new-websocket-inspector/ ",
+        "https://hacks.mozilla.org/2019/10/firefoxs-new-websocket-inspector/",
     },
     {
       header: `Block URLs in the Network panel`,
       description: `Test how a page loads without specific files, like CSS or JavaScript. Right-click network requests and select “Block URL” or use the new Request Blocking pane.`,
-      href: `https://developer.mozilla.org/docs/Tools/Network_Monitor/request_list?utm_source=devtools&utm_medium=devtools_whatsnew#Block_a_specific_URL`,
+      href: `https://developer.mozilla.org/docs/Tools/Network_Monitor/request_list?${utmParams}#Block_a_specific_URL`,
     },
     {
       header: `New multi-line editor mode in Console`,
       description: `Iterate quickly on JavaScript snippets with the new multi-line editor input. It combines the ease of authoring code in an IDE with the workflow of repeatedly executing code in the context of the page.`,
-      href: `https://developer.mozilla.org/docs/Tools/Web_Console/The_command_line_interpreter?utm_source=devtools&utm_medium=devtools_whatsnew`,
+      href: `https://developer.mozilla.org/docs/Tools/Web_Console/The_command_line_interpreter?${utmParams}`,
       hidden: true,
     },
     {
       header: `Search across all Network Headers and Content`,
       description: `The new Search pane lets you search across all network headers and response bodies in the Network panel.`,
-      href: `https://developer.mozilla.org/docs/Tools/Network_Monitor/request_list?utm_source=devtools&utm_medium=devtools_whatsnew#Search_Requests`,
+      href: `https://developer.mozilla.org/docs/Tools/Network_Monitor/request_list?${utmParams}#Search_Requests`,
     },
     {
       header: `Log on Events`,
       description: `Enabling logging for Event Listener Breakpoints in the Debugger lets you observe which event handlers are being executed without the overhead of pausing.`,
-      href: `https://developer.mozilla.org/docs/Tools/Debugger/Set_event_listener_breakpoints?utm_source=devtools&utm_medium=devtools_whatsnew`,
+      href: `https://developer.mozilla.org/docs/Tools/Debugger/Set_event_listener_breakpoints?${utmParams}`,
     },
     {
       header: `Quick search in Event Listeners Breakpoints`,
       description: `Quickly find the right event category and type with the new filter field in the Debugger’s Event Listener Breakpoints pane. `,
-      href: `https://developer.mozilla.org/docs/Tools/Debugger/Set_event_listener_breakpoints?utm_source=devtools&utm_medium=devtools_whatsnew`,
+      href: `https://developer.mozilla.org/docs/Tools/Debugger/Set_event_listener_breakpoints?${utmParams}`,
     },
     {
       header: `New in 70: Inactive CSS rules indicator in Rules pane`,
       description: `The Inspector now grays out CSS declarations that don’t affect the selected element and shows a tooltip explaining why—and even how to fix it.`,
       href: `https://hacks.mozilla.org/2019/10/firefox-70-a-bountiful-release-for-all/#developertools`,
     },
     {
       header: `New in 70: Pause on DOM Mutation in Debugger`,
       description: `DOM Mutation Breakpoints (aka DOM Change Breakpoints) let you pause scripts that add, remove, or change specific elements.`,
-      href: `https://developer.mozilla.org/docs/Tools/Debugger/Break_on_DOM_mutation?utm_source=devtools&utm_medium=devtools_whatsnew`,
+      href: `https://developer.mozilla.org/docs/Tools/Debugger/Break_on_DOM_mutation?${utmParams}`,
     },
     {
       header: `New in 70: Color contrast information in the color picker`,
       description: `In the CSS Rules view, you can click foreground colors with the color picker to determine if their contrast with the background color meets accessibility guidelines.`,
-      href: `https://developer.mozilla.org/docs/Tools/Page_Inspector/How_to/Inspect_and_select_colors?utm_source=devtools&utm_medium=devtools_whatsnew`,
+      href: `https://developer.mozilla.org/docs/Tools/Page_Inspector/How_to/Inspect_and_select_colors?${utmParams}`,
     },
     {
       header: `New in 70: Auditing checks in the Accessibility inspector`,
       description: `The Accessibility Inspector’s “Check for issues” tool can now audit for keyboard accessibility in addition to color contrast and text labels.`,
-      href: `https://developer.mozilla.org/docs/Tools/Accessibility_inspector?utm_source=devtools&utm_medium=devtools_whatsnew#Check_for_accessibility_issues`,
+      href: `https://developer.mozilla.org/docs/Tools/Accessibility_inspector?${utmParams}#Check_for_accessibility_issues`,
     },
   ],
 };
 
 const dev = {
   title: "Experimental Features in Firefox Developer Edition",
-  linkUrl:
-    "https://www.mozilla.org/firefox/developer/?utm_medium=devtools_whatsnew&utm_source=devtools",
+  linkUrl: `https://www.mozilla.org/firefox/developer/?${utmParams}`,
   linkText: "Get DevEdition",
   features: [
     {
       header: `Debug Variables with Watchpoints`,
       description: `Debugger’s new Watchpoints feature lets you pause when properties get read or written. Right-click object properties in the Scopes pane when paused to use the new “Break on…” menu.`,
-      href: `https://developer.mozilla.org/docs/Tools/Debugger/How_to/Set_a_watchpoint_on_a_property?utm_source=devtools&utm_medium=devtools_whatsnew`,
+      href: `https://developer.mozilla.org/docs/Tools/Debugger/How_to/Set_a_watchpoint_on_a_property?${utmParams}`,
     },
   ],
 };
 
+function openLink(href, e) {
+  return openDocLink(href, {
+    relatedToCurrent: true,
+    inBackground: isMacOS ? e.metaKey : e.ctrlKey,
+  });
+}
+
 class Aside extends Component {
   render() {
     return dom.aside(
       {},
       dom.div(
         { className: "call-out" },
         dom.h3({}, aside.header),
         dom.p({}, aside.content),
         dom.p(
           { className: "cta" },
           dom.a(
             {
               href: aside.href,
               className: "devtools-button",
               onClick: e => {
                 e.preventDefault();
-                openDocLink(aside.href);
+                openLink(aside.href, e);
               },
             },
             aside.cta
           )
         )
       )
     );
   }
@@ -137,34 +148,34 @@ class Feature extends Component {
     const { header, description, href } = this.props;
     return dom.li(
       { key: header, className: "feature" },
       dom.a(
         {
           href,
           onClick: e => {
             e.preventDefault();
-            openDocLink(href);
+            openLink(href, e);
           },
         },
         dom.h3({}, header),
         dom.p({}, description)
       )
     );
   }
 }
 
 function Link(text, href) {
   return dom.a(
     {
       className: "link",
       href,
       onClick: e => {
         e.preventDefault();
-        openDocLink(href);
+        openLink(href, e);
       },
     },
     text
   );
 }
 
 class App extends Component {
   render() {
--- a/devtools/server/actors/highlighters/utils/markup.js
+++ b/devtools/server/actors/highlighters/utils/markup.js
@@ -4,17 +4,16 @@
 
 "use strict";
 
 const { Cu, Cr } = require("chrome");
 const {
   getCurrentZoom,
   getWindowDimensions,
   getViewportDimensions,
-  getRootBindingParent,
   loadSheet,
 } = require("devtools/shared/layout/utils");
 const EventEmitter = require("devtools/shared/event-emitter");
 const InspectorUtils = require("InspectorUtils");
 
 const lazyContainer = {};
 
 loader.lazyRequireGetter(
@@ -132,20 +131,18 @@ function isNodeValid(node, nodeType = No
   }
 
   // Is its document accessible?
   const doc = node.ownerDocument;
   if (!doc || !doc.defaultView) {
     return false;
   }
 
-  // Is the node connected to the document? Using getBindingParent adds
-  // support for anonymous elements generated by a node in the document.
-  const bindingParent = getRootBindingParent(node);
-  if (!doc.documentElement.contains(bindingParent)) {
+  // Is the node connected to the document?
+  if (!node.isConnected) {
     return false;
   }
 
   return true;
 }
 exports.isNodeValid = isNodeValid;
 
 /**
--- a/dom/base/CharacterData.cpp
+++ b/dom/base/CharacterData.cpp
@@ -406,17 +406,17 @@ nsresult CharacterData::BindToTree(BindC
   const bool hadParent = !!GetParentNode();
 
   if (aParent.IsInNativeAnonymousSubtree()) {
     SetFlags(NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE);
   }
   if (aParent.HasFlag(NODE_HAS_BEEN_IN_UA_WIDGET)) {
     SetFlags(NODE_HAS_BEEN_IN_UA_WIDGET);
   }
-  if (HasFlag(NODE_IS_ANONYMOUS_ROOT)) {
+  if (IsRootOfNativeAnonymousSubtree()) {
     aParent.SetMayHaveAnonymousChildren();
   }
 
   // Set parent
   mParent = &aParent;
   if (!hadParent && aParent.IsContent()) {
     SetParentIsContent(true);
     NS_ADDREF(mParent);
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -1464,17 +1464,17 @@ nsresult Element::BindToTree(BindContext
   const bool hadParent = !!GetParentNode();
 
   if (aParent.IsInNativeAnonymousSubtree()) {
     SetFlags(NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE);
   }
   if (aParent.HasFlag(NODE_HAS_BEEN_IN_UA_WIDGET)) {
     SetFlags(NODE_HAS_BEEN_IN_UA_WIDGET);
   }
-  if (HasFlag(NODE_IS_ANONYMOUS_ROOT)) {
+  if (IsRootOfNativeAnonymousSubtree()) {
     aParent.SetMayHaveAnonymousChildren();
   }
 
   // Now set the parent.
   mParent = &aParent;
   if (!hadParent && aParent.IsContent()) {
     SetParentIsContent(true);
     NS_ADDREF(mParent);
--- a/dom/base/nsIContent.h
+++ b/dom/base/nsIContent.h
@@ -170,32 +170,34 @@ class nsIContent : public nsINode {
    */
   virtual already_AddRefed<nsINodeList> GetChildren(uint32_t aFilter) = 0;
 
   /**
    * Makes this content anonymous
    * @see nsIAnonymousContentCreator
    */
   void SetIsNativeAnonymousRoot() {
-    SetFlags(NODE_IS_ANONYMOUS_ROOT | NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE |
+    SetFlags(NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE |
              NODE_IS_NATIVE_ANONYMOUS_ROOT);
   }
 
   /**
    * Returns |this| if it is not chrome-only/native anonymous, otherwise
    * first non chrome-only/native anonymous ancestor.
    */
   nsIContent* FindFirstNonChromeOnlyAccessContent() const;
 
   /**
    * Returns true if and only if this node has a parent, but is not in
    * its parent's child list.
+   *
+   * FIXME(emilio): Remove along nsINode::IsInAnonymousSubtree.
    */
   bool IsRootOfAnonymousSubtree() const {
-    return HasFlag(NODE_IS_ANONYMOUS_ROOT);
+    return IsRootOfNativeAnonymousSubtree();
   }
 
   /**
    * Return true iff this node is in an HTML document (in the HTML5 sense of
    * the term, i.e. not in an XHTML/XML document).
    */
   inline bool IsInHTMLDocument() const;
 
--- a/dom/base/nsINode.cpp
+++ b/dom/base/nsINode.cpp
@@ -1157,22 +1157,17 @@ nsPIDOMWindowOuter* nsINode::GetOwnerGlo
 }
 
 nsIGlobalObject* nsINode::GetOwnerGlobal() const {
   bool dummy;
   return OwnerDoc()->GetScriptHandlingObject(dummy);
 }
 
 bool nsINode::UnoptimizableCCNode() const {
-  const uintptr_t problematicFlags =
-      (NODE_IS_ANONYMOUS_ROOT | NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE |
-       NODE_IS_NATIVE_ANONYMOUS_ROOT);
-  return HasFlag(problematicFlags) || NodeType() == ATTRIBUTE_NODE ||
-         // For strange cases like xbl:content/xbl:children
-         (IsElement() && AsElement()->IsInNamespace(kNameSpaceID_XBL));
+  return IsInNativeAnonymousSubtree() || IsAttr();
 }
 
 /* static */
 bool nsINode::Traverse(nsINode* tmp, nsCycleCollectionTraversalCallback& cb) {
   if (MOZ_LIKELY(!cb.WantAllTraces())) {
     Document* currentDoc = tmp->GetComposedDoc();
     if (currentDoc && nsCCUncollectableMarker::InGeneration(
                           currentDoc->GetMarkedCCGeneration())) {
--- a/dom/base/nsINode.h
+++ b/dom/base/nsINode.h
@@ -106,90 +106,83 @@ enum class CallerType : uint32_t;
 
 enum {
   // This bit will be set if the node has a listener manager.
   NODE_HAS_LISTENERMANAGER = NODE_FLAG_BIT(0),
 
   // Whether this node has had any properties set on it
   NODE_HAS_PROPERTIES = NODE_FLAG_BIT(1),
 
-  // Whether this node is the root of an anonymous subtree.  Note that this
-  // need not be a native anonymous subtree.  Any anonymous subtree, including
-  // XBL-generated ones, will do.  This flag is set-once: once a node has it,
-  // it must not be removed.
-  // NOTE: Should only be used on nsIContent nodes
-  NODE_IS_ANONYMOUS_ROOT = NODE_FLAG_BIT(2),
-
   // Whether the node has some ancestor, possibly itself, that is native
   // anonymous.  This includes ancestors crossing XBL scopes, in cases when an
   // XBL binding is attached to an element which has a native anonymous
   // ancestor.  This flag is set-once: once a node has it, it must not be
   // removed.
   // NOTE: Should only be used on nsIContent nodes
-  NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE = NODE_FLAG_BIT(3),
+  NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE = NODE_FLAG_BIT(2),
 
   // Whether this node is the root of a native anonymous (from the perspective
   // of its parent) subtree.  This flag is set-once: once a node has it, it
   // must not be removed.
   // NOTE: Should only be used on nsIContent nodes
-  NODE_IS_NATIVE_ANONYMOUS_ROOT = NODE_FLAG_BIT(4),
+  NODE_IS_NATIVE_ANONYMOUS_ROOT = NODE_FLAG_BIT(3),
 
-  NODE_IS_EDITABLE = NODE_FLAG_BIT(5),
+  NODE_IS_EDITABLE = NODE_FLAG_BIT(4),
 
   // Whether the node participates in a shadow tree.
-  NODE_IS_IN_SHADOW_TREE = NODE_FLAG_BIT(6),
+  NODE_IS_IN_SHADOW_TREE = NODE_FLAG_BIT(5),
 
   // Node has an :empty or :-moz-only-whitespace selector
-  NODE_HAS_EMPTY_SELECTOR = NODE_FLAG_BIT(7),
+  NODE_HAS_EMPTY_SELECTOR = NODE_FLAG_BIT(6),
 
   // A child of the node has a selector such that any insertion,
   // removal, or appending of children requires restyling the parent.
-  NODE_HAS_SLOW_SELECTOR = NODE_FLAG_BIT(8),
+  NODE_HAS_SLOW_SELECTOR = NODE_FLAG_BIT(7),
 
   // A child of the node has a :first-child, :-moz-first-node,
   // :only-child, :last-child or :-moz-last-node selector.
-  NODE_HAS_EDGE_CHILD_SELECTOR = NODE_FLAG_BIT(9),
+  NODE_HAS_EDGE_CHILD_SELECTOR = NODE_FLAG_BIT(8),
 
   // A child of the node has a selector such that any insertion or
   // removal of children requires restyling later siblings of that
   // element.  Additionally (in this manner it is stronger than
   // NODE_HAS_SLOW_SELECTOR), if a child's style changes due to any
   // other content tree changes (e.g., the child changes to or from
   // matching :empty due to a grandchild insertion or removal), the
   // child's later siblings must also be restyled.
-  NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS = NODE_FLAG_BIT(10),
+  NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS = NODE_FLAG_BIT(9),
 
   NODE_ALL_SELECTOR_FLAGS = NODE_HAS_EMPTY_SELECTOR | NODE_HAS_SLOW_SELECTOR |
                             NODE_HAS_EDGE_CHILD_SELECTOR |
                             NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS,
 
   // This node needs to go through frame construction to get a frame (or
   // undisplayed entry).
-  NODE_NEEDS_FRAME = NODE_FLAG_BIT(11),
+  NODE_NEEDS_FRAME = NODE_FLAG_BIT(10),
 
   // At least one descendant in the flattened tree has NODE_NEEDS_FRAME set.
   // This should be set on every node on the flattened tree path between the
   // node(s) with NODE_NEEDS_FRAME and the root content.
-  NODE_DESCENDANTS_NEED_FRAMES = NODE_FLAG_BIT(12),
+  NODE_DESCENDANTS_NEED_FRAMES = NODE_FLAG_BIT(11),
 
   // Set if the node has the accesskey attribute set.
-  NODE_HAS_ACCESSKEY = NODE_FLAG_BIT(13),
+  NODE_HAS_ACCESSKEY = NODE_FLAG_BIT(12),
 
   // Set if the node has right-to-left directionality
-  NODE_HAS_DIRECTION_RTL = NODE_FLAG_BIT(14),
+  NODE_HAS_DIRECTION_RTL = NODE_FLAG_BIT(13),
 
   // Set if the node has left-to-right directionality
-  NODE_HAS_DIRECTION_LTR = NODE_FLAG_BIT(15),
+  NODE_HAS_DIRECTION_LTR = NODE_FLAG_BIT(14),
 
   NODE_ALL_DIRECTION_FLAGS = NODE_HAS_DIRECTION_LTR | NODE_HAS_DIRECTION_RTL,
 
-  NODE_HAS_BEEN_IN_UA_WIDGET = NODE_FLAG_BIT(16),
+  NODE_HAS_BEEN_IN_UA_WIDGET = NODE_FLAG_BIT(15),
 
   // Remaining bits are node type specific.
-  NODE_TYPE_SPECIFIC_BITS_OFFSET = 17
+  NODE_TYPE_SPECIFIC_BITS_OFFSET = 16
 };
 
 // Make sure we have space for our bits
 #define ASSERT_NODE_FLAGS_SPACE(n)                         \
   static_assert(WRAPPER_CACHE_FLAGS_BITS_USED + (n) <=     \
                     sizeof(nsWrapperCache::FlagsType) * 8, \
                 "Not enough space for our bits")
 ASSERT_NODE_FLAGS_SPACE(NODE_TYPE_SPECIFIC_BITS_OFFSET);
@@ -1207,29 +1200,28 @@ class nsINode : public mozilla::dom::Eve
    */
 #ifdef DEBUG
   nsSlots* DebugGetSlots() { return Slots(); }
 #endif
 
   void SetFlags(FlagsType aFlagsToSet) {
     NS_ASSERTION(
         !(aFlagsToSet &
-          (NODE_IS_ANONYMOUS_ROOT | NODE_IS_NATIVE_ANONYMOUS_ROOT |
-           NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE | NODE_DESCENDANTS_NEED_FRAMES |
-           NODE_NEEDS_FRAME | NODE_HAS_BEEN_IN_UA_WIDGET)) ||
+          (NODE_IS_NATIVE_ANONYMOUS_ROOT | NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE |
+           NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME |
+           NODE_HAS_BEEN_IN_UA_WIDGET)) ||
             IsContent(),
         "Flag only permitted on nsIContent nodes");
     nsWrapperCache::SetFlags(aFlagsToSet);
   }
 
   void UnsetFlags(FlagsType aFlagsToUnset) {
-    NS_ASSERTION(
-        !(aFlagsToUnset & (NODE_IS_ANONYMOUS_ROOT | NODE_HAS_BEEN_IN_UA_WIDGET |
-                           NODE_IS_NATIVE_ANONYMOUS_ROOT)),
-        "Trying to unset write-only flags");
+    NS_ASSERTION(!(aFlagsToUnset & (NODE_HAS_BEEN_IN_UA_WIDGET |
+                                    NODE_IS_NATIVE_ANONYMOUS_ROOT)),
+                 "Trying to unset write-only flags");
     nsWrapperCache::UnsetFlags(aFlagsToUnset);
   }
 
   void SetEditableFlag(bool aEditable) {
     if (aEditable) {
       SetFlags(NODE_IS_EDITABLE);
     } else {
       UnsetFlags(NODE_IS_EDITABLE);
@@ -1339,20 +1331,19 @@ class nsINode : public mozilla::dom::Eve
   bool IsInShadowTree() const { return HasFlag(NODE_IS_IN_SHADOW_TREE); }
 
   /**
    * Get whether this node is C++-generated anonymous content
    * @see nsIAnonymousContentCreator
    * @return whether this content is anonymous
    */
   bool IsRootOfNativeAnonymousSubtree() const {
-    NS_ASSERTION(!HasFlag(NODE_IS_NATIVE_ANONYMOUS_ROOT) ||
-                     (HasFlag(NODE_IS_ANONYMOUS_ROOT) &&
-                      HasFlag(NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE)),
-                 "Some flags seem to be missing!");
+    NS_ASSERTION(
+        !HasFlag(NODE_IS_NATIVE_ANONYMOUS_ROOT) || IsInNativeAnonymousSubtree(),
+        "Some flags seem to be missing!");
     return HasFlag(NODE_IS_NATIVE_ANONYMOUS_ROOT);
   }
 
   // Whether this node is a UA Widget ShadowRoot.
   inline bool IsUAWidget() const;
   // Whether this node is currently in a UA Widget Shadow tree.
   inline bool IsInUAWidget() const;
   // Whether this node is the root of a ChromeOnlyAccess DOM subtree.
--- a/dom/battery/test/test_battery_unprivileged.html
+++ b/dom/battery/test/test_battery_unprivileged.html
@@ -12,13 +12,14 @@
 <pre id="test">
 <script type="application/javascript">
 
 "use strict";
 
 /** Test for Battery API **/
 ok(!("getBattery" in navigator), "navigator.getBattery should not exist for unprivileged web content");
 ok(!("battery" in navigator), "navigator.battery should not exist");
+ok(!("BatteryManager" in window), "BatteryManager should not exist");
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -5524,24 +5524,19 @@ void EventStateManager::RemoveNodeFromCh
 
   nsCOMPtr<nsIContent>& leaf =
       aState == NS_EVENT_STATE_HOVER ? mHoverContent : mActiveContent;
 
   MOZ_ASSERT(leaf);
   // These two NS_ASSERTIONS below can fail for Shadow DOM sometimes, and it's
   // not clear how to best handle it, see
   // https://github.com/whatwg/html/issues/4795 and bug 1551621.
-  //
-  // The NODE_IS_ANONYMOUS_ROOT is there because XBL used to remove content
-  // without notifying, but it shouldn't apply to NAC since
-  // NAC notifies (see NativeAnonymousContentRemoved).
-  NS_ASSERTION(nsContentUtils::ContentIsFlattenedTreeDescendantOf(
-                   leaf, aContentRemoved) ||
-               leaf->SubtreeRoot()->HasFlag(NODE_IS_ANONYMOUS_ROOT),
-               "Flat tree and active / hover chain got out of sync");
+  NS_ASSERTION(
+      nsContentUtils::ContentIsFlattenedTreeDescendantOf(leaf, aContentRemoved),
+      "Flat tree and active / hover chain got out of sync");
 
   nsIContent* newLeaf = aContentRemoved->GetFlattenedTreeParent();
   MOZ_ASSERT(!newLeaf || newLeaf->IsElement());
   NS_ASSERTION(!newLeaf || newLeaf->AsElement()->State().HasState(aState),
                "State got out of sync because of shadow DOM");
   if (aNotify) {
     SetContentState(newLeaf, aState);
   } else {
--- a/dom/media/MediaCache.cpp
+++ b/dom/media/MediaCache.cpp
@@ -2554,17 +2554,16 @@ nsresult MediaCacheStream::Read(AutoLock
       // Update mStreamOffset before we drop the lock. We need to run
       // Update() again since stream reading strategy might have changed.
       mStreamOffset = streamOffset;
       mMediaCache->QueueUpdate(aLock);
     }
 
     // No data to read, so block
     aLock.Wait();
-    continue;
   }
 
   uint32_t count = buffer.Elements() - aBuffer;
   *aBytes = count;
   if (count == 0) {
     return NS_OK;
   }
 
--- a/dom/media/mediacapabilities/BenchmarkStorageChild.cpp
+++ b/dom/media/mediacapabilities/BenchmarkStorageChild.cpp
@@ -19,9 +19,16 @@ PBenchmarkStorageChild* BenchmarkStorage
     PContentChild* contentChild = dom::ContentChild::GetSingleton();
     MOZ_ASSERT(contentChild);
     contentChild->SendPBenchmarkStorageConstructor();
   }
   MOZ_ASSERT(sChild);
   return sChild;
 }
 
+BenchmarkStorageChild::~BenchmarkStorageChild() {
+  MOZ_ASSERT(NS_IsMainThread());
+  if (sChild == this) {
+    sChild = nullptr;
+  }
+}
+
 }  // namespace mozilla
--- a/dom/media/mediacapabilities/BenchmarkStorageChild.h
+++ b/dom/media/mediacapabilities/BenchmarkStorageChild.h
@@ -12,15 +12,16 @@
 namespace mozilla {
 
 class BenchmarkStorageChild : public PBenchmarkStorageChild {
  public:
   /* Singleton class to avoid recreating the protocol every time we need access
    * to the storage. */
   static PBenchmarkStorageChild* Instance();
 
+  ~BenchmarkStorageChild();
  private:
   BenchmarkStorageChild() = default;
 };
 
 }  // namespace mozilla
 
 #endif  // include_dom_media_mediacapabilities_BenchmarkStorageChild_h
--- a/dom/media/webaudio/MediaStreamTrackAudioSourceNode.cpp
+++ b/dom/media/webaudio/MediaStreamTrackAudioSourceNode.cpp
@@ -82,16 +82,18 @@ void MediaStreamTrackAudioSourceNode::In
   }
 
   if (aMediaStreamTrack->Ended()) {
     // The track is ended and will never produce any data. Pretend like this is
     // fine.
     return;
   }
 
+  MarkActive();
+
   MediaTrackGraph* graph = Context()->Graph();
 
   AudioNodeEngine* engine = new MediaStreamTrackAudioSourceNodeEngine(this);
   mTrack = AudioNodeExternalInputTrack::Create(graph, engine);
 
   MOZ_ASSERT(mTrack);
 
   mInputTrack = aMediaStreamTrack;
--- a/dom/tests/mochitest/general/test_interfaces.js
+++ b/dom/tests/mochitest/general/test_interfaces.js
@@ -175,18 +175,16 @@ var interfaceNamesInGlobalScope = [
   { name: "AuthenticatorAttestationResponse" },
   // IMPORTANT: Do not change this list without review from a DOM peer!
   { name: "AuthenticatorResponse" },
   // IMPORTANT: Do not change this list without review from a DOM peer!
   { name: "BarProp", insecureContext: true },
   // IMPORTANT: Do not change this list without review from a DOM peer!
   { name: "BaseAudioContext", insecureContext: true },
   // IMPORTANT: Do not change this list without review from a DOM peer!
-  { name: "BatteryManager", insecureContext: true },
-  // IMPORTANT: Do not change this list without review from a DOM peer!
   { name: "BeforeUnloadEvent", insecureContext: true },
   // IMPORTANT: Do not change this list without review from a DOM peer!
   { name: "BiquadFilterNode", insecureContext: true },
   // IMPORTANT: Do not change this list without review from a DOM peer!
   { name: "Blob", insecureContext: true },
   // IMPORTANT: Do not change this list without review from a DOM peer!
   { name: "BlobEvent", insecureContext: true },
   // IMPORTANT: Do not change this list without review from a DOM peer!
--- a/dom/webidl/BatteryManager.webidl
+++ b/dom/webidl/BatteryManager.webidl
@@ -5,17 +5,17 @@
  *
  * The origin of this IDL file is
  * http://www.w3.org/TR/battery-status/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
-[Exposed=Window]
+[ChromeOnly, Exposed=Window]
 interface BatteryManager : EventTarget {
     readonly attribute boolean             charging;
     readonly attribute unrestricted double chargingTime;
     readonly attribute unrestricted double dischargingTime;
     readonly attribute double              level;
 
              attribute EventHandler        onchargingchange;
              attribute EventHandler        onchargingtimechange;
--- a/gfx/layers/wr/WebRenderBridgeParent.cpp
+++ b/gfx/layers/wr/WebRenderBridgeParent.cpp
@@ -1628,16 +1628,17 @@ bool WebRenderBridgeParent::ProcessWebRe
         mAsyncImageManager->ApplyAsyncImageForPipeline(
             op.pipelineId(), aTxn, txnForImageBridge,
             RenderRootForExternal(aRenderRoot));
         break;
       }
       case WebRenderParentCommand::TOpUpdatedAsyncImagePipeline: {
         const OpUpdatedAsyncImagePipeline& op =
             cmd.get_OpUpdatedAsyncImagePipeline();
+        aTxn.InvalidateRenderedFrame();
         mAsyncImageManager->ApplyAsyncImageForPipeline(
             op.pipelineId(), aTxn, txnForImageBridge,
             RenderRootForExternal(aRenderRoot));
         break;
       }
       case WebRenderParentCommand::TCompositableOperation: {
         if (!ReceiveCompositableUpdate(cmd.get_CompositableOperation())) {
           NS_ERROR("ReceiveCompositableUpdate failed");
--- a/gfx/layers/wr/WebRenderCanvasRenderer.cpp
+++ b/gfx/layers/wr/WebRenderCanvasRenderer.cpp
@@ -78,18 +78,19 @@ void WebRenderCanvasRendererAsync::Destr
   if (mPipelineId.isSome()) {
     mManager->RemovePipelineIdForCompositable(mPipelineId.ref());
     mPipelineId.reset();
   }
 }
 
 void WebRenderCanvasRendererAsync::
     UpdateCompositableClientForEmptyTransaction() {
+  bool wasDirty = IsDirty();
   UpdateCompositableClient(mManager->GetRenderRoot());
-  if (mPipelineId.isSome()) {
+  if (wasDirty && mPipelineId.isSome()) {
     // Notify an update of async image pipeline during empty transaction.
     // During non empty transaction, WebRenderBridgeParent receives
     // OpUpdateAsyncImagePipeline message, but during empty transaction, the
     // message is not sent to WebRenderBridgeParent. Then
     // OpUpdatedAsyncImagePipeline is used to notify the update.
     mManager->AddWebRenderParentCommand(
         OpUpdatedAsyncImagePipeline(mPipelineId.ref()));
   }
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/onEnterFrame-generator-08.js
@@ -0,0 +1,17 @@
+// Bug 1556033. Test behavior of onEnterFrame "return" completion
+// values during explicit .throw() calls.
+
+let g = newGlobal({newCompartment: true});
+g.eval(`function* f(x) { }`);
+let dbg = new Debugger(g);
+
+let it = g.f();
+
+dbg.onEnterFrame = () => ({ return: "exit" });
+const result = it.throw();
+assertEq(result.value, "exit");
+assertEq(result.done, true);
+
+const result2 = it.next();
+assertEq(result2.value, undefined);
+assertEq(result2.done, true);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/onEnterFrame-generator-09.js
@@ -0,0 +1,17 @@
+// Bug 1556033. Test behavior of onEnterFrame "return" completion
+// values during explicit .return() calls.
+
+let g = newGlobal({newCompartment: true});
+g.eval(`function* f(x) { }`);
+let dbg = new Debugger(g);
+
+let it = g.f();
+
+dbg.onEnterFrame = () => ({ return: "exit" });
+const result = it.return();
+assertEq(result.value, "exit");
+assertEq(result.done, true);
+
+const result2 = it.next();
+assertEq(result2.value, undefined);
+assertEq(result2.done, true);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/onEnterFrame-generator-10.js
@@ -0,0 +1,17 @@
+// Bug 1556033. Test behavior of onEnterFrame "return" completion
+// values during explicit .next() calls.
+
+let g = newGlobal({newCompartment: true});
+g.eval(`function* f(x) { }`);
+let dbg = new Debugger(g);
+
+let it = g.f();
+
+dbg.onEnterFrame = () => ({ return: "exit" });
+const result = it.next();
+assertEq(result.value, "exit");
+assertEq(result.done, true);
+
+const result2 = it.next();
+assertEq(result2.value, undefined);
+assertEq(result2.done, true);
--- a/js/src/jit-test/tests/wasm/multi-value/block-validate.js
+++ b/js/src/jit-test/tests/wasm/multi-value/block-validate.js
@@ -186,8 +186,81 @@ wasmValidateText(`
         }
         return s;
        })()}
     (func (result i32)
       (i32.add
         (block (result i32 i32)
           (i32.const 32)
           (i32.const 10)))))`);
+
+wasmValidateText(`
+  (module
+    (func (result i32)
+      (block $B (result i32 i32)
+        (br $B (i32.const 1) (i32.const 2))
+        (i32.const 0x1337))
+      (drop)))`);
+
+wasmValidateText(`
+  (module
+    (func (result i32)
+      (block $B (result i32 i32)
+        (i32.const 1)
+        (br $B (i32.const 2))
+        (i32.const 0x1337))
+      (drop)))`);
+
+wasmValidateText(`
+  (module
+    (func (result i32)
+      (block $B (result i32 i32)
+        (i32.const 1)
+        (i32.const 2)
+        (br $B)
+        (i32.const 0x1337))
+      (drop)))`);
+
+wasmValidateText(`
+  (module
+    (func (param $cond i32) (result i32)
+      (block $B (result i32 i32)
+        (br_if $B (i32.const 1) (i32.const 2) (local.get $cond)))
+      (drop)))`);
+
+wasmValidateText(`
+  (module
+    (func (param $cond i32) (result i32)
+      (block $B (result i32 i32)
+        (i32.const 1)
+        (br_if $B (i32.const 2) (local.get $cond)))
+      (drop)))`);
+
+wasmValidateText(`
+  (module
+    (func (param $cond i32) (result i32)
+      (block $B (result i32 i32)
+        (i32.const 1)
+        (i32.const 2)
+        (br_if $B (local.get $cond)))
+      (drop)))`);
+
+wasmValidateText(`
+  (module
+    (func (param $cond i32) (result i32)
+      (block $B (result i32 i32)
+        (i32.const 1)
+        (i32.const 2)
+        (local.get $cond)
+        (br_if $B))
+      (drop)))`);
+
+wasmValidateText(`
+  (module
+    (func (param $index i32) (result i32)
+      (block $OUT (result i32)
+        (block $B1 (result i32 i32)
+          (block $B2 (result i32 i32)
+            (block $B3 (result i32 i32)
+              (br_table $B1 $B2 $B3 (i32.const 1) (i32.const 2) (local.get $index)))
+            (br $OUT (i32.add)))
+          (br $OUT (i32.sub)))
+        (br $OUT (i32.mul)))))`);
--- a/js/src/jit/BaselineCodeGen.cpp
+++ b/js/src/jit/BaselineCodeGen.cpp
@@ -6023,17 +6023,35 @@ bool BaselineCodeGen<Handler>::emit_JSOP
 
 template <>
 bool BaselineInterpreterCodeGen::emitGeneratorThrowOrReturnCallVM() {
   // Record the offset for the Baseline JIT code below.
   handler.setGeneratorThrowOrReturnCallOffset(masm.currentOffset());
 
   using Fn = bool (*)(JSContext*, BaselineFrame*,
                       Handle<AbstractGeneratorObject*>, HandleValue, uint32_t);
-  return callVM<Fn, jit::GeneratorThrowOrReturn>();
+  if (!callVM<Fn, jit::GeneratorThrowOrReturn>()) {
+    return false;
+  }
+
+  // Control only flows out of GeneratorThrowOrReturn if the debugger
+  // overrode the function resumption with an explicit return value, so here
+  // we want to do all of the cleanup on the baseline frame that _would_ have
+  // happened inside the epilogue of the baseline frame execution.
+
+  // Save the frame's return value to return from resume.
+  masm.loadValue(frame.addressOfReturnValue(), JSReturnOperand);
+
+  // Remove the baseline frame from the stack.
+  masm.moveToStackPtr(BaselineFrameReg);
+  masm.pop(BaselineFrameReg);
+
+  masm.ret();
+
+  return true;
 }
 
 template <>
 bool BaselineCompilerCodeGen::emitGeneratorThrowOrReturnCallVM() {
   // Jump to the interpreter code where we call GeneratorThrowOrReturn. This way
   // we ensure we have a sane return address (into the interpreter code instead
   // of the self-hosted code's BaselineScript) because we turn the generator
   // frame into an interpreter frame in jit::GeneratorThrowOrReturn.
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -1020,17 +1020,20 @@ bool GeneratorThrowOrReturn(JSContext* c
 
   bool mustReturn = false;
   if (!DebugAfterYield(cx, frame, pc, &mustReturn)) {
     return false;
   }
 
   GeneratorResumeKind resumeKind = GeneratorResumeKind(resumeKindArg);
   if (mustReturn) {
-    resumeKind = GeneratorResumeKind::Return;
+    // This function always returns false for the traditional JS control
+    // flow for throw, return and terminate, so we can use the true case to
+    // indicate an explicit return via the debugger.
+    return true;
   }
 
   MOZ_ALWAYS_FALSE(
       js::GeneratorThrowOrReturn(cx, frame, genObj, arg, resumeKind));
   return false;
 }
 
 bool GlobalNameConflictsCheckFromIon(JSContext* cx, HandleScript script) {
--- a/js/src/make-source-package.sh
+++ b/js/src/make-source-package.sh
@@ -2,17 +2,18 @@
 
 # Find out ASAP if some command breaks here, because we're copying a lot of
 # files we don't actually maintain ourselves, and requirements could easily be
 # broken.
 set -e
 
 : ${MKDIR:=mkdir}
 : ${TAR:=tar}
-: ${AUTOCONF:=$(which autoconf-2.13 autoconf2.13 | head -1)}
+: ${RSYNC:=rsync}
+: ${AUTOCONF:=$(which autoconf-2.13 autoconf2.13 autoconf213 | head -1)}
 : ${SRCDIR:=$(cd $(dirname $0); pwd 2>/dev/null)}
 : ${MOZJS_NAME:=mozjs}
 # The place to gather files to be added to the tarball.
 : ${STAGING:=/tmp/mozjs-src-pkg}
 # The place to put the resulting tarball.
 : ${DIST:=/tmp}
 
 if [[ -f "$SRCDIR/../../config/milestone.txt" ]]; then
@@ -30,16 +31,17 @@ pkg="${version}.tar.bz2"
 pkgpath="${DIST}/${pkg}"
 taropts="-jcf"
 
 # need these environment vars:
 echo "Environment:"
 echo "    MAKE = $MAKE"
 echo "    MKDIR = $MKDIR"
 echo "    TAR = $TAR"
+echo "    RSYNC = $RSYNC"
 echo "    AUTOCONF = $AUTOCONF"
 echo "    STAGING = $STAGING"
 echo "    DIST = $DIST"
 echo "    SRCDIR = $SRCDIR"
 echo "    MOZJS_NAME = $MOZJS_NAME"
 echo "    MOZJS_MAJOR_VERSION = $MOZJS_MAJOR_VERSION"
 echo "    MOZJS_MINOR_VERSION = $MOZJS_MINOR_VERSION"
 echo "    MOZJS_PATCH_VERSION = $MOZJS_PATCH_VERSION"
@@ -50,156 +52,106 @@ TOPSRCDIR=${SRCDIR}/../..
 
 case $cmd in
 "clean")
     echo "Cleaning ${pkgpath} and ${tgtpath} ..."
     rm -rf ${pkgpath} ${tgtpath}
     ;;
 "build")
     # Make sure that everything copied here is kept in sync with
-    # `testing/taskcluster/tasks/branches/base_jobs.yml`!
+    # `taskcluster/ci/spidermonkey/kind.yml`!
 
     if [ -e ${tgtpath}/js/src/Makefile ]; then
         echo "error: found js/src/Makefile. Please clean before packaging." >&2
         exit 1
     fi
 
     echo "Staging source tarball in ${tgtpath}..."
     if [ -d ${tgtpath} ]; then
         echo "WARNING - dist tree ${tgtpath} already exists!"
     fi
 
     ${MKDIR} -p ${tgtpath}
 
-    # Top-level config and build files
-    cp -pPR \
-        ${TOPSRCDIR}/LICENSE \
-        ${TOPSRCDIR}/Makefile.in \
-        ${TOPSRCDIR}/configure.py \
-        ${TOPSRCDIR}/moz.build \
-        ${TOPSRCDIR}/moz.configure \
-        ${TOPSRCDIR}/test.mozbuild \
-        ${tgtpath}/
+    rsync \
+        --delete-excluded \
+        --prune-empty-dirs \
+        --quiet \
+        --recursive \
+        "${TOPSRCDIR}"/ ${tgtpath}/ \
+        --filter=". -" <<FILTER_EOF
+# Top-level config and build files
 
-    # Additional libraries (optionally) used by SpiderMonkey
-    cp -pPR \
-        ${TOPSRCDIR}/mfbt \
-        ${TOPSRCDIR}/nsprpub \
-        ${tgtpath}/
-
-    ${MKDIR} -p ${tgtpath}/intl
-    cp -pPR \
-        ${TOPSRCDIR}/intl/icu \
-        ${tgtpath}/intl/
-
-    ${MKDIR} -p ${tgtpath}/memory
-    cp -pPR \
-        ${TOPSRCDIR}/memory/moz.build \
-        ${TOPSRCDIR}/memory/build \
-        ${TOPSRCDIR}/memory/mozalloc \
-        ${tgtpath}/memory/
++ /configure.py
++ /LICENSE
++ /Makefile.in
++ /moz.build
++ /moz.configure
++ /test.mozbuild
 
-    ${MKDIR} -p ${tgtpath}/modules
-    cp -pPR \
-        ${TOPSRCDIR}/modules/fdlibm \
-        ${TOPSRCDIR}/modules/zlib \
-        ${tgtpath}/modules/
+# Additional libraries (optionally) used by SpiderMonkey
 
-    ${MKDIR} -p ${tgtpath}/mozglue
-    cp -pPR \
-        ${TOPSRCDIR}/mozglue/baseprofiler \
-        ${TOPSRCDIR}/mozglue/build \
-        ${TOPSRCDIR}/mozglue/misc \
-        ${TOPSRCDIR}/mozglue/moz.build \
-        ${TOPSRCDIR}/mozglue/static \
-        ${tgtpath}/mozglue/
++ /mfbt/**
++ /nsprpub/**
 
-    ${MKDIR} -p ${tgtpath}/tools/fuzzing
-    cp -pPR \
-        ${TOPSRCDIR}/tools/fuzzing/moz.build \
-        ${TOPSRCDIR}/tools/fuzzing/interface \
-        ${TOPSRCDIR}/tools/fuzzing/registry \
-        ${TOPSRCDIR}/tools/fuzzing/libfuzzer \
-        ${tgtpath}/tools/fuzzing/
+- /intl/icu/source/data
+- /intl/icu/source/test
+- /intl/icu/source/tools
++ /intl/icu/**
 
++ /memory/build/**
++ /memory/moz.build
++ /memory/mozalloc/**
 
-    # Build system and dependencies
-    cp -pPR \
-        ${TOPSRCDIR}/build \
-        ${TOPSRCDIR}/config \
-        ${TOPSRCDIR}/python \
-        ${tgtpath}/
++ /modules/fdlibm/**
++ /modules/zlib/**
 
-    ${MKDIR} -p ${tgtpath}/.cargo
-    cp -pPR \
-        ${TOPSRCDIR}/.cargo/config.in \
-        ${tgtpath}/.cargo/
-
-    ${MKDIR} -p ${tgtpath}/third_party
-    cp -pPR \
-        ${TOPSRCDIR}/third_party/python \
-        ${TOPSRCDIR}/third_party/rust \
-        ${tgtpath}/third_party/
++ /mozglue/baseprofiler/**
++ /mozglue/build/**
++ /mozglue/misc/**
++ /mozglue/moz.build
++ /mozglue/static/**
 
-    ${MKDIR} -p ${tgtpath}/dom/bindings
-    cp -pPR \
-        ${TOPSRCDIR}/dom/bindings/mozwebidlcodegen \
-        ${tgtpath}/dom/bindings/
-
-    ${MKDIR} -p ${tgtpath}/layout/tools/reftest
-    cp -pPR \
-        ${TOPSRCDIR}/layout/tools/reftest/reftest \
-        ${tgtpath}/layout/tools/reftest/
++ /tools/fuzzing/moz.build
++ /tools/fuzzing/interface/**
++ /tools/fuzzing/registry/**
++ /tools/fuzzing/libfuzzer/**
 
-    ${MKDIR} -p ${tgtpath}/testing
-    cp -pPR \
-        ${TOPSRCDIR}/testing/mozbase \
-        ${tgtpath}/testing/
+# Build system and dependencies
 
-    ${MKDIR} -p ${tgtpath}/testing/web-platform/tests
-    cp -pPR \
-        ${TOPSRCDIR}/testing/web-platform/tests/streams \
-        ${tgtpath}/testing/web-platform/tests/
++ /build/**
++ /config/**
++ /python/**
+
++ /.cargo/config.in
 
-    ${MKDIR} -p ${tgtpath}/taskcluster/taskgraph
-    cp -pPR \
-        ${TOPSRCDIR}/taskcluster/moz.build \
-        ${tgtpath}/taskcluster/
-    cp -pPR \
-        ${TOPSRCDIR}/taskcluster/taskgraph/test \
-        ${tgtpath}/taskcluster/taskgraph/
+- /third_party/python/gyp
++ /third_party/python/**
++ /third_party/rust/**
+
++ /layout/tools/reftest/reftest/**
 
-    ${MKDIR} -p ${tgtpath}/toolkit/crashreporter/tools
-    cp -pPR \
-        ${TOPSRCDIR}/toolkit/crashreporter/tools/symbolstore.py \
-        ${tgtpath}/toolkit/crashreporter/tools/
++ /testing/mozbase/**
++ /testing/web-platform/tests/streams/**
+
++ /toolkit/crashreporter/tools/symbolstore.py
++ /toolkit/mozapps/installer/package-name.mk
 
-    ${MKDIR} -p ${tgtpath}/toolkit/mozapps/installer
-    cp -pPR \
-        ${TOPSRCDIR}/toolkit/mozapps/installer/package-name.mk \
-        ${TOPSRCDIR}/toolkit/mozapps/installer/upload-files.mk \
-        ${tgtpath}/toolkit/mozapps/installer/
-
-
-    # SpiderMonkey itself
+# SpiderMonkey itself
 
-    ${MKDIR} -p ${tgtpath}/js/src
-    cp -pPR \
-        ${TOPSRCDIR}/js/app.mozbuild \
-        ${TOPSRCDIR}/js/*.configure \
-        ${tgtpath}/js/
-    cp -pPR \
-        ${TOPSRCDIR}/js/examples \
-        ${TOPSRCDIR}/js/public \
-        ${TOPSRCDIR}/js/rust \
-        ${tgtpath}/js/
++ /js/src/**
++ /js/app.mozbuild
++ /js/*.configure
++ /js/examples/**
++ /js/public/**
++ /js/rust/**
 
-    find ${TOPSRCDIR}/js/src -mindepth 1 -maxdepth 1 -not -path ${STAGING} -a -not -name ${pkg} \
-        -exec cp -pPR {} ${tgtpath}/js/src/ \;
-
++ */
+- /**
+FILTER_EOF
 
     # Generate configure files to avoid build dependency on autoconf-2.13
     cp -pPR ${TOPSRCDIR}/js/src/configure.in ${tgtpath}/js/src/configure
     chmod a+x ${tgtpath}/js/src/configure
     ${AUTOCONF} --localdir=${TOPSRCDIR}/js/src \
         ${TOPSRCDIR}/js/src/old-configure.in >${tgtpath}/js/src/old-configure
 
     # Copy or create INSTALL
@@ -244,20 +196,16 @@ README_EOF
         cp -pPR ${STAGING}/patches ${tgtpath}/
     elif [ -d ${TOPSRCDIR}/patches ]; then
         cp -pPR ${TOPSRCDIR}/patches ${tgtpath}/
     fi
 
     # Remove *.pyc and *.pyo files if any
     find ${tgtpath} -type f -name "*.pyc" -o -name "*.pyo" | xargs rm -f
 
-    # Remove non-JS Cargo.toml files (for example, the invalid Cargo.toml files
-    # used for some testing).
-    find ${tgtpath} -type f -name Cargo.toml | grep -v js | xargs rm -f
-
     # Roll the tarball
     echo "Packaging source tarball at ${pkgpath}..."
     ${TAR} $taropts ${pkgpath} -C ${STAGING} ${version}
     ;;
 *)
     echo "Unrecognized command: $cmd"
     ;;
 esac
--- a/js/src/wasm/WasmAST.h
+++ b/js/src/wasm/WasmAST.h
@@ -544,32 +544,27 @@ class AstBlock : public AstExpr {
   Op op() const { return op_; }
   AstName name() const { return name_; }
   const AstExprVector& exprs() const { return exprs_; }
   AstBlockType& type() { return type_; }
 };
 
 class AstBranch : public AstExpr {
   Op op_;
-  AstExpr* cond_;
   AstRef target_;
-  AstExpr* value_;
+  AstExprVector values_;  // Includes the condition, for br_if
 
  public:
   static const AstExprKind Kind = AstExprKind::Branch;
-  explicit AstBranch(Op op, AstExpr* cond, AstRef target, AstExpr* value)
-      : AstExpr(Kind), op_(op), cond_(cond), target_(target), value_(value) {}
+  explicit AstBranch(Op op, AstRef target, AstExprVector&& values)
+      : AstExpr(Kind), op_(op), target_(target), values_(std::move(values)) {}
 
   Op op() const { return op_; }
   AstRef& target() { return target_; }
-  AstExpr& cond() const {
-    MOZ_ASSERT(cond_);
-    return *cond_;
-  }
-  AstExpr* maybeValue() const { return value_; }
+  AstExprVector& values() { return values_; }
 };
 
 class AstCall : public AstExpr {
   Op op_;
   AstRef func_;
   AstExprVector args_;
 
  public:
@@ -1056,34 +1051,31 @@ class AstMemoryGrow final : public AstEx
  public:
   static const AstExprKind Kind = AstExprKind::MemoryGrow;
   explicit AstMemoryGrow(AstExpr* operand) : AstExpr(Kind), operand_(operand) {}
 
   AstExpr* operand() const { return operand_; }
 };
 
 class AstBranchTable : public AstExpr {
-  AstExpr& index_;
   AstRef default_;
   AstRefVector table_;
-  AstExpr* value_;
+  AstExprVector values_;
 
  public:
   static const AstExprKind Kind = AstExprKind::BranchTable;
-  explicit AstBranchTable(AstExpr& index, AstRef def, AstRefVector&& table,
-                          AstExpr* maybeValue)
+  explicit AstBranchTable(AstRef def, AstRefVector&& table,
+                          AstExprVector&& values)
       : AstExpr(Kind),
-        index_(index),
         default_(def),
         table_(std::move(table)),
-        value_(maybeValue) {}
-  AstExpr& index() const { return index_; }
+        values_(std::move(values)) {}
   AstRef& def() { return default_; }
   AstRefVector& table() { return table_; }
-  AstExpr* maybeValue() { return value_; }
+  AstExprVector& values() { return values_; }
 };
 
 class AstFunc : public AstNode {
   AstName name_;
   AstRef funcType_;
   AstValTypeVector vars_;
   AstNameVector localNames_;
   AstExprVector body_;
--- a/js/src/wasm/WasmTextToBinary.cpp
+++ b/js/src/wasm/WasmTextToBinary.cpp
@@ -2604,81 +2604,57 @@ static AstBlock* ParseBlock(WasmParseCon
     }
     result =
         new (c.lifo) AstBlock(Op::Block, type, otherName, std::move(exprs));
   }
 
   return result;
 }
 
-static AstBranch* ParseBranch(WasmParseContext& c, Op op, bool inParens) {
-  MOZ_ASSERT(op == Op::Br || op == Op::BrIf);
-
-  AstRef target;
-  if (!c.ts.matchRef(&target, c.error)) {
-    return nullptr;
-  }
-
-  AstExpr* value = nullptr;
-  if (inParens) {
-    if (c.ts.getIf(WasmToken::OpenParen)) {
-      value = ParseExprInsideParens(c);
-      if (!value) {
-        return nullptr;
-      }
-      if (!c.ts.match(WasmToken::CloseParen, c.error)) {
-        return nullptr;
-      }
-    }
-  }
-
-  AstExpr* cond = nullptr;
-  if (op == Op::BrIf) {
-    if (inParens && c.ts.getIf(WasmToken::OpenParen)) {
-      cond = ParseExprInsideParens(c);
-      if (!cond) {
-        return nullptr;
-      }
-      if (!c.ts.match(WasmToken::CloseParen, c.error)) {
-        return nullptr;
-      }
-    } else {
-      cond = new (c.lifo) AstPop();
-      if (!cond) {
-        return nullptr;
-      }
-    }
-  }
-
-  return new (c.lifo) AstBranch(op, cond, target, value);
-}
-
-static bool ParseArgs(WasmParseContext& c, AstExprVector* args) {
+static bool ParseExprs(WasmParseContext& c, AstExprVector* exprs) {
   while (c.ts.getIf(WasmToken::OpenParen)) {
-    AstExpr* arg = ParseExprInsideParens(c);
-    if (!arg || !args->append(arg)) {
+    AstExpr* expr = ParseExprInsideParens(c);
+    if (!expr || !exprs->append(expr)) {
       return false;
     }
     if (!c.ts.match(WasmToken::CloseParen, c.error)) {
       return false;
     }
   }
 
   return true;
 }
 
+static AstBranch* ParseBranch(WasmParseContext& c, Op op, bool inParens) {
+  MOZ_ASSERT(op == Op::Br || op == Op::BrIf);
+
+  AstRef target;
+  if (!c.ts.matchRef(&target, c.error)) {
+    return nullptr;
+  }
+
+  AstExprVector values(c.lifo);
+  if (inParens) {
+    if (!ParseExprs(c, &values)) {
+      return nullptr;
+    }
+  }
+
+  return new (c.lifo) AstBranch(op, target, std::move(values));
+}
+
 static AstCall* ParseCall(WasmParseContext& c, bool inParens) {
   AstRef func;
   if (!c.ts.matchRef(&func, c.error)) {
     return nullptr;
   }
 
   AstExprVector args(c.lifo);
   if (inParens) {
-    if (!ParseArgs(c, &args)) {
+    if (!ParseExprs(c, &args)) {
       return nullptr;
     }
   }
 
   return new (c.lifo) AstCall(Op::Call, func, std::move(args));
 }
 
 static AstCallIndirect* ParseCallIndirect(WasmParseContext& c, bool inParens) {
@@ -2698,17 +2674,17 @@ static AstCallIndirect* ParseCallIndirec
     funcType = secondRef;
   } else {
     funcType = firstRef;
   }
 
   AstExprVector args(c.lifo);
   AstExpr* index;
   if (inParens) {
-    if (!ParseArgs(c, &args)) {
+    if (!ParseExprs(c, &args)) {
       return nullptr;
     }
 
     if (args.empty()) {
       index = new (c.lifo) AstPop();
     } else {
       index = args.popCopy();
     }
@@ -3726,36 +3702,24 @@ static AstBranchTable* ParseBranchTable(
 
   if (table.empty()) {
     c.ts.generateError(c.ts.get(), c.error);
     return nullptr;
   }
 
   AstRef def = table.popCopy();
 
-  AstExpr* index = ParseExpr(c, inParens);
-  if (!index) {
-    return nullptr;
-  }
-
-  AstExpr* value = nullptr;
+  AstExprVector values(c.lifo);
   if (inParens) {
-    if (c.ts.getIf(WasmToken::OpenParen)) {
-      value = index;
-      index = ParseExprInsideParens(c);
-      if (!index) {
-        return nullptr;
-      }
-      if (!c.ts.match(WasmToken::CloseParen, c.error)) {
-        return nullptr;
-      }
-    }
-  }
-
-  return new (c.lifo) AstBranchTable(*index, def, std::move(table), value);
+    if (!ParseExprs(c, &values)) {
+      return nullptr;
+    }
+  }
+
+  return new (c.lifo) AstBranchTable(def, std::move(table), std::move(values));
 }
 
 static AstMemoryGrow* ParseMemoryGrow(WasmParseContext& c, bool inParens) {
   AstExpr* operand = ParseExpr(c, inParens);
   if (!operand) {
     return nullptr;
   }
 
@@ -3986,17 +3950,17 @@ static AstTableSize* ParseTableSize(Wasm
 static AstExpr* ParseStructNew(WasmParseContext& c, bool inParens) {
   AstRef typeDef;
   if (!c.ts.matchRef(&typeDef, c.error)) {
     return nullptr;
   }
 
   AstExprVector args(c.lifo);
   if (inParens) {
-    if (!ParseArgs(c, &args)) {
+    if (!ParseExprs(c, &args)) {
       return nullptr;
     }
   }
 
   // An AstRef cast to AstValType turns into a Ref type, which is exactly what
   // we need here.
 
   return new (c.lifo) AstStructNew(typeDef, std::move(args));
@@ -5613,55 +5577,39 @@ static bool ResolveSelect(Resolver& r, A
          ResolveExpr(r, *b.op2());
 }
 
 static bool ResolveBranch(Resolver& r, AstBranch& br) {
   if (!r.resolveBranchTarget(br.target())) {
     return false;
   }
 
-  if (br.maybeValue() && !ResolveExpr(r, *br.maybeValue())) {
-    return false;
-  }
-
-  if (br.op() == Op::BrIf) {
-    if (!ResolveExpr(r, br.cond())) {
-      return false;
-    }
-  }
-
-  return true;
-}
-
-static bool ResolveArgs(Resolver& r, const AstExprVector& args) {
-  for (AstExpr* arg : args) {
-    if (!ResolveExpr(r, *arg)) {
-      return false;
-    }
+  if (!ResolveExprList(r, br.values())) {
+    return false;
   }
 
   return true;
 }
 
 static bool ResolveCall(Resolver& r, AstCall& c) {
   MOZ_ASSERT(c.op() == Op::Call);
 
-  if (!ResolveArgs(r, c.args())) {
+  if (!ResolveExprList(r, c.args())) {
     return false;
   }
 
   if (!r.resolveFunction(c.func())) {
     return false;
   }
 
   return true;
 }
 
 static bool ResolveCallIndirect(Resolver& r, AstCallIndirect& c) {
-  if (!ResolveArgs(r, c.args())) {
+  if (!ResolveExprList(r, c.args())) {
     return false;
   }
 
   if (!ResolveExpr(r, *c.index())) {
     return false;
   }
 
   if (!r.resolveSignature(c.funcType())) {
@@ -5793,21 +5741,17 @@ static bool ResolveBranchTable(Resolver&
   }
 
   for (AstRef& elem : bt.table()) {
     if (!r.resolveBranchTarget(elem)) {
       return false;
     }
   }
 
-  if (bt.maybeValue() && !ResolveExpr(r, *bt.maybeValue())) {
-    return false;
-  }
-
-  return ResolveExpr(r, bt.index());
+  return ResolveExprList(r, bt.values());
 }
 
 static bool ResolveAtomicCmpXchg(Resolver& r, AstAtomicCmpXchg& s) {
   return ResolveLoadStoreAddress(r, s.address()) &&
          ResolveExpr(r, s.expected()) && ResolveExpr(r, s.replacement());
 }
 
 static bool ResolveAtomicLoad(Resolver& r, AstAtomicLoad& l) {
@@ -5872,17 +5816,17 @@ static bool ResolveTableSet(Resolver& r,
 
 static bool ResolveTableSize(Resolver& r, AstTableSize& s) {
   return r.resolveTable(s.targetTable());
 }
 #endif
 
 #ifdef ENABLE_WASM_GC
 static bool ResolveStructNew(Resolver& r, AstStructNew& s) {
-  if (!ResolveArgs(r, s.fieldValues())) {
+  if (!ResolveExprList(r, s.fieldValues())) {
     return false;
   }
 
   if (!r.resolveType(s.structType())) {
     return false;
   }
 
   return true;
@@ -6323,26 +6267,18 @@ static bool EncodeBlock(Encoder& e, AstB
   }
 
   return true;
 }
 
 static bool EncodeBranch(Encoder& e, AstBranch& br) {
   MOZ_ASSERT(br.op() == Op::Br || br.op() == Op::BrIf);
 
-  if (br.maybeValue()) {
-    if (!EncodeExpr(e, *br.maybeValue())) {
-      return false;
-    }
-  }
-
-  if (br.op() == Op::BrIf) {
-    if (!EncodeExpr(e, br.cond())) {
-      return false;
-    }
+  if (!EncodeExprList(e, br.values())) {
+    return false;
   }
 
   if (!e.writeOp(br.op())) {
     return false;
   }
 
   if (!e.writeVarU32(br.target().index())) {
     return false;
@@ -6350,44 +6286,34 @@ static bool EncodeBranch(Encoder& e, Ast
 
   return true;
 }
 
 static bool EncodeFirst(Encoder& e, AstFirst& f) {
   return EncodeExprList(e, f.exprs());
 }
 
-static bool EncodeArgs(Encoder& e, const AstExprVector& args) {
-  for (AstExpr* arg : args) {
-    if (!EncodeExpr(e, *arg)) {
-      return false;
-    }
-  }
-
-  return true;
-}
-
 static bool EncodeCall(Encoder& e, AstCall& c) {
-  if (!EncodeArgs(e, c.args())) {
+  if (!EncodeExprList(e, c.args())) {
     return false;
   }
 
   if (!e.writeOp(c.op())) {
     return false;
   }
 
   if (!e.writeVarU32(c.func().index())) {
     return false;
   }
 
   return true;
 }
 
 static bool EncodeCallIndirect(Encoder& e, AstCallIndirect& c) {
-  if (!EncodeArgs(e, c.args())) {
+  if (!EncodeExprList(e, c.args())) {
     return false;
   }
 
   if (!EncodeExpr(e, *c.index())) {
     return false;
   }
 
   if (!e.writeOp(Op::CallIndirect)) {
@@ -6545,23 +6471,17 @@ static bool EncodeReturn(Encoder& e, Ast
   if (!e.writeOp(Op::Return)) {
     return false;
   }
 
   return true;
 }
 
 static bool EncodeBranchTable(Encoder& e, AstBranchTable& bt) {
-  if (bt.maybeValue()) {
-    if (!EncodeExpr(e, *bt.maybeValue())) {
-      return false;
-    }
-  }
-
-  if (!EncodeExpr(e, bt.index())) {
+  if (!EncodeExprList(e, bt.values())) {
     return false;
   }
 
   if (!e.writeOp(Op::BrTable)) {
     return false;
   }
 
   if (!e.writeVarU32(bt.table().length())) {
@@ -6695,17 +6615,17 @@ static bool EncodeTableSet(Encoder& e, A
 
 static bool EncodeTableSize(Encoder& e, AstTableSize& s) {
   return e.writeOp(MiscOp::TableSize) && e.writeVarU32(s.targetTable().index());
 }
 #endif
 
 #ifdef ENABLE_WASM_GC
 static bool EncodeStructNew(Encoder& e, AstStructNew& s) {
-  if (!EncodeArgs(e, s.fieldValues())) {
+  if (!EncodeExprList(e, s.fieldValues())) {
     return false;
   }
 
   if (!e.writeOp(MiscOp::StructNew)) {
     return false;
   }
 
   if (!e.writeVarU32(s.structType().index())) {
--- a/layout/generic/nsFlexContainerFrame.cpp
+++ b/layout/generic/nsFlexContainerFrame.cpp
@@ -1789,18 +1789,29 @@ void nsFlexContainerFrame::MarkCachedFle
     nsIFrame* aItemFrame) {
   aItemFrame->DeleteProperty(CachedFlexMeasuringReflow());
 }
 
 const CachedMeasuringReflowResult&
 nsFlexContainerFrame::MeasureAscentAndBSizeForFlexItem(
     FlexItem& aItem, nsPresContext* aPresContext,
     ReflowInput& aChildReflowInput) {
-  if (const auto* cachedResult =
-          aItem.Frame()->GetProperty(CachedFlexMeasuringReflow())) {
+  if (HasAnyStateBits(NS_STATE_FLEX_MEASUREMENTS_INTERRUPTED) &&
+      !aPresContext->HasPendingInterrupt()) {
+    // Our measurements are from an earlier reflow which was interrupted.
+    // (and the current reflow is not [yet] interrupted, so we have a chance
+    // to maybe get a more accurate measurement now).
+    // Purge our potentially-invalid item measurements.
+    for (nsIFrame* frame : mFrames) {
+      frame->DeleteProperty(CachedFlexMeasuringReflow());
+    }
+    RemoveStateBits(NS_STATE_FLEX_MEASUREMENTS_INTERRUPTED);
+    FLEX_LOG("[perf] MeasureAscentAndBSizeForFlexItem purged cached values");
+  } else if (const auto* cachedResult =
+                 aItem.Frame()->GetProperty(CachedFlexMeasuringReflow())) {
     if (cachedResult->IsValidFor(aChildReflowInput)) {
       return *cachedResult;
     }
     FLEX_LOG("[perf] MeasureAscentAndBSizeForFlexItem rejected cached value");
   } else {
     FLEX_LOG(
         "[perf] MeasureAscentAndBSizeForFlexItem didn't have a cached value");
   }
@@ -4288,26 +4299,27 @@ void FlexLine::PositionItemsInCrossAxis(
 
     // Back out to cross-axis edge of the line.
     lineCrossAxisPosnTracker.ResetPosition();
   }
 }
 
 void nsFlexContainerFrame::DidReflow(nsPresContext* aPresContext,
                                      const ReflowInput* aReflowInput) {
-  // Remove the cached values if we got an interrupt because the values will be
-  // the wrong ones for following reflows.
+  // If we got an interrupt, we make a note here that our cached measurements
+  // are potentially invalid, because our descendant block frames' reflows may
+  // have bailed out early due to the interrupt.  We'll keep these invalid
+  // measurements for the rest of this reflow (to avoid repeating the same
+  // bogus measurement), and purge them on the next (non-interrupted) reflow.
   //
   // TODO(emilio): Can we do this only for the kids that are interrupted? We
   // probably want to figure out what the right thing to do here is regarding
   // interrupts, see bug 1495532.
   if (aPresContext->HasPendingInterrupt()) {
-    for (nsIFrame* frame : mFrames) {
-      frame->DeleteProperty(CachedFlexMeasuringReflow());
-    }
+    AddStateBits(NS_STATE_FLEX_MEASUREMENTS_INTERRUPTED);
   }
   nsContainerFrame::DidReflow(aPresContext, aReflowInput);
 }
 
 void nsFlexContainerFrame::Reflow(nsPresContext* aPresContext,
                                   ReflowOutput& aDesiredSize,
                                   const ReflowInput& aReflowInput,
                                   nsReflowStatus& aStatus) {
--- a/layout/generic/nsFrameStateBits.h
+++ b/layout/generic/nsFrameStateBits.h
@@ -349,16 +349,24 @@ FRAME_STATE_BIT(FlexContainer, 22, NS_ST
 
 // True if the container has no flex items; may lie if there is a pending reflow
 FRAME_STATE_BIT(FlexContainer, 23, NS_STATE_FLEX_SYNTHESIZE_BASELINE)
 
 // True if any flex item in the container has a line with a
 // -webkit-line-ellipsis marker.
 FRAME_STATE_BIT(FlexContainer, 24, NS_STATE_FLEX_HAS_LINE_CLAMP_ELLIPSIS)
 
+// True if the flex container's items' cached measurements are potentially
+// invalid, due to them having been computed during a reflow that was
+// interrupted.  (We may still use those invalid measurements for the rest of
+// the interrupted reflow; but as soon as we need a cached measurement in a
+// non-interrupted reflow, this bit should make us purge our flex items'
+// measurements and remeasure.)
+FRAME_STATE_BIT(FlexContainer, 25, NS_STATE_FLEX_MEASUREMENTS_INTERRUPTED)
+
 // == Frame state bits that apply to grid container frames ====================
 
 FRAME_STATE_GROUP(GridContainer, nsGridContainerFrame)
 
 // True iff the normal flow children are already in CSS 'order' in the
 // order they occur in the child frame list.
 FRAME_STATE_BIT(GridContainer, 20,
                 NS_STATE_GRID_NORMAL_FLOW_CHILDREN_IN_CSS_ORDER)
--- a/layout/generic/test/mochitest.ini
+++ b/layout/generic/test/mochitest.ini
@@ -100,16 +100,17 @@ support-files = bug633762_iframe.html
 support-files = file_bug1307853.html
 [test_bug1408607.html]
 [test_bug1499961.html]
 [test_bug1566783.html]
 support-files = file_bug1566783.html
 [test_contained_plugin_transplant.html]
 skip-if = os=='win'
 [test_dynamic_reflow_root_disallowal.html]
+[test_flex_interrupt.html]
 [test_image_selection.html]
 [test_image_selection_2.html]
 [test_image_selection_in_contenteditable.html]
 [test_invalidate_during_plugin_paint.html]
 skip-if = headless # Headless:Bug 1405871
 [test_intrinsic_size_on_loading.html]
 [test_movement_by_characters.html]
 [test_movement_by_words.html]
new file mode 100644
--- /dev/null
+++ b/layout/generic/test/test_flex_interrupt.html
@@ -0,0 +1,107 @@
+<!doctype html>
+<title>Test for bug 1579929</title>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<style>
+  .container {
+    display: flex;
+    flex-direction: column;
+    overflow-y: scroll;
+  }
+  #outer {
+    width: 500px;
+    height: 300px;
+  }
+</style>
+<div class="container" id="outer">
+  <div class="item" id="firstItem" style="width: 50px">
+    <!--popuplated by script-->
+  </div>
+  <div class="container">
+    <div class="container">
+      <div class="container">
+        <div class="container">
+          <div class="container">
+            <div class="item">Item</div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</div>
+<script>
+SimpleTest.waitForExplicitFinish();
+
+// Globals/constants:
+
+const gUtils = SpecialPowers.DOMWindowUtils;
+
+// This needs to match/exceed nsPresContext::sInterruptChecksToSkip in order
+// for us to actually be able to trigger the mHasPendingInterrupt=true codepath
+// in this test.  Each of these "dummy" blocks will trigger a call to
+// nsPresContext::CheckForInterrupt, and after 200, we'll actually trigger an
+// interrupt.
+const gInterruptCheckThreshold = 200;
+
+// Expected to match the inline style="width:..." for #firstItem (we slowly
+// increment it to trigger reflows):
+let gFirstItemWidthPX = 50;
+
+function main() {
+  const outer = document.getElementById("outer");
+  const firstItem = document.getElementById("firstItem");
+
+  // Insert a bunch of elements to be sure we actually get interrupted.
+  // (See description of gInterruptCheckThreshold above)
+  for (let i = 0; i < gInterruptCheckThreshold; i++) {
+    let dummyBlock = document.createElement("div");
+    dummyBlock.innerText = "dummy";
+    firstItem.appendChild(dummyBlock);
+  }
+
+  // Flush any pending relayout:
+  outer.offsetHeight;
+
+  // Take control of the refresh driver
+  gUtils.advanceTimeAndRefresh(0);
+
+  // Force reflow and store the "cost" (in num-frames-reflowed)
+  const costOfNormalReflow = forceReflow();
+
+  // Sanity-check: do that again and remeasure cost, to be sure it's stable:
+  const costOfNormalReflow2 = forceReflow();
+  is(costOfNormalReflow, costOfNormalReflow2,
+     "Our forceReflow function is expected to reliably trigger " +
+     "the same set of frames to be reflowed. If this fails, there's a " +
+     "bug in the test, or non-determinism in layout code.");
+
+  // Now, we force the next reflow to get interrupted, and then measure the
+  // cost under those conditions:
+  gUtils.forceReflowInterrupt();
+  const costOfInterruptedReflow = forceReflow();
+
+  // Hopefully the interrupted one was less expensive...
+  ok(costOfInterruptedReflow <= costOfNormalReflow,
+     "num frames reflowed in interrupted reflow (" +
+     costOfInterruptedReflow +
+     ") shouldn't exceed the num frames reflowed in normal reflow (" +
+     costOfNormalReflow + ")");
+
+  gUtils.restoreNormalRefresh();
+  SimpleTest.finish();
+}
+
+// This function dirties firstItem's width, forces a reflow, and
+// returns the number of frames that were reflowed as a result.
+function forceReflow() {
+  gFirstItemWidthPX++;
+  firstItem.style.width = gFirstItemWidthPX + "px";
+
+  const origFramesReflowed = gUtils.framesReflowed;
+  gUtils.advanceTimeAndRefresh(0);
+  return gUtils.framesReflowed - origFramesReflowed;
+}
+
+main();
+</script>
--- a/layout/style/GeckoBindings.cpp
+++ b/layout/style/GeckoBindings.cpp
@@ -149,20 +149,16 @@ const Element* Gecko_GetBeforeOrAfterPse
 
 const Element* Gecko_GetMarkerPseudo(const Element* aElement) {
   MOZ_ASSERT(aElement);
   MOZ_ASSERT(aElement->HasProperties());
 
   return nsLayoutUtils::GetMarkerPseudo(aElement);
 }
 
-bool Gecko_IsInAnonymousSubtree(const Element* aElement) {
-  return aElement->IsInAnonymousSubtree();
-}
-
 nsTArray<nsIContent*>* Gecko_GetAnonymousContentForElement(
     const Element* aElement) {
   nsIAnonymousContentCreator* ac = do_QueryFrame(aElement->GetPrimaryFrame());
   if (!ac) {
     return nullptr;
   }
 
   auto* array = new nsTArray<nsIContent*>();
--- a/layout/style/GeckoBindings.h
+++ b/layout/style/GeckoBindings.h
@@ -76,18 +76,16 @@ const nsINode* Gecko_GetLastChild(const 
 const nsINode* Gecko_GetPreviousSibling(const nsINode*);
 
 const nsINode* Gecko_GetFlattenedTreeParentNode(const nsINode*);
 const mozilla::dom::Element* Gecko_GetBeforeOrAfterPseudo(
     const mozilla::dom::Element*, bool is_before);
 const mozilla::dom::Element* Gecko_GetMarkerPseudo(
     const mozilla::dom::Element*);
 
-bool Gecko_IsInAnonymousSubtree(const mozilla::dom::Element*);
-
 nsTArray<nsIContent*>* Gecko_GetAnonymousContentForElement(
     const mozilla::dom::Element*);
 void Gecko_DestroyAnonymousContentList(nsTArray<nsIContent*>* anon_content);
 
 const nsTArray<RefPtr<nsINode>>* Gecko_GetAssignedNodes(
     const mozilla::dom::Element*);
 
 void Gecko_ComputedStyle_Init(mozilla::ComputedStyle* context,
--- a/moz.build
+++ b/moz.build
@@ -121,21 +121,23 @@ CONFIGURE_SUBST_FILES += [
 ]
 
 if CONFIG['ENABLE_CLANG_PLUGIN']:
     DIRS += ['build/clang-plugin']
 
 DIRS += [
     'config',
     'python',
-    'taskcluster',
     'testing/mozbase',
     'third_party/python',
 ]
 
+if not CONFIG['JS_STANDALONE']:
+    DIRS += ['taskcluster']
+
 if not CONFIG['JS_STANDALONE'] or not CONFIG['MOZ_BUILD_APP']:
     CONFIGURE_DEFINE_FILES += [
         'mozilla-config.h',
     ]
     EXPORTS += [
         '!buildid.h',
         '!mozilla-config.h',
         '!source-repo.h',
--- a/netwerk/protocol/http/nsHttpChannelAuthProvider.cpp
+++ b/netwerk/protocol/http/nsHttpChannelAuthProvider.cpp
@@ -672,16 +672,17 @@ nsresult nsHttpChannelAuthProvider::GetC
     nsCOMPtr<nsIChannel> chan = do_QueryInterface(mAuthChannel);
     GetOriginAttributesSuffix(chan, suffix);
 
     // if this is the first challenge, then try using the identity
     // specified in the URL.
     if (mIdent.IsEmpty()) {
       GetIdentityFromURI(authFlags, mIdent);
       identFromURI = !mIdent.IsEmpty();
+      Telemetry::Accumulate(Telemetry::HTTP_AUTH_USERINFO_URI, identFromURI);
     }
 
     if ((loadFlags & nsIRequest::LOAD_ANONYMOUS) && !identFromURI) {
       LOG(("Skipping authentication for anonymous non-proxy request\n"));
       return NS_ERROR_NOT_AVAILABLE;
     }
 
     // Let explicit URL credentials pass
@@ -1421,42 +1422,54 @@ nsresult nsHttpChannelAuthProvider::Cont
   // authentication it'll respond with failure and resend the challenge list
   mRemainingChallenges.Truncate();
 
   Unused << mAuthChannel->OnAuthAvailable();
 
   return NS_OK;
 }
 
+void nsHttpChannelAuthProvider::RecordConfirmAuthTelemetry(const char* aType) {
+  if (nsCRT::strcmp(aType, "SuperfluousAuth")) {
+    Telemetry::AccumulateCategorical(
+        Telemetry::LABELS_HTTP_AUTH_CONFIRM_PROMPT::Superfluous);
+  } else if (nsCRT::strcmp(aType, "AutomaticAuth")) {
+    Telemetry::AccumulateCategorical(
+        Telemetry::LABELS_HTTP_AUTH_CONFIRM_PROMPT::Automatic);
+  }
+}
+
 bool nsHttpChannelAuthProvider::ConfirmAuth(const char* bundleKey,
                                             bool doYesNoPrompt) {
   // skip prompting the user if
   //   1) prompts are disabled by pref
   //   2) we've already prompted the user
   //   3) we're not a toplevel channel
   //   4) the userpass length is less than the "phishy" threshold
 
-  if (!StaticPrefs::network_auth_confirmAuth_enabled()) {
-    return true;
-  }
-
   uint32_t loadFlags;
   nsresult rv = mAuthChannel->GetLoadFlags(&loadFlags);
   if (NS_FAILED(rv)) return true;
 
   if (mSuppressDefensiveAuth ||
       !(loadFlags & nsIChannel::LOAD_INITIAL_DOCUMENT_URI))
     return true;
 
   nsAutoCString userPass;
   rv = mURI->GetUserPass(userPass);
   if (NS_FAILED(rv) ||
       (userPass.Length() < gHttpHandler->PhishyUserPassLength()))
     return true;
 
+  if (!StaticPrefs::network_auth_confirmAuth_enabled()) {
+    // If it wasn't for the pref, we would have prompted, record telemetry
+    RecordConfirmAuthTelemetry(bundleKey);
+    return true;
+  }
+
   // we try to confirm by prompting the user.  if we cannot do so, then
   // assume the user said ok.  this is done to keep things working in
   // embedded builds, where the string bundle might not be present, etc.
 
   nsCOMPtr<nsIStringBundleService> bundleService =
       do_GetService(NS_STRINGBUNDLE_CONTRACTID);
   if (!bundleService) return true;
 
@@ -1533,16 +1546,18 @@ bool nsHttpChannelAuthProvider::ConfirmA
     if (NS_FAILED(rv)) return true;
 
     confirmed = choice == 0;
   } else {
     rv = prompt->Confirm(nullptr, msg.get(), &confirmed);
     if (NS_FAILED(rv)) return true;
   }
 
+  RecordConfirmAuthTelemetry(bundleKey);
+
   return confirmed;
 }
 
 void nsHttpChannelAuthProvider::SetAuthorizationHeader(
     nsHttpAuthCache* authCache, nsHttpAtom header, const char* scheme,
     const char* host, int32_t port, const char* path,
     nsHttpAuthIdentity& ident) {
   nsHttpAuthEntry* entry = nullptr;
--- a/netwerk/protocol/http/nsHttpChannelAuthProvider.h
+++ b/netwerk/protocol/http/nsHttpChannelAuthProvider.h
@@ -81,16 +81,17 @@ class nsHttpChannelAuthProvider final : 
                                                    nsIHttpAuthenticator* auth,
                                                    nsCString& creds);
   MOZ_MUST_USE nsresult PromptForIdentity(uint32_t level, bool proxyAuth,
                                           const char* realm,
                                           const char* authType,
                                           uint32_t authFlags,
                                           nsHttpAuthIdentity&);
 
+  void RecordConfirmAuthTelemetry(const char* aType);
   bool ConfirmAuth(const char* bundleKey, bool doYesNoPrompt);
   void SetAuthorizationHeader(nsHttpAuthCache*, nsHttpAtom header,
                               const char* scheme, const char* host,
                               int32_t port, const char* path,
                               nsHttpAuthIdentity& ident);
   MOZ_MUST_USE nsresult GetCurrentPath(nsACString&);
   /**
    * Return all information needed to build authorization information,
--- a/netwerk/protocol/http/nsHttpResponseHead.cpp
+++ b/netwerk/protocol/http/nsHttpResponseHead.cpp
@@ -219,19 +219,19 @@ void nsHttpResponseHead::SetContentLengt
 }
 
 void nsHttpResponseHead::Flatten(nsACString& buf, bool pruneTransients) {
   RecursiveMutexAutoLock monitor(mRecursiveMutex);
   if (mVersion == HttpVersion::v0_9) return;
 
   buf.AppendLiteral("HTTP/");
   if (mVersion == HttpVersion::v3_0) {
-    buf.AppendLiteral("3.0 ");
+    buf.AppendLiteral("3 ");
   } else if (mVersion == HttpVersion::v2_0) {
-    buf.AppendLiteral("2.0 ");
+    buf.AppendLiteral("2 ");
   } else if (mVersion == HttpVersion::v1_1) {
     buf.AppendLiteral("1.1 ");
   } else {
     buf.AppendLiteral("1.0 ");
   }
 
   buf.Append(nsPrintfCString("%u", unsigned(mStatus)) +
              NS_LITERAL_CSTRING(" ") + mStatusText +
--- a/python/mozbuild/mozbuild/code-analysis/mach_commands.py
+++ b/python/mozbuild/mozbuild/code-analysis/mach_commands.py
@@ -816,21 +816,18 @@ class StaticAnalysis(MachCommandBase):
                 return rc
         rc = self._get_infer(verbose=verbose)
         if rc != 0:
             self.log(logging.WARNING, 'static-analysis', {},
                      'This command is only available for linux64!')
             return rc
         # which checkers to use, and which folders to exclude
         all_checkers, third_party_path, generated_path = self._get_infer_config()
-        checkers, excludes = self._get_infer_args(
-            checks=checks or all_checkers,
-            third_party_path=third_party_path,
-            generated_path=generated_path
-        )
+        checkers, excludes = self._get_infer_args(checks or all_checkers, third_party_path,
+                                                  generated_path)
         rc = rc or self._gradle(['clean'])  # clean so that we can recompile
         # infer capture command
         capture_cmd = [self._infer_path, 'capture'] + excludes + ['--']
         rc = rc or self._gradle([task], infer_args=capture_cmd, verbose=verbose)
         tmp_file, args = self._get_infer_source_args(java_sources)
         # infer analyze command
         analysis_cmd = [self._infer_path, 'analyze', '--keep-going'] +  \
             checkers + args
--- a/remote/domains/ParentProcessDomains.jsm
+++ b/remote/domains/ParentProcessDomains.jsm
@@ -9,14 +9,15 @@ var EXPORTED_SYMBOLS = ["ParentProcessDo
 const { XPCOMUtils } = ChromeUtils.import(
   "resource://gre/modules/XPCOMUtils.jsm"
 );
 
 const ParentProcessDomains = {};
 
 XPCOMUtils.defineLazyModuleGetters(ParentProcessDomains, {
   Browser: "chrome://remote/content/domains/parent/Browser.jsm",
+  Emulation: "chrome://remote/content/domains/parent/Emulation.jsm",
   Input: "chrome://remote/content/domains/parent/Input.jsm",
   Network: "chrome://remote/content/domains/parent/Network.jsm",
   Page: "chrome://remote/content/domains/parent/Page.jsm",
   Security: "chrome://remote/content/domains/parent/Security.jsm",
   Target: "chrome://remote/content/domains/parent/Target.jsm",
 });
new file mode 100644
--- /dev/null
+++ b/remote/domains/parent/Emulation.jsm
@@ -0,0 +1,71 @@
+/* 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";
+
+var EXPORTED_SYMBOLS = ["Emulation"];
+
+const { Domain } = ChromeUtils.import(
+  "chrome://remote/content/domains/Domain.jsm"
+);
+const { UnsupportedError } = ChromeUtils.import(
+  "chrome://remote/content/Error.jsm"
+);
+const { NetUtil } = ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
+const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+const PREF_USER_AGENT_OVERRIDE = "general.useragent.override";
+
+class Emulation extends Domain {
+  destructor() {
+    this.setUserAgentOverride({ userAgent: "" });
+
+    super.destructor();
+  }
+
+  /**
+   * Allows overriding user agent with the given string.
+   *
+   * @param {Object} options
+   * @param {string} options.userAgent
+   *     User agent to use.
+   * @param {string=} options.acceptLanguage [not yet supported]
+   *     Browser langugage to emulate.
+   * @param {number=} options.platform [not yet supported]
+   *     The platform navigator.platform should return.
+   */
+  setUserAgentOverride(options = {}) {
+    const { userAgent } = options;
+
+    if (options.acceptLanguage) {
+      throw new UnsupportedError("'acceptLanguage' not supported");
+    }
+    if (options.platform) {
+      throw new UnsupportedError("'platform' not supported");
+    }
+
+    // TODO: Set the user agent for the current session only (Bug 1596136)
+    if (userAgent.length == 0) {
+      Services.prefs.clearUserPref(PREF_USER_AGENT_OVERRIDE);
+    } else if (this._isValidHTTPRequestHeaderValue(userAgent)) {
+      Services.prefs.setStringPref(PREF_USER_AGENT_OVERRIDE, userAgent);
+    } else {
+      throw new Error("Invalid characters found in userAgent");
+    }
+  }
+
+  _isValidHTTPRequestHeaderValue(value) {
+    try {
+      const channel = NetUtil.newChannel({
+        uri: "http://localhost",
+        loadUsingSystemPrincipal: true,
+      });
+      channel.QueryInterface(Ci.nsIHttpChannel);
+      channel.setRequestHeader("X-check", value, false);
+      return true;
+    } catch (e) {
+      return false;
+    }
+  }
+}
--- a/remote/domains/parent/Network.jsm
+++ b/remote/domains/parent/Network.jsm
@@ -70,16 +70,26 @@ class Network extends Domain {
     }
     const { browser } = this.session.target;
     this._networkObserver.stopTrackingBrowserNetwork(browser);
     this._networkObserver.off("request", this._onRequest);
     this._networkObserver.dispose();
     this.enabled = false;
   }
 
+  /**
+   * Allows overriding user agent with the given string.
+   *
+   * Redirected to Emulation.setUserAgentOverride.
+   */
+  setUserAgentOverride(options) {
+    const { id } = this.session;
+    this.session.execute(id, "Emulation", "setUserAgentOverride", options);
+  }
+
   _onRequest(eventName, httpChannel, data) {
     const topFrame = getLoadContext(httpChannel).topFrameElement;
     const request = {
       url: httpChannel.URI.spec,
       urlFragment: undefined,
       method: httpChannel.requestMethod,
       headers: [],
       postData: undefined,
--- a/remote/jar.mn
+++ b/remote/jar.mn
@@ -43,16 +43,17 @@ remote.jar:
   content/domains/content/Input.jsm (domains/content/Input.jsm)
   content/domains/content/Log.jsm (domains/content/Log.jsm)
   content/domains/content/Page.jsm (domains/content/Page.jsm)
   content/domains/content/Performance.jsm (domains/content/Performance.jsm)
   content/domains/content/Runtime.jsm (domains/content/Runtime.jsm)
   content/domains/content/runtime/ExecutionContext.jsm (domains/content/runtime/ExecutionContext.jsm)
   content/domains/content/Security.jsm (domains/content/Security.jsm)
   content/domains/parent/Browser.jsm (domains/parent/Browser.jsm)
+  content/domains/parent/Emulation.jsm (domains/parent/Emulation.jsm)
   content/domains/parent/Input.jsm (domains/parent/Input.jsm)
   content/domains/parent/Network.jsm (domains/parent/Network.jsm)
   content/domains/parent/network/ChannelEventSink.jsm (domains/parent/network/ChannelEventSink.jsm)
   content/domains/parent/network/NetworkObserver.jsm (domains/parent/network/NetworkObserver.jsm)
   content/domains/parent/Page.jsm (domains/parent/Page.jsm)
   content/domains/parent/page/DialogHandler.jsm (domains/parent/page/DialogHandler.jsm)
   content/domains/parent/Security.jsm (domains/parent/Security.jsm)
   content/domains/parent/Target.jsm (domains/parent/Target.jsm)
new file mode 100644
--- /dev/null
+++ b/remote/test/browser/emulation/browser.ini
@@ -0,0 +1,8 @@
+[DEFAULT]
+tags = remote
+subsuite = remote
+prefs = remote.enabled=true
+support-files =
+  head.js
+
+[browser_setUserAgentOverride.js]
new file mode 100644
--- /dev/null
+++ b/remote/test/browser/emulation/browser_setUserAgentOverride.js
@@ -0,0 +1,115 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const DOC = toDataURL(`<script>document.write(navigator.userAgent);</script>`);
+
+add_task(async function setAndResetUserAgent({ Emulation }) {
+  const userAgent = "foo bar";
+
+  await loadURL(DOC);
+  isnot(
+    await getNavigatorProperty("userAgent"),
+    userAgent,
+    "Custom user agent hasn't been set"
+  );
+
+  try {
+    await Emulation.setUserAgentOverride({ userAgent });
+    await loadURL(DOC);
+    is(
+      await getNavigatorProperty("userAgent"),
+      userAgent,
+      "Custom user agent has been set"
+    );
+
+    await Emulation.setUserAgentOverride({ userAgent: "" });
+    await loadURL(DOC);
+    isnot(
+      await getNavigatorProperty("userAgent"),
+      userAgent,
+      "Custom user agent hasn't been set anymore"
+    );
+  } finally {
+    Services.prefs.clearUserPref("general.useragent.override");
+  }
+});
+
+add_task(async function invalidUserAgent({ Emulation }) {
+  const userAgent = "foobar\n";
+
+  await loadURL(DOC);
+  isnot(
+    await getNavigatorProperty("userAgent"),
+    userAgent,
+    "Custom user agent hasn't been set"
+  );
+
+  let errorThrown = false;
+  try {
+    await Emulation.setUserAgentOverride({ userAgent });
+  } catch (e) {
+    errorThrown = true;
+  }
+  ok(errorThrown, "Invalid user agent format raised error");
+});
+
+// Network.setUserAgentOverride is a redirect to Emulation.setUserAgentOverride.
+// Run duplicated tests for Network to ensure the redirect works.
+
+add_task(async function networkSetAndResetUserAgent({ Network }) {
+  const userAgent = "foo bar";
+
+  await loadURL(DOC);
+  isnot(
+    await getNavigatorProperty("userAgent"),
+    userAgent,
+    "Custom user agent hasn't been set"
+  );
+
+  try {
+    await Network.setUserAgentOverride({ userAgent });
+    await loadURL(DOC);
+    is(
+      await getNavigatorProperty("userAgent"),
+      userAgent,
+      "Custom user agent has been set"
+    );
+
+    await Network.setUserAgentOverride({ userAgent: "" });
+    await loadURL(DOC);
+    isnot(
+      await getNavigatorProperty("userAgent"),
+      userAgent,
+      "Custom user agent hasn't been set anymore"
+    );
+  } finally {
+    Services.prefs.clearUserPref("general.useragent.override");
+  }
+});
+
+add_task(async function networkInvalidUserAgent({ Network }) {
+  const userAgent = "foobar\n";
+
+  await loadURL(DOC);
+  isnot(
+    await getNavigatorProperty("userAgent"),
+    userAgent,
+    "Custom user agent hasn't been set"
+  );
+
+  let errorThrown = false;
+  try {
+    await Network.setUserAgentOverride({ userAgent });
+  } catch (e) {
+    errorThrown = true;
+  }
+  ok(errorThrown, "Invalid user agent format raised error");
+});
+
+async function getNavigatorProperty(prop) {
+  return ContentTask.spawn(gBrowser.selectedBrowser, prop, _prop => {
+    return content.navigator[_prop];
+  });
+}
new file mode 100644
--- /dev/null
+++ b/remote/test/browser/emulation/head.js
@@ -0,0 +1,11 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/* import-globals-from ../head.js */
+
+Services.scriptloader.loadSubScript(
+  "chrome://mochitests/content/browser/remote/test/browser/head.js",
+  this
+);
--- a/remote/test/moz.build
+++ b/remote/test/moz.build
@@ -1,15 +1,16 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 XPCSHELL_TESTS_MANIFESTS += ["unit/xpcshell.ini"]
 BROWSER_CHROME_MANIFESTS += [
     "browser/browser.ini",
+    "browser/emulation/browser.ini",
     "browser/input/browser.ini",
     "browser/network/browser.ini",
     "browser/page/browser.ini",
     "browser/runtime/browser.ini",
     "browser/security/browser.ini",
     "browser/target/browser.ini",
 ]
 
--- a/servo/components/style/gecko/wrapper.rs
+++ b/servo/components/style/gecko/wrapper.rs
@@ -756,35 +756,23 @@ impl<'le> GeckoElement<'le> {
         } else {
             bindings::Gecko_NoteDirtyElement(self.0);
         }
 
         data.hint.insert(restyle_hint);
         data.damage |= damage;
     }
 
-    /// This logic is duplicated in Gecko's nsIContent::IsRootOfAnonymousSubtree.
-    #[inline]
-    fn is_root_of_anonymous_subtree(&self) -> bool {
-        use crate::gecko_bindings::structs::NODE_IS_ANONYMOUS_ROOT;
-        self.flags() & (NODE_IS_ANONYMOUS_ROOT as u32) != 0
-    }
-
     /// This logic is duplicated in Gecko's nsIContent::IsRootOfNativeAnonymousSubtree.
     #[inline]
     fn is_root_of_native_anonymous_subtree(&self) -> bool {
         use crate::gecko_bindings::structs::NODE_IS_NATIVE_ANONYMOUS_ROOT;
         return self.flags() & (NODE_IS_NATIVE_ANONYMOUS_ROOT as u32) != 0;
     }
 
-    #[inline]
-    fn is_in_anonymous_subtree(&self) -> bool {
-        unsafe { bindings::Gecko_IsInAnonymousSubtree(self.0) }
-    }
-
     /// Returns true if this node is the shadow root of an use-element shadow tree.
     #[inline]
     fn is_root_of_use_element_shadow_tree(&self) -> bool {
         if !self.as_node().is_in_shadow_tree() {
             return false;
         }
         if !self.parent_node_is_shadow_root() {
             return false;
@@ -1023,17 +1011,17 @@ impl<'le> TElement for GeckoElement<'le>
             .and_then(|n| n.as_element())
     }
 
     fn traversal_children(&self) -> LayoutIterator<GeckoChildrenIterator<'le>> {
         // This condition is similar to the check that
         // StyleChildrenIterator::IsNeeded does, except that it might return
         // true if we used to (but no longer) have anonymous content from
         // ::before/::after, or nsIAnonymousContentCreators.
-        if self.is_in_anonymous_subtree() ||
+        if self.is_in_native_anonymous_subtree() ||
             self.is_html_slot_element() ||
             self.shadow_root().is_some() ||
             self.may_have_anonymous_children()
         {
             unsafe {
                 let mut iter: structs::StyleChildrenIterator = ::std::mem::zeroed();
                 bindings::Gecko_ConstructStyleChildrenIterator(self.0, &mut iter);
                 return LayoutIterator(GeckoChildrenIterator::GeckoIterator(iter));
@@ -2238,17 +2226,17 @@ impl<'le> ::selectors::Element for Gecko
 
     #[inline]
     fn is_html_slot_element(&self) -> bool {
         self.is_html_element() && self.local_name().as_ptr() == local_name!("slot").as_ptr()
     }
 
     #[inline]
     fn ignores_nth_child_selectors(&self) -> bool {
-        self.is_root_of_anonymous_subtree()
+        self.is_root_of_native_anonymous_subtree()
     }
 }
 
 /// A few helpers to help with attribute selectors and snapshotting.
 pub trait NamespaceConstraintHelpers {
     /// Returns the namespace of the selector, or null otherwise.
     fn atom_or_null(&self) -> *mut nsAtom;
 }
--- a/testing/web-platform/meta/battery-status/battery-interface-idlharness.https.window.js.ini
+++ b/testing/web-platform/meta/battery-status/battery-interface-idlharness.https.window.js.ini
@@ -1,40 +1,82 @@
 [battery-interface-idlharness.https.window.html]
-  [idl_test setup]
+  [Navigator interface: operation getBattery()]
+    expected: FAIL
+
+  [BatteryManager interface: manager must inherit property "chargingTime" with the proper type]
+    expected: FAIL
+
+  [BatteryManager interface: manager must inherit property "level" with the proper type]
     expected: FAIL
 
   [BatteryManager interface: manager must inherit property "onlevelchange" with the proper type]
     expected: FAIL
 
-  [BatteryManager interface: manager must inherit property "chargingTime" with the proper type]
+  [BatteryManager interface: attribute chargingTime]
+    expected: FAIL
+
+  [BatteryManager interface: existence and properties of interface prototype object's "constructor" property]
+    expected: FAIL
+
+  [BatteryManager interface: attribute ondischargingtimechange]
+    expected: FAIL
+
+  [BatteryManager interface: attribute dischargingTime]
+    expected: FAIL
+
+  [BatteryManager interface object name]
     expected: FAIL
 
   [BatteryManager must be primary interface of manager]
     expected: FAIL
 
   [Stringification of manager]
     expected: FAIL
 
   [BatteryManager interface: manager must inherit property "dischargingTime" with the proper type]
     expected: FAIL
 
+  [BatteryManager interface: existence and properties of interface object]
+    expected: FAIL
+
+  [BatteryManager interface: attribute onlevelchange]
+    expected: FAIL
+
   [Navigator interface: navigator must inherit property "getBattery()" with the proper type]
     expected: FAIL
 
+  [BatteryManager interface: attribute level]
+    expected: FAIL
+
+  [BatteryManager interface: existence and properties of interface prototype object]
+    expected: FAIL
+
+  [BatteryManager interface: existence and properties of interface prototype object's @@unscopables property]
+    expected: FAIL
+
   [BatteryManager interface: manager must inherit property "ondischargingtimechange" with the proper type]
     expected: FAIL
 
   [BatteryManager interface: manager must inherit property "onchargingchange" with the proper type]
     expected: FAIL
 
-  [BatteryManager interface: manager must inherit property "level" with the proper type]
+  [BatteryManager interface object length]
     expected: FAIL
 
-  [Navigator interface: operation getBattery()]
+  [BatteryManager interface: attribute onchargingtimechange]
+    expected: FAIL
+
+  [idl_test setup]
     expected: FAIL
 
   [BatteryManager interface: manager must inherit property "onchargingtimechange" with the proper type]
     expected: FAIL
 
   [BatteryManager interface: manager must inherit property "charging" with the proper type]
     expected: FAIL
 
+  [BatteryManager interface: attribute charging]
+    expected: FAIL
+
+  [BatteryManager interface: attribute onchargingchange]
+    expected: FAIL
+
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -2496,16 +2496,37 @@
     "alert_emails": ["rbarnes@mozilla.com"],
     "bug_numbers": [1266571],
     "expires_in_version": "52",
     "kind": "enumerated",
     "n_values": 8,
     "releaseChannelCollection": "opt-out",
     "description": "Recorded once for each HTTP 401 response. The value records the type of authentication and the TLS-enabled status. (0=basic/clear, 1=basic/tls, 2=digest/clear, 3=digest/tls, 4=ntlm/clear, 5=ntlm/tls, 6=negotiate/clear, 7=negotiate/tls)"
   },
+  "HTTP_AUTH_CONFIRM_PROMPT": {
+    "record_in_processes": ["main"],
+    "products": ["firefox", "geckoview"],
+    "alert_emails": ["seceng-telemetry@mozilla.com", "pbz@mozilla.com"],
+    "bug_numbers": [1594613],
+    "expires_in_version": "75",
+    "kind": "categorical",
+    "releaseChannelCollection": "opt-out",
+    "description": "Number of Automatic/Superfluous auth warning prompts shown. Prompts are counted even if disabled by pref.",
+    "labels": ["Automatic", "Superfluous"]
+  },
+  "HTTP_AUTH_USERINFO_URI": {
+    "record_in_processes": ["main"],
+    "products": ["firefox", "geckoview"],
+    "alert_emails": ["seceng-telemetry@mozilla.com", "pbz@mozilla.com"],
+    "bug_numbers": [1594613],
+    "expires_in_version": "75",
+    "kind": "boolean",
+    "releaseChannelCollection": "opt-out",
+    "description": "Counts http auth connections with (true) / without (false) userinfo in URI"
+  },
   "HTTP_CHILD_OMT_STATS": {
     "record_in_processes": ["content"],
     "products": ["firefox", "fennec", "geckoview"],
     "alert_emails": ["necko@mozilla.com"],
     "bug_numbers": [1357682],
     "expires_in_version": "61",
     "kind": "categorical",
     "keyed": true,
--- a/toolkit/components/url-classifier/HashStore.cpp
+++ b/toolkit/components/url-classifier/HashStore.cpp
@@ -1104,18 +1104,16 @@ static void EnsureSorted(FallibleTArray<
 
   while (iter != end) {
     previous = iter;
     ++iter;
     if (iter != end) {
       MOZ_ASSERT(iter->Compare(*previous) >= 0);
     }
   }
-
-  return;
 }
 #endif
 
 nsresult HashStore::ProcessSubs() {
 #ifdef DEBUG
   EnsureSorted(&mAddPrefixes);
   EnsureSorted(&mSubPrefixes);
   EnsureSorted(&mAddCompletes);
--- a/uriloader/exthandler/nsExternalHelperAppService.cpp
+++ b/uriloader/exthandler/nsExternalHelperAppService.cpp
@@ -804,18 +804,29 @@ NS_IMETHODIMP nsExternalHelperAppService
   return NS_OK;
 }
 
 NS_IMETHODIMP nsExternalHelperAppService::DoContent(
     const nsACString& aMimeContentType, nsIRequest* aRequest,
     nsIInterfaceRequestor* aContentContext, bool aForceSave,
     nsIInterfaceRequestor* aWindowContext,
     nsIStreamListener** aStreamListener) {
-  nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(aContentContext);
-  RefPtr<BrowsingContext> bc = window ? window->GetBrowsingContext() : nullptr;
+  // Scripted interface requestors cannot return an instance of the
+  // (non-scriptable) nsPIDOMWindowOuter or nsPIDOMWindowInner interfaces, so
+  // get to the window via `nsIDOMWindow`.  Unfortunately, at that point we
+  // don't know whether the thing we got is an inner or outer window, so have to
+  // work with either one.
+  RefPtr<BrowsingContext> bc;
+  nsCOMPtr<nsIDOMWindow> domWindow = do_GetInterface(aContentContext);
+  if (nsCOMPtr<nsPIDOMWindowOuter> outerWindow = do_QueryInterface(domWindow)) {
+    bc = outerWindow->GetBrowsingContext();
+  } else if (nsCOMPtr<nsPIDOMWindowInner> innerWindow =
+                 do_QueryInterface(domWindow)) {
+    bc = innerWindow->GetBrowsingContext();
+  }
 
   if (XRE_IsContentProcess()) {
     return DoContentContentProcessHelper(aMimeContentType, aRequest, bc,
                                          aForceSave, aWindowContext,
                                          aStreamListener);
   }
 
   RefPtr<nsExternalAppHandler> handler;