Merge autoland to mozilla-central. a=merge
authorNoemi Erli <nerli@mozilla.com>
Wed, 17 Apr 2019 01:01:20 +0300
changeset 528538 12a60898fdc12e4ab8735f228a77d6c1e6a600a9
parent 528477 258af4e91151d1d8e0183d80b4483b199207225d (current diff)
parent 528537 140dfb5fd701bdf3d0feb04363bd32d485552421 (diff)
child 528560 79e6ed0b08d67523705ca61981437874340a38f6
push id11265
push userffxbld-merge
push dateMon, 13 May 2019 10:53:39 +0000
treeherdermozilla-beta@77e0fe8dbdd3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone68.0a1
first release with
nightly linux32
12a60898fdc1 / 68.0a1 / 20190416220148 / files
nightly linux64
12a60898fdc1 / 68.0a1 / 20190416220148 / files
nightly mac
12a60898fdc1 / 68.0a1 / 20190416220148 / files
nightly win32
12a60898fdc1 / 68.0a1 / 20190416220148 / files
nightly win64
12a60898fdc1 / 68.0a1 / 20190416220148 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge autoland to mozilla-central. a=merge
browser/extensions/fxmonitor/privileged/subscripts/EveryWindow.jsm
taskcluster/docker/desktop1604-test/tester.env
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1228,16 +1228,17 @@ dependencies = [
  "cubeb-sys 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "encoding_c 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "encoding_glue 0.1.0",
  "env_logger 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "geckoservo 0.0.1",
  "gkrust_utils 0.1.0",
  "jsrust_shared 0.1.0",
  "kvstore 0.1.0",
+ "lmdb-rkv-sys 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "mozurl 0.0.1",
  "mp4parse_capi 0.11.2",
  "netwerk_helper 0.0.1",
  "nserror 0.1.0",
  "nsstring 0.1.0",
  "prefs_parser 0.0.1",
  "profiler_helper 0.1.0",
@@ -1579,22 +1580,22 @@ source = "registry+https://github.com/ru
 
 [[package]]
 name = "lmdb-rkv"
 version = "0.11.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)",
- "lmdb-rkv-sys 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lmdb-rkv-sys 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "lmdb-rkv-sys"
-version = "0.8.2"
+version = "0.8.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "cc 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)",
  "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -3587,17 +3588,17 @@ dependencies = [
 "checksum lazycell 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ce12306c4739d86ee97c23139f3a34ddf0387bbf181bc7929d287025a8c3ef6b"
 "checksum lazycell 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a6f08839bc70ef4a3fe1d566d5350f519c5912ea86be0df1740a7d247c7fc0ef"
 "checksum libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)" = "bedcc7a809076656486ffe045abeeac163da1b558e963a31e29fbfbeba916917"
 "checksum libloading 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9c3ad660d7cb8c5822cd83d10897b0f1f1526792737a179e73896152f85b88c2"
 "checksum libudev 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ea626d3bdf40a1c5aee3bcd4f40826970cae8d80a8fec934c82a63840094dcfe"
 "checksum libz-sys 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)" = "3fdd64ef8ee652185674455c1d450b83cbc8ad895625d543b5324d923f82e4d8"
 "checksum linked-hash-map 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "70fb39025bc7cdd76305867c4eccf2f2dcf6e9a57f5b21a93e1c2d86cd03ec9e"
 "checksum lmdb-rkv 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1452294309db7977dc75e1e8135a8c654d9e52e04ff0c0bd06c880897a91defd"
-"checksum lmdb-rkv-sys 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "96846a2e6785ec0fce6577479d18273c8e5b287e6df8a1b398b7f0f7a41cdcbb"
+"checksum lmdb-rkv-sys 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1470e0168f1832e35afd6d0931ae60db625685332837b97aa156773ec9c5e393"
 "checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c"
 "checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b"
 "checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6"
 "checksum malloc_size_of_derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "35adee9ed962cf7d07d62cb58bc45029f3227f5b5b86246caa8632f06c187bc3"
 "checksum matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "100aabe6b8ff4e4a7e32c1c13523379802df0772b82466207ac25b013f193376"
 "checksum memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "148fab2e51b4f1cfc66da2a7c32981d1d3c083a803978268bb11fe4b86925e7a"
 "checksum memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2efc7bc57c883d4a4d6e3246905283d8dae951bb3bd32f49d6ef297f546e1c39"
 "checksum memmap 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "46f3c7359028b31999287dae4e5047ddfe90a23b7dca2282ce759b491080c99b"
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -406,17 +406,21 @@ pref("permissions.manager.defaultsUrl", 
 // Set default fallback values for site permissions we want
 // the user to be able to globally change.
 pref("permissions.default.camera", 0);
 pref("permissions.default.microphone", 0);
 pref("permissions.default.geo", 0);
 pref("permissions.default.desktop-notification", 0);
 pref("permissions.default.shortcuts", 0);
 
+#ifdef NIGHTLY_BUILD
+pref("permissions.desktop-notification.postPrompt.enabled", true);
+#else
 pref("permissions.desktop-notification.postPrompt.enabled", false);
+#endif
 
 pref("permissions.postPrompt.animate", true);
 
 // handle links targeting new windows
 // 1=current window/tab, 2=new window, 3=new tab in most recent window
 pref("browser.link.open_newwindow", 3);
 
 // handle external links (i.e. links opened from a different application)
@@ -1537,20 +1541,24 @@ pref("toolkit.telemetry.ecosystemtelemet
 pref("browser.ping-centre.telemetry", true);
 pref("browser.ping-centre.log", false);
 pref("browser.ping-centre.staging.endpoint", "https://onyx_tiles.stage.mozaws.net/v3/links/ping-centre");
 pref("browser.ping-centre.production.endpoint", "https://tiles.services.mozilla.com/v3/links/ping-centre");
 
 // Enable GMP support in the addon manager.
 pref("media.gmp-provider.enabled", true);
 
+#ifdef EARLY_BETA_OR_EARLIER
 // Enable blocking access to storage from tracking resources only in nightly
 // and early beta. By default the value is 0: BEHAVIOR_ACCEPT
-#ifdef EARLY_BETA_OR_EARLIER
 pref("network.cookie.cookieBehavior", 4 /* BEHAVIOR_REJECT_TRACKER */);
+// Enable fingerprinting blocking by default only in nightly and early beta.
+pref("privacy.trackingprotection.fingerprinting.enabled", true);
+// Enable cryptomining blocking by default only in nightly and early beta.
+pref("privacy.trackingprotection.cryptomining.enabled", true);
 #endif
 
 pref("browser.contentblocking.allowlist.storage.enabled", true);
 
 pref("dom.storage_access.enabled", true);
 
 pref("dom.storage_access.auto_grants", true);
 pref("dom.storage_access.max_concurrent_auto_grants", 5);
@@ -1580,22 +1588,23 @@ pref("browser.contentblocking.fingerprin
 //     "-cm": cryptomining blocking disabled
 //   Cookie behavior:
 //     "cookieBehavior0": cookie behaviour BEHAVIOR_ACCEPT
 //     "cookieBehavior1": cookie behaviour BEHAVIOR_REJECT_FOREIGN
 //     "cookieBehavior2": cookie behaviour BEHAVIOR_REJECT
 //     "cookieBehavior3": cookie behaviour BEHAVIOR_LIMIT_FOREIGN
 //     "cookieBehavior4": cookie behaviour BEHAVIOR_REJECT_TRACKER
 // One value from each section must be included in each browser.contentblocking.features.* pref.
-pref("browser.contentblocking.features.strict", "tp,tpPrivate,cookieBehavior4,-cm,-fp");
-
+pref("browser.contentblocking.features.strict", "tp,tpPrivate,cookieBehavior4,cm,fp");
 // Enable blocking access to storage from tracking resources only in nightly
 // and early beta. By default the value is "cookieBehavior0": BEHAVIOR_ACCEPT
+// Enable cryptomining blocking in standard in nightly and early beta.
+// Enable fingerprinting blocking in standard in nightly and early beta.
 #ifdef EARLY_BETA_OR_EARLIER
-pref("browser.contentblocking.features.standard", "-tp,tpPrivate,cookieBehavior4,-cm,-fp");
+pref("browser.contentblocking.features.standard", "-tp,tpPrivate,cookieBehavior4,cm,fp");
 #else
 pref("browser.contentblocking.features.standard", "-tp,tpPrivate,cookieBehavior0,-cm,-fp");
 #endif
 
 // Enable the Report Breakage UI on Nightly and Beta but not on Release yet.
 #ifdef EARLY_BETA_OR_EARLIER
 pref("browser.contentblocking.reportBreakage.enabled", true);
 #else
--- a/browser/base/content/browser-contentblocking.js
+++ b/browser/base/content/browser-contentblocking.js
@@ -1076,33 +1076,33 @@ var ContentBlocking = {
   // This triggers from top level location changes.
   onLocationChange() {
     // Reset blocking and exception status so that we can send telemetry
     this.hadShieldState = false;
     let baseURI = this._baseURIForChannelClassifier;
 
     // Don't deal with about:, file: etc.
     if (!baseURI) {
-      this.iconBox.removeAttribute("animate");
-      this.iconBox.removeAttribute("active");
-      this.iconBox.removeAttribute("hasException");
       return;
     }
     // Add to telemetry per page load as a baseline measurement.
     this.fingerprintersHistogramAdd("pageLoad");
     this.cryptominersHistogramAdd("pageLoad");
     this.shieldHistogramAdd(0);
   },
 
   onContentBlockingEvent(event, webProgress, isSimulated) {
     let previousState = gBrowser.securityUI.contentBlockingEvent;
     let baseURI = this._baseURIForChannelClassifier;
 
     // Don't deal with about:, file: etc.
     if (!baseURI) {
+      this.iconBox.removeAttribute("animate");
+      this.iconBox.removeAttribute("active");
+      this.iconBox.removeAttribute("hasException");
       return;
     }
 
     let anyDetected = false;
     let anyBlocking = false;
 
     for (let blocker of this.blockers) {
       // Store data on whether the blocker is activated in the current document for
--- a/browser/base/content/test/performance/browser_startup.js
+++ b/browser/base/content/test/performance/browser_startup.js
@@ -111,16 +111,22 @@ const startupPhases = {
     modules: new Set([
       "resource://gre/modules/AsyncPrefs.jsm",
       "resource://gre/modules/LoginManagerContextMenu.jsm",
       "resource://pdf.js/PdfStreamConverter.jsm",
     ]),
   }},
 };
 
+if (Services.prefs.getBoolPref("browser.startup.blankWindow") &&
+    Services.prefs.getCharPref("extensions.activeThemeID", "default-theme@mozilla.org") ==
+      "default-theme@mozilla.org") {
+  startupPhases["before profile selection"].whitelist.modules.add("resource://gre/modules/XULStore.jsm");
+}
+
 if (!gBrowser.selectedBrowser.isRemoteBrowser) {
   // With e10s disabled, Places and BrowserWindowTracker.jsm (from a
   // SessionSaver.jsm timer) intermittently get loaded earlier. Likely
   // due to messages from the 'content' process arriving synchronously
   // instead of crossing a process boundary.
   info("merging the 'before handling user events' blacklist into the " +
        "'before first paint' one when e10s is disabled.");
   let from = startupPhases["before handling user events"].blacklist;
--- a/browser/base/content/test/static/browser_all_files_referenced.js
+++ b/browser/base/content/test/static/browser_all_files_referenced.js
@@ -121,16 +121,20 @@ var whitelist = [
   // browser/extensions/pdfjs/content/web/viewer.js#7450
   {file: "resource://pdf.js/web/debugger.js"},
 
   // resource://app/modules/translation/TranslationContentHandler.jsm
   {file: "resource://app/modules/translation/BingTranslator.jsm"},
   {file: "resource://app/modules/translation/GoogleTranslator.jsm"},
   {file: "resource://app/modules/translation/YandexTranslator.jsm"},
 
+  // Used in Firefox Monitor, which is an extension - we don't check
+  // files inside the XPI.
+  {file: "resource://app/modules/EveryWindow.jsm"},
+
   // Starting from here, files in the whitelist are bugs that need fixing.
   // Bug 1339424 (wontfix?)
   {file: "chrome://browser/locale/taskbar.properties",
    platforms: ["linux", "macosx"]},
   // Bug 1356031 (only used by devtools)
   {file: "chrome://global/skin/icons/error-16.png"},
   // Bug 1344267
   {file: "chrome://marionette/content/test_anonymous_content.xul"},
--- a/browser/base/content/test/trackingUI/browser_trackingUI_state_reset.js
+++ b/browser/base/content/test/trackingUI/browser_trackingUI_state_reset.js
@@ -1,14 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 const TP_PREF = "privacy.trackingprotection.enabled";
 const TRACKING_PAGE = "http://tracking.example.org/browser/browser/base/content/test/trackingUI/trackingPage.html";
 const BENIGN_PAGE = "http://tracking.example.org/browser/browser/base/content/test/trackingUI/benignPage.html";
+const ABOUT_PAGE = "about:preferences";
 
 /* This asserts that the content blocking event state is correctly reset
  * when navigating to a new location, and that the user is correctly
  * reset when switching between tabs. */
 
 add_task(async function testResetOnLocationChange() {
   Services.prefs.setBoolPref(TP_PREF, true);
 
@@ -40,8 +41,37 @@ add_task(async function testResetOnLocat
   is(browser.securityUI.contentBlockingEvent, 0, "Benign page has no content blocking event");
   ok(!ContentBlocking.iconBox.hasAttribute("active"), "shield is not active");
 
   gBrowser.removeTab(trackingTab);
   gBrowser.removeTab(tab);
 
   Services.prefs.clearUserPref(TP_PREF);
 });
+
+/* Test that the content blocking icon is correctly reset
+ * when changing tabs or navigating to an about: page */
+add_task(async function testResetOnTabChange() {
+  Services.prefs.setBoolPref(TP_PREF, true);
+
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, ABOUT_PAGE);
+  ok(!ContentBlocking.iconBox.hasAttribute("active"), "shield is not active");
+
+  await Promise.all([promiseTabLoadEvent(tab, TRACKING_PAGE),
+                     waitForContentBlockingEvent(3)]);
+  ok(ContentBlocking.iconBox.hasAttribute("active"), "shield is active");
+
+  await promiseTabLoadEvent(tab, ABOUT_PAGE);
+  ok(!ContentBlocking.iconBox.hasAttribute("active"), "shield is not active");
+
+  let contentBlockingEvent = waitForContentBlockingEvent(3);
+  let trackingTab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TRACKING_PAGE);
+  await contentBlockingEvent;
+  ok(ContentBlocking.iconBox.hasAttribute("active"), "shield is active");
+
+  gBrowser.selectedTab = tab;
+  ok(!ContentBlocking.iconBox.hasAttribute("active"), "shield is not active");
+
+  gBrowser.removeTab(trackingTab);
+  gBrowser.removeTab(tab);
+
+  Services.prefs.clearUserPref(TP_PREF);
+});
--- a/browser/components/BrowserGlue.jsm
+++ b/browser/components/BrowserGlue.jsm
@@ -299,17 +299,17 @@ let ACTORS = {
 };
 
 (function earlyBlankFirstPaint() {
   if (!Services.prefs.getBoolPref("browser.startup.blankWindow", false))
     return;
 
   // Until bug 1450626 and bug 1488384 are fixed, skip the blank window when
   // using a non-default theme.
-  if (Services.prefs.getCharPref("extensions.activeThemeID", "") !=
+  if (Services.prefs.getCharPref("extensions.activeThemeID", "default-theme@mozilla.org") !=
         "default-theme@mozilla.org")
     return;
 
   let store = Services.xulStore;
   let getValue = attr =>
     store.getValue(AppConstants.BROWSER_CHROME_URL, "main-window", attr);
   let width = getValue("width");
   let height = getValue("height");
--- a/browser/components/extensions/test/browser/browser_ext_browserAction_contextMenu.js
+++ b/browser/components/extensions/test/browser/browser_ext_browserAction_contextMenu.js
@@ -67,17 +67,17 @@ function assertTelemetryMatches(events) 
     .map(relatedEvent => relatedEvent.slice(2, 6));
 
   // Events are now [method, object, value, extra] as expected.
   Assert.deepEqual(relatedEvents, events, "The events are recorded correctly");
 }
 
 add_task(async function test_setup() {
   // Clear any previosuly collected telemetry event.
-  Services.telemetry.snapshotEvents(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, true);
+  Services.telemetry.clearEvents();
 });
 
 add_task(async function browseraction_popup_contextmenu() {
   let extension = ExtensionTestUtils.loadExtension(extData);
   await extension.startup();
 
   await clickBrowserAction(extension, window);
 
--- a/browser/components/preferences/in-content/tests/browser_contentblocking.js
+++ b/browser/components/preferences/in-content/tests/browser_contentblocking.js
@@ -146,16 +146,17 @@ add_task(async function testContentBlock
     [TP_PREF]: null,
     [TP_PBM_PREF]: null,
     [NCB_PREF]: null,
     [FP_PREF]: null,
     [CM_PREF]: null,
   };
 
   for (let pref in prefs) {
+    Services.prefs.clearUserPref(pref);
     switch (Services.prefs.getPrefType(pref)) {
     case Services.prefs.PREF_BOOL:
       prefs[pref] = Services.prefs.getBoolPref(pref);
       break;
     case Services.prefs.PREF_INT:
       prefs[pref] = Services.prefs.getIntPref(pref);
       break;
     case Services.prefs.PREF_STRING:
@@ -164,18 +165,18 @@ add_task(async function testContentBlock
     default:
       ok(false, `Unknown pref type for ${pref}`);
     }
   }
 
   Services.prefs.setBoolPref(TP_PREF, true);
   Services.prefs.setBoolPref(TP_PBM_PREF, false);
   Services.prefs.setIntPref(NCB_PREF, Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER);
-  Services.prefs.setBoolPref(FP_PREF, true);
-  Services.prefs.setBoolPref(CM_PREF, true);
+  Services.prefs.setBoolPref(FP_PREF, !Services.prefs.getBoolPref(FP_PREF));
+  Services.prefs.setBoolPref(CM_PREF, !Services.prefs.getBoolPref(CM_PREF));
 
   for (let pref in prefs) {
     switch (Services.prefs.getPrefType(pref)) {
     case Services.prefs.PREF_BOOL:
       // Account for prefs that may have retained their default value
       if (Services.prefs.getBoolPref(pref) != prefs[pref]) {
         ok(Services.prefs.prefHasUserValue(pref), `modified the pref ${pref}`);
       }
@@ -267,17 +268,17 @@ add_task(async function testContentBlock
   await TestUtils.waitForCondition(() => !Services.prefs.prefHasUserValue(TP_PREF));
   is(Services.prefs.getStringPref(CAT_PREF), "custom", `${CAT_PREF} has been set to custom`);
 
   strictRadioOption.click();
   await TestUtils.waitForCondition(() => Services.prefs.getStringPref(CAT_PREF) == "strict");
 
   // Changing the FP_PREF and CM_PREF should necessarily set CAT_PREF to "custom"
   for (let pref of [FP_PREF, CM_PREF]) {
-    Services.prefs.setBoolPref(pref, true);
+    Services.prefs.setBoolPref(pref, !Services.prefs.getBoolPref(pref));
     await TestUtils.waitForCondition(() => Services.prefs.prefHasUserValue(pref));
     is(Services.prefs.getStringPref(CAT_PREF), "custom", `${CAT_PREF} has been set to custom`);
 
     strictRadioOption.click();
     await TestUtils.waitForCondition(() => Services.prefs.getStringPref(CAT_PREF) == "strict");
   }
 
   // Changing the NCB_PREF should necessarily set CAT_PREF to "custom"
--- a/browser/extensions/fxmonitor/moz.build
+++ b/browser/extensions/fxmonitor/moz.build
@@ -22,14 +22,13 @@ FINAL_TARGET_FILES.features['fxmonitor@m
 FINAL_TARGET_FILES.features['fxmonitor@mozilla.org']['privileged'] += [
   'privileged/api.js',
   'privileged/FirefoxMonitor.css',
   'privileged/FirefoxMonitor.jsm',
   'privileged/schema.json'
 ]
 
 FINAL_TARGET_FILES.features['fxmonitor@mozilla.org']['privileged']['subscripts'] += [
-  'privileged/subscripts/EveryWindow.jsm',
   'privileged/subscripts/Globals.jsm'
 ]
 
 with Files('**'):
   BUG_COMPONENT = ('Firefox', 'Firefox Monitor')
--- a/browser/extensions/fxmonitor/privileged/FirefoxMonitor.jsm
+++ b/browser/extensions/fxmonitor/privileged/FirefoxMonitor.jsm
@@ -93,23 +93,20 @@ this.FirefoxMonitor = {
   _delayedInited: false,
   async delayedInit() {
     if (this._delayedInited) {
       return;
     }
 
     this._delayedInited = true;
 
-    /* globals Preferences, RemoteSettings, fetch, btoa, XUL_NS */
+    /* globals EveryWindow, Preferences, RemoteSettings, fetch, btoa, XUL_NS */
     Services.scriptloader.loadSubScript(
       this.getURL("privileged/subscripts/Globals.jsm"));
 
-    /* globals EveryWindow */
-    Services.scriptloader.loadSubScript(
-      this.getURL("privileged/subscripts/EveryWindow.jsm"));
 
     // Expire our telemetry on November 1, at which time
     // we should redo data-review.
     let telemetryExpiryDate = new Date(2019, 10, 1); // Month is zero-index
     let today = new Date();
     let expired = today.getTime() > telemetryExpiryDate.getTime();
 
     Services.telemetry.registerEvents("fxmonitor", {
deleted file mode 100644
--- a/browser/extensions/fxmonitor/privileged/subscripts/EveryWindow.jsm
+++ /dev/null
@@ -1,62 +0,0 @@
-/* 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/. */
-
-/* globals Services */
-
-this.EveryWindow = {
-  _callbacks: new Map(),
-  _initialized: false,
-
-  registerCallback: function EW_registerCallback(id, init, uninit) {
-    if (this._callbacks.has(id)) {
-      return;
-    }
-
-    this._callForEveryWindow(init);
-    this._callbacks.set(id, {id, init, uninit});
-
-    if (!this._initialized) {
-      Services.obs.addObserver(this._onOpenWindow.bind(this),
-                               "browser-delayed-startup-finished");
-      this._initialized = true;
-    }
-  },
-
-  unregisterCallback: function EW_unregisterCallback(aId, aCallUninit = true) {
-    if (!this._callbacks.has(aId)) {
-      return;
-    }
-
-    if (aCallUninit) {
-      this._callForEveryWindow(this._callbacks.get(aId).uninit);
-    }
-
-    this._callbacks.delete(aId);
-  },
-
-  _callForEveryWindow(aFunction) {
-    let windowList = Services.wm.getEnumerator("navigator:browser");
-    while (windowList.hasMoreElements()) {
-      let win = windowList.getNext();
-      win.delayedStartupPromise.then(() => { aFunction(win); });
-    }
-  },
-
-  _onOpenWindow(aWindow) {
-    for (let c of this._callbacks.values()) {
-      c.init(aWindow);
-    }
-
-    aWindow.addEventListener("unload",
-                             this._onWindowClosing.bind(this),
-                             { once: true });
-  },
-
-  _onWindowClosing(aEvent) {
-    let win = aEvent.target;
-    for (let c of this._callbacks.values()) {
-      c.uninit(win, true);
-    }
-  },
-};
--- a/browser/extensions/fxmonitor/privileged/subscripts/Globals.jsm
+++ b/browser/extensions/fxmonitor/privileged/subscripts/Globals.jsm
@@ -5,12 +5,14 @@
 /* eslint-disable no-unused-vars */
 
 ChromeUtils.defineModuleGetter(this, "Preferences",
                                "resource://gre/modules/Preferences.jsm");
 ChromeUtils.defineModuleGetter(this, "PluralForm",
                                "resource://gre/modules/PluralForm.jsm");
 ChromeUtils.defineModuleGetter(this, "RemoteSettings",
                                "resource://services-settings/remote-settings.js");
+ChromeUtils.defineModuleGetter(this, "EveryWindow",
+                               "resource:///modules/EveryWindow.jsm");
 const {setTimeout, clearTimeout} = ChromeUtils.import("resource://gre/modules/Timer.jsm", {});
 Cu.importGlobalProperties(["fetch", "btoa"]);
 
 const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
new file mode 100644
--- /dev/null
+++ b/browser/modules/EveryWindow.jsm
@@ -0,0 +1,109 @@
+/* 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 = ["EveryWindow"];
+
+/*
+ * This module enables consumers to register callbacks on every
+ * current and future browser window.
+ *
+ * Usage: EveryWindow.registerCallback(id, init, uninit);
+ *        EveryWindow.unregisterCallback(id);
+ *
+ * id is expected to be a unique value that identifies the
+ * consumer, to be used for unregistration. If the id is already
+ * in use, registerCallback returns false without doing anything.
+ *
+ * Each callback will receive the window for which it is presently
+ * being called as the first argument.
+ *
+ * init is called on every existing window at the time of registration,
+ * and on all future windows at browser-delayed-startup-finished.
+ *
+ * uninit is called on every existing window if requested at the time
+ * of unregistration, and at the time of domwindowclosed.
+ * If the window is closing, a second argument is passed with value `true`.
+ */
+
+const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+var initialized = false;
+var callbacks = new Map();
+
+function callForEveryWindow(callback) {
+  let windowList = Services.wm.getEnumerator("navigator:browser");
+  for (let win of windowList) {
+    win.delayedStartupPromise.then(() => { callback(win); });
+  }
+}
+
+this.EveryWindow = {
+  /**
+   * Registers init and uninit functions to be called on every window.
+   *
+   * @param {string} id A unique identifier for the consumer, to be
+   *   used for unregistration.
+   * @param {function} init The function to be called on every currently
+   *   existing window and every future window after delayed startup.
+   * @param {function} uninit The function to be called on every window
+   *   at the time of callback unregistration or after domwindowclosed.
+   * @returns {boolean} Returns false if the id was taken, else true.
+   */
+  registerCallback: function EW_registerCallback(id, init, uninit) {
+    if (callbacks.has(id)) {
+      return false;
+    }
+
+    if (!initialized) {
+      let addUnloadListener = (win) => {
+        function observer(subject, topic, data) {
+          if (topic == "domwindowclosed" && subject === win) {
+            Services.ww.unregisterNotification(observer);
+            for (let c of callbacks.values()) {
+              c.uninit(win, true);
+            }
+          }
+        }
+        Services.ww.registerNotification(observer);
+      };
+
+      Services.obs.addObserver(win => {
+        for (let c of callbacks.values()) {
+          c.init(win);
+        }
+        addUnloadListener(win);
+      }, "browser-delayed-startup-finished");
+
+      callForEveryWindow(addUnloadListener);
+
+      initialized = true;
+    }
+
+    callForEveryWindow(init);
+    callbacks.set(id, {id, init, uninit});
+
+    return true;
+  },
+
+  /**
+   * Unregisters a previously registered consumer.
+   *
+   * @param {string} id The id to unregister.
+   * @param {boolean} [callUninit=true] Whether to call the registered uninit
+   *   function on every window.
+   */
+  unregisterCallback: function EW_unregisterCallback(id, callUninit = true) {
+    if (!callbacks.has(id)) {
+      return;
+    }
+
+    if (callUninit) {
+      callForEveryWindow(callbacks.get(id).uninit);
+    }
+
+    callbacks.delete(id);
+  },
+};
--- a/browser/modules/moz.build
+++ b/browser/modules/moz.build
@@ -56,16 +56,19 @@ with Files("*Telemetry.jsm"):
     BUG_COMPONENT = ("Toolkit", "Telemetry")
 
 with Files("ContentCrashHandlers.jsm"):
     BUG_COMPONENT = ("Toolkit", "Crash Reporting")
 
 with Files("ContentSearch.jsm"):
     BUG_COMPONENT = ("Firefox", "Search")
 
+with Files("EveryWindow.jsm"):
+    BUG_COMPONENT = ("Firefox", "General")
+
 with Files("ExtensionsUI.jsm"):
     BUG_COMPONENT = ("WebExtensions", "General")
 
 with Files("LaterRun.jsm"):
     BUG_COMPONENT = ("Firefox", "Tours")
 
 with Files("LiveBookmarkMigrator.jsm"):
     BUG_COMPONENT = ("Firefox", "General")
@@ -131,16 +134,17 @@ EXTRA_JS_MODULES += [
     'BrowserUsageTelemetry.jsm',
     'BrowserWindowTracker.jsm',
     'ContentClick.jsm',
     'ContentCrashHandlers.jsm',
     'ContentMetaHandler.jsm',
     'ContentObservers.js',
     'ContentSearch.jsm',
     'Discovery.jsm',
+    'EveryWindow.jsm',
     'ExtensionsUI.jsm',
     'FaviconLoader.jsm',
     'FormValidationHandler.jsm',
     'HomePage.jsm',
     'LaterRun.jsm',
     'LiveBookmarkMigrator.jsm',
     'NewTabPagePreloading.jsm',
     'OpenInTabsUtils.jsm',
--- a/browser/modules/test/browser/browser.ini
+++ b/browser/modules/test/browser/browser.ini
@@ -9,16 +9,17 @@ prefs =
 support-files =
   contentSearch.js
   contentSearchBadImage.xml
   contentSearchSuggestions.sjs
   contentSearchSuggestions.xml
   !/browser/components/search/test/browser/head.js
   !/browser/components/search/test/browser/testEngine.xml
   testEngine_chromeicon.xml
+[browser_EveryWindow.js]
 [browser_LiveBookmarkMigrator.js]
 [browser_PageActions.js]
 [browser_PermissionUI.js]
 [browser_PermissionUI_prompts.js]
 [browser_preloading_tab_moving.js]
 [browser_ProcessHangNotifications.js]
 skip-if = !e10s
 [browser_SitePermissions.js]
new file mode 100644
--- /dev/null
+++ b/browser/modules/test/browser/browser_EveryWindow.js
@@ -0,0 +1,129 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+/* eslint-disable mozilla/no-arbitrary-setTimeout */
+
+const {EveryWindow} = ChromeUtils.import("resource:///modules/EveryWindow.jsm");
+
+async function windowInited(aId, aWin) {
+  // TestUtils.topicObserved returns [subject, data]. We return the
+  // subject, which in this case is the window.
+  return (await TestUtils.topicObserved(`${aId}:init`, (win) => {
+    return aWin ? win == aWin : true;
+  }))[0];
+}
+
+function windowUninited(aId, aWin, aClosing) {
+  return TestUtils.topicObserved(`${aId}:uninit`, (win, closing) => {
+    if (aWin && aWin != win) {
+      return false;
+    }
+    if (!aWin) {
+      return true;
+    }
+    if (!!aClosing != !!closing) {
+      return false;
+    }
+    return true;
+  });
+}
+
+function registerEWCallback(id) {
+  EveryWindow.registerCallback(
+    id,
+    (win) => {
+      Services.obs.notifyObservers(win, `${id}:init`);
+    },
+    (win, closing) => {
+      Services.obs.notifyObservers(win, `${id}:uninit`, closing);
+    },
+  );
+}
+
+function unregisterEWCallback(id, aCallUninit) {
+  EveryWindow.unregisterCallback(id, aCallUninit);
+}
+
+add_task(async function test_stuff() {
+  let win2 = await BrowserTestUtils.openNewBrowserWindow();
+  let win3 = await BrowserTestUtils.openNewBrowserWindow();
+
+  let callbackId1 = "EveryWindow:test:1";
+  let callbackId2 = "EveryWindow:test:2";
+
+  let initPromise = Promise.all([windowInited(callbackId1, window),
+                                windowInited(callbackId1, win2),
+                                windowInited(callbackId1, win3),
+                                windowInited(callbackId2, window),
+                                windowInited(callbackId2, win2),
+                                windowInited(callbackId2, win3)]);
+
+  registerEWCallback(callbackId1);
+  registerEWCallback(callbackId2);
+
+  await initPromise;
+  ok(true, "Init called for all existing windows for all registered consumers");
+
+  let uninitPromise = Promise.all([windowUninited(callbackId1, window, false),
+                                  windowUninited(callbackId1, win2, false),
+                                  windowUninited(callbackId1, win3, false),
+                                  windowUninited(callbackId2, window, false),
+                                  windowUninited(callbackId2, win2, false),
+                                  windowUninited(callbackId2, win3, false)]);
+
+  unregisterEWCallback(callbackId1);
+  unregisterEWCallback(callbackId2);
+  await uninitPromise;
+  ok(true, "Uninit called for all existing windows");
+
+  initPromise = Promise.all([windowInited(callbackId1, window),
+                            windowInited(callbackId1, win2),
+                            windowInited(callbackId1, win3),
+                            windowInited(callbackId2, window),
+                            windowInited(callbackId2, win2),
+                            windowInited(callbackId2, win3)]);
+
+  registerEWCallback(callbackId1);
+  registerEWCallback(callbackId2);
+
+  await initPromise;
+  ok(true, "Init called for all existing windows for all registered consumers");
+
+  uninitPromise = Promise.all([windowUninited(callbackId1, win2, true),
+                              windowUninited(callbackId2, win2, true)]);
+  await BrowserTestUtils.closeWindow(win2);
+  await uninitPromise;
+  ok(true, "Uninit called with closing=true for win2 for all registered consumers");
+
+  uninitPromise = Promise.all([windowUninited(callbackId1, win3, true),
+                              windowUninited(callbackId2, win3, true)]);
+  await BrowserTestUtils.closeWindow(win3);
+  await uninitPromise;
+  ok(true, "Uninit called with closing=true for win3 for all registered consumers");
+
+  initPromise = windowInited(callbackId1);
+  let initPromise2 = windowInited(callbackId2);
+  win2 = await BrowserTestUtils.openNewBrowserWindow();
+  is(await initPromise, win2, "Init called for new window for callback 1");
+  is(await initPromise2, win2, "Init called for new window for callback 2");
+
+  uninitPromise = Promise.all([windowUninited(callbackId1, win2, true),
+                              windowUninited(callbackId2, win2, true)]);
+  await BrowserTestUtils.closeWindow(win2);
+  await uninitPromise;
+  ok(true, "Uninit called with closing=true for win2 for all registered consumers");
+
+  uninitPromise = windowUninited(callbackId1, window, false);
+  unregisterEWCallback(callbackId1);
+  await uninitPromise;
+  ok(true, "Uninit called for main window without closing flag for the unregistered consumer");
+
+  uninitPromise = windowUninited(callbackId2, window, false);
+  let timeoutPromise = new Promise(resolve => setTimeout(resolve, 500));
+  unregisterEWCallback(callbackId2, false);
+  let result = await Promise.race([uninitPromise, timeoutPromise]);
+  is(result, undefined, "Uninit not called when unregistering a consumer with aCallUninit=false");
+});
--- a/build/unix/elfhack/elf.cpp
+++ b/build/unix/elfhack/elf.cpp
@@ -492,17 +492,20 @@ ElfSection::ElfSection(Elf_Shdr &s, std:
                                      : parent->getSection(shdr.sh_link)),
       next(nullptr),
       previous(nullptr),
       index(-1) {
   if ((file == nullptr) || (shdr.sh_type == SHT_NULL) ||
       (shdr.sh_type == SHT_NOBITS))
     data = nullptr;
   else {
-    data = new char[shdr.sh_size];
+    data = static_cast<char *>(malloc(shdr.sh_size));
+    if (!data) {
+      throw std::runtime_error("Could not malloc ElfSection data");
+    }
     int pos = file->tellg();
     file->seekg(shdr.sh_offset);
     file->read(data, shdr.sh_size);
     file->seekg(pos);
   }
   if (shdr.sh_name == 0)
     name = nullptr;
   else {
--- a/build/unix/elfhack/elfhack.cpp
+++ b/build/unix/elfhack/elfhack.cpp
@@ -174,17 +174,20 @@ class ElfRelHackCode_Section : public El
         addr = (addr | ((*c)->getAddrAlign() - 1)) + 1;
       (*c)->getShdr().sh_addr = addr;
       // We need to align this section depending on the greater
       // alignment required by code sections.
       if (shdr.sh_addralign < (*c)->getAddrAlign())
         shdr.sh_addralign = (*c)->getAddrAlign();
     }
     shdr.sh_size = code.back()->getAddr() + code.back()->getSize();
-    data = new char[shdr.sh_size];
+    data = static_cast<char *>(malloc(shdr.sh_size));
+    if (!data) {
+      throw std::runtime_error("Could not malloc ElfSection data");
+    }
     char *buf = data;
     for (c = code.begin(); c != code.end(); ++c) {
       memcpy(buf, (*c)->getData(), (*c)->getSize());
       buf += (*c)->getSize();
     }
     name = elfhack_text;
   }
 
--- a/build/unix/elfhack/elfxx.h
+++ b/build/unix/elfhack/elfxx.h
@@ -320,17 +320,17 @@ class ElfSection {
  public:
   typedef union {
     ElfSection *section;
     int index;
   } SectionInfo;
 
   ElfSection(Elf_Shdr &s, std::ifstream *file, Elf *parent);
 
-  virtual ~ElfSection() { delete[] data; }
+  virtual ~ElfSection() { free(data); }
 
   const char *getName() { return name; }
   unsigned int getType() { return shdr.sh_type; }
   unsigned int getFlags() { return shdr.sh_flags; }
   unsigned int getAddr();
   unsigned int getSize() { return shdr.sh_size; }
   unsigned int getAddrAlign() { return shdr.sh_addralign; }
   unsigned int getEntSize() { return shdr.sh_entsize; }
--- a/devtools/client/aboutdebugging-new/src/components/RuntimePage.js
+++ b/devtools/client/aboutdebugging-new/src/components/RuntimePage.js
@@ -66,17 +66,17 @@ class RuntimePage extends PureComponent 
     dispatch(Actions.selectPage(PAGE_TYPES.RUNTIME, runtimeId));
   }
 
   getIconByType(type) {
     switch (type) {
       case DEBUG_TARGETS.EXTENSION:
         return "chrome://devtools/skin/images/debugging-addons.svg";
       case DEBUG_TARGETS.PROCESS:
-        return "chrome://devtools/skin/images/settings.svg";
+        return "chrome://devtools/skin/images/aboutdebugging-process-icon.svg";
       case DEBUG_TARGETS.TAB:
         return "chrome://devtools/skin/images/debugging-tabs.svg";
       case DEBUG_TARGETS.WORKER:
         return "chrome://devtools/skin/images/debugging-workers.svg";
     }
 
     throw new Error(`Unsupported type [${ type }]`);
   }
--- a/devtools/client/aboutdebugging-new/src/middleware/process-component-data.js
+++ b/devtools/client/aboutdebugging-new/src/middleware/process-component-data.js
@@ -25,17 +25,17 @@ const processComponentDataMiddleware = s
   }
 
   return next(action);
 };
 
 function toMainProcessComponentData(process) {
   const type = DEBUG_TARGETS.PROCESS;
   const id = process.processFront.actorID;
-  const icon = "chrome://devtools/skin/images/settings.svg";
+  const icon = "chrome://devtools/skin/images/aboutdebugging-process-icon.svg";
   const name = l10n.getString("about-debugging-main-process-name");
   const description = l10n.getString("about-debugging-main-process-description2");
 
   return {
     name,
     icon,
     id,
     type,
--- a/devtools/client/debugger/images/breakpoint.svg
+++ b/devtools/client/debugger/images/breakpoint.svg
@@ -1,6 +1,6 @@
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 15" width="60" height="15">
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 15" width="60" height="15" stroke="currentColor">
   <path d="M53.07.5H1.5c-.54 0-1 .46-1 1v12c0 .54.46 1 1 1h51.57c.58 0 1.15-.26 1.53-.7l4.7-6.3-4.7-6.3c-.38-.44-.95-.7-1.53-.7z"/>
 </svg>
--- a/devtools/client/debugger/packages/devtools-reps/src/reps/tests/grip.js
+++ b/devtools/client/debugger/packages/devtools-reps/src/reps/tests/grip.js
@@ -259,37 +259,16 @@ describe("Grip - Object with more than l
     const props = Array.from({ length: maxLengthMap.get(MODE.LONG) }).map(
       (item, i) => `p${i}: "${i}"`
     );
     const longOutput = `Object { ${props.join(", ")}, … }`;
     expect(renderRep({ mode: MODE.LONG }).text()).toBe(longOutput);
   });
 });
 
-describe("Grip - Object with uninteresting properties", () => {
-  // Test object: `{a: undefined, b: undefined, c: "c", d: 1}`
-  const object = stubs.get("testUninterestingProps");
-
-  it("correctly selects Grip Rep", () => {
-    expect(getRep(object)).toBe(Grip.rep);
-  });
-
-  it.skip("renders as expected", () => {
-    // @TODO This is broken at the moment.
-    // See https://bugzilla.mozilla.org/show_bug.cgi?id=1276376
-    const renderRep = props => shallowRenderRep(object, props);
-    const defaultOutput = 'Object {c: "c", d: 1, a: undefined, more...}';
-
-    expect(renderRep({ mode: undefined }).text()).toBe(defaultOutput);
-    expect(renderRep({ mode: MODE.TINY }).text()).toBe("{…}");
-    expect(renderRep({ mode: MODE.SHORT }).text()).toBe(defaultOutput);
-    expect(renderRep({ mode: MODE.LONG }).text()).toBe(defaultOutput);
-  });
-});
-
 describe("Grip - Object with non-enumerable properties", () => {
   // Test object: `Object.defineProperty({}, "foo", {enumerable : false});`
   const object = stubs.get("testNonEnumerableProps");
 
   it("correctly selects Grip Rep", () => {
     expect(getRep(object)).toBe(Grip.rep);
   });
 
--- a/devtools/client/debugger/src/actions/breakpoints/index.js
+++ b/devtools/client/debugger/src/actions/breakpoints/index.js
@@ -242,17 +242,18 @@ export function toggleBreakpointAtLine(c
       })
     );
   };
 }
 
 export function addBreakpointAtLine(
   cx: Context,
   line: number,
-  shouldLog: ?boolean = false
+  shouldLog: boolean = false,
+  disabled: boolean = false
 ) {
   return ({ dispatch, getState, client, sourceMaps }: ThunkArgs) => {
     const state = getState();
     const source = getSelectedSource(state);
 
     if (!source || isEmptyLineInSource(state, line, source.id)) {
       return;
     }
@@ -263,17 +264,17 @@ export function addBreakpointAtLine(
       line
     };
 
     const options = {};
     if (shouldLog) {
       options.logValue = "displayName";
     }
 
-    return dispatch(addBreakpoint(cx, breakpointLocation, options));
+    return dispatch(addBreakpoint(cx, breakpointLocation, options, disabled));
   };
 }
 
 export function removeBreakpointsAtLine(
   cx: Context,
   sourceId: string,
   line: number
 ) {
--- a/devtools/client/debugger/src/components/Editor/Breakpoints.css
+++ b/devtools/client/debugger/src/components/Editor/Breakpoints.css
@@ -1,38 +1,31 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 .theme-light {
   --gutter-hover-background-color: #dde1e4;
-
   --breakpoint-fill: var(--blue-50);
   --breakpoint-stroke: var(--blue-60);
-
-  --breakpoint-disabled-fill: var(--blue-55);
-  --breakpoint-disabled-stroke: var(--blue-55);
 }
 
 .theme-dark {
   --gutter-hover-background-color: #414141;
-
   --breakpoint-fill: var(--blue-55);
   --breakpoint-stroke: var(--blue-40);
-
-  --breakpoint-disabled-fill: var(--blue-55);
-  --breakpoint-disabled-stroke: var(--blue-55);
 }
 
 .theme-light,
 .theme-dark {
   --logpoint-fill: var(--theme-graphs-purple);
   --logpoint-stroke: var(--purple-60);
   --breakpoint-condition-fill: var(--theme-graphs-yellow);
   --breakpoint-condition-stroke: var(--theme-graphs-orange);
+  --breakpoint-skipped-opacity: 0.15;
   --breakpoint-inactive-opacity: 0.3;
   --breakpoint-disabled-opacity: 0.6;
 }
 
 /* Standard gutter breakpoints */
 .editor-wrapper .breakpoints {
   position: absolute;
   top: 0;
@@ -47,17 +40,17 @@
   > .CodeMirror-gutter-wrapper:hover
   > .CodeMirror-linenumber::after {
   content: "";
   position: absolute;
   /* paint below the number */
   z-index: -1;
   top: 0;
   left: 0;
-  right: -7px;
+  right: -4px;
   bottom: 0;
   height: 15px;
   background-color: var(--gutter-hover-background-color);
   mask: url(resource://devtools/client/debugger/images/breakpoint.svg)
     no-repeat;
   mask-size: auto 15px;
   mask-position: right;
 }
@@ -91,16 +84,20 @@
   stroke: var(--logpoint-stroke);
 }
 
 .editor.new-breakpoint.breakpoint-disabled svg {
   fill-opacity: var(--breakpoint-disabled-opacity);
   stroke-opacity: var(--breakpoint-disabled-opacity);
 }
 
+.editor-wrapper.skip-pausing .editor.new-breakpoint svg {
+  fill-opacity: var(--breakpoint-skipped-opacity);
+}
+
 /* Columnn breakpoints */
 .column-breakpoint {
   display: inline;
   padding-inline-start: 1px;
   padding-inline-end: 1px;
 }
 
 .column-breakpoint:hover {
@@ -131,26 +128,25 @@
   stroke-opacity: var(--breakpoint-disabled-opacity);
 }
 
 .column-breakpoint.has-log.disabled svg {
   fill-opacity: 0.5;
   stroke-opacity: 0.5;
 }
 
-.column-breakpoint.disabled:not(.has-condition):not(.has-log) svg {
-  fill: var(--breakpoint-disabled-fill);
-  stroke: var(--breakpoint-disabled-stroke);
-}
-
 .column-breakpoint.has-condition svg {
   fill: var(--breakpoint-condition-fill);
   stroke: var(--breakpoint-condition-stroke);
 }
 
 .column-breakpoint.has-log svg {
   fill: var(--logpoint-fill);
   stroke: var(--logpoint-stroke);
 }
 
+.editor-wrapper.skip-pausing .column-breakpoint svg {
+  fill-opacity: var(--breakpoint-skipped-opacity);
+}
+
 .img.column-marker {
   background-image: url(resource://devtools/client/debugger/images/column-marker.svg);
 }
--- a/devtools/client/debugger/src/components/Editor/Editor.css
+++ b/devtools/client/debugger/src/components/Editor/Editor.css
@@ -84,17 +84,17 @@ html[dir="rtl"] .editor-mount {
 }
 
 .folding-enabled .CodeMirror-linenumber {
   text-align: left;
   padding: 0 0 0 2px;
 }
 
 /* set the linenumber white when there is a breakpoint */
-.cm-s-mozilla
+.editor-wrapper:not(.skip-pausing)
   .new-breakpoint
   .CodeMirror-gutter-wrapper
   .CodeMirror-linenumber {
   color: white;
 }
 
 /* move the breakpoint below the other gutter elements */
 .new-breakpoint .CodeMirror-gutter-elt:nth-child(2) {
--- a/devtools/client/debugger/src/components/Editor/index.js
+++ b/devtools/client/debugger/src/components/Editor/index.js
@@ -31,17 +31,18 @@ import type { EditorItemActions } from "
 import {
   getActiveSearch,
   getSelectedLocation,
   getSelectedSource,
   getConditionalPanelLocation,
   getSymbols,
   getIsPaused,
   getCurrentThread,
-  getThreadContext
+  getThreadContext,
+  getSkipPausing
 } from "../../selectors";
 
 // Redux actions
 import actions from "../../actions";
 
 import SearchBar from "./SearchBar";
 import HighlightLines from "./HighlightLines";
 import Preview from "./Preview";
@@ -91,16 +92,17 @@ export type Props = {
   selectedLocation: ?SourceLocation,
   selectedSource: ?Source,
   searchOn: boolean,
   startPanelSize: number,
   endPanelSize: number,
   conditionalPanelLocation: SourceLocation,
   symbols: SymbolDeclarations,
   isPaused: boolean,
+  skipPausing: boolean,
 
   // Actions
   openConditionalPanel: typeof actions.openConditionalPanel,
   closeConditionalPanel: typeof actions.closeConditionalPanel,
   continueToHere: typeof actions.continueToHere,
   addBreakpointAtLine: typeof actions.addBreakpointAtLine,
   jumpToMappedLocation: typeof actions.jumpToMappedLocation,
   toggleBreakpointAtLine: typeof actions.toggleBreakpointAtLine,
@@ -413,17 +415,17 @@ class Editor extends PureComponent<Props
     if (typeof sourceLine !== "number") {
       return;
     }
 
     if (ev.metaKey) {
       return continueToHere(cx, sourceLine);
     }
 
-    return addBreakpointAtLine(cx, sourceLine, ev.altKey);
+    return addBreakpointAtLine(cx, sourceLine, ev.altKey, ev.shiftKey);
   };
 
   onGutterContextMenu = (event: MouseEvent) => {
     return this.openMenu(event);
   };
 
   onClick(e: MouseEvent) {
     const { cx, selectedSource, jumpToMappedLocation } = this.props;
@@ -609,21 +611,22 @@ class Editor extends PureComponent<Props
     if (!this.props.selectedSource) {
       return null;
     }
 
     return <SearchBar editor={editor} />;
   }
 
   render() {
-    const { selectedSource } = this.props;
+    const { selectedSource, skipPausing } = this.props;
     return (
       <div
         className={classnames("editor-wrapper", {
-          blackboxed: selectedSource && selectedSource.isBlackBoxed
+          blackboxed: selectedSource && selectedSource.isBlackBoxed,
+          "skip-pausing": skipPausing
         })}
         ref={c => (this.$editorWrapper = c)}
       >
         <div
           className="editor-mount devtools-monospace"
           style={this.getInlineEditorStyles()}
         />
         {this.renderSearchBar()}
@@ -642,17 +645,18 @@ const mapStateToProps = state => {
 
   return {
     cx: getThreadContext(state),
     selectedLocation: getSelectedLocation(state),
     selectedSource,
     searchOn: getActiveSearch(state) === "file",
     conditionalPanelLocation: getConditionalPanelLocation(state),
     symbols: getSymbols(state, selectedSource),
-    isPaused: getIsPaused(state, getCurrentThread(state))
+    isPaused: getIsPaused(state, getCurrentThread(state)),
+    skipPausing: getSkipPausing(state)
   };
 };
 
 const mapDispatchToProps = dispatch => ({
   ...bindActionCreators(
     {
       openConditionalPanel: actions.openConditionalPanel,
       closeConditionalPanel: actions.closeConditionalPanel,
--- a/devtools/client/debugger/src/components/SecondaryPanes/Breakpoints/Breakpoint.js
+++ b/devtools/client/debugger/src/components/SecondaryPanes/Breakpoints/Breakpoint.js
@@ -149,16 +149,17 @@ class Breakpoint extends PureComponent<P
   );
 
   /* eslint-disable react/no-danger */
   render() {
     const { breakpoint } = this.props;
     const text = this.getBreakpointText();
     const editor = getEditor();
     const labelId = `${breakpoint.id}-label`;
+
     return (
       <div
         className={classnames({
           breakpoint,
           paused: this.isCurrentlyPausedAtBreakpoint(),
           disabled: breakpoint.disabled,
           "is-conditional": !!breakpoint.options.condition,
           "is-log": !!breakpoint.options.logValue
--- a/devtools/client/debugger/src/components/SecondaryPanes/Breakpoints/Breakpoints.css
+++ b/devtools/client/debugger/src/components/SecondaryPanes/Breakpoints/Breakpoints.css
@@ -1,23 +1,24 @@
 /* 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/>. */
 
 .breakpoints-toggle {
   margin: 2px 3px;
 }
 
+.breakpoints-exceptions-options *,
 .breakpoints-list * {
+  -moz-user-select: none;
   user-select: none;
 }
 
 .breakpoints-list {
-  margin-top: 4px;
-  margin-bottom: 4px;
+  padding: 4px 0;
 }
 
 .breakpoints-list .breakpoint-heading {
   text-overflow: ellipsis;
   width: 100%;
   font-size: 12px;
   line-height: 16px;
 }
@@ -119,21 +120,16 @@ html .breakpoints-list .breakpoint.is-lo
   border-inline-start-color: var(--theme-graphs-purple);
 }
 
 html .breakpoints-list .breakpoint.paused {
   background-color: var(--theme-toolbar-background-alt);
   border-color: var(--breakpoint-active-color);
 }
 
-.breakpoints-list .breakpoint.disabled .breakpoint-label {
-  color: var(--theme-comment);
-  transition: color 0.15s linear;
-}
-
 .breakpoints-list .breakpoint:hover {
   background-color: var(--search-overlays-semitransparent);
 }
 
 .breakpoint-line-close {
   margin-inline-start: 4px;
 }
 
@@ -200,21 +196,28 @@ html .breakpoints-list .breakpoint.pause
 [dir="ltr"] .breakpoint .close-btn {
   right: 12px;
 }
 
 [dir="rtl"] .breakpoint .close-btn {
   left: 12px;
 }
 
+/* Reveal the remove button on hover/focus */
 .breakpoint:hover .close-btn,
 .breakpoint .close-btn:focus {
   top: calc(50% - 8px);
 }
 
+/* Hide the line number when revealing the remove button (since they're overlayed) */
+.breakpoint-line-close:focus-within .breakpoint-line,
+.breakpoint:hover .breakpoint-line {
+  visibility: hidden;
+}
+
 .CodeMirror.cm-s-mozilla-breakpoint {
   cursor: pointer;
 }
 
 .CodeMirror.cm-s-mozilla-breakpoint .CodeMirror-lines {
   padding: 0;
 }
 
@@ -240,8 +243,27 @@ html .breakpoints-list .breakpoint.pause
 .CodeMirror.cm-s-mozilla-breakpoint .CodeMirror-code,
 .CodeMirror.cm-s-mozilla-breakpoint .CodeMirror-scroll {
   pointer-events: none;
 }
 
 .CodeMirror.cm-s-mozilla-breakpoint {
   padding-top: 1px;
 }
+
+/**
+ * Skip Pausing style
+ * Add a gray background and lower content opacity
+ */
+.breakpoints-pane .pane.skip-pausing {
+  background-color: var(--theme-toolbar-hover);
+}
+
+.breakpoints-pane .pane.skip-pausing > * {
+  opacity: 0.6;
+}
+
+.breakpoints-pane .skip-pausing .breakpoint-label,
+.breakpoints-pane .skip-pausing .breakpoint-heading .filename,
+.breakpoints-pane .skip-pausing .breakpoint-exceptions-label,
+.breakpoints-pane .skip-pausing .breakpoint .breakpoint-line {
+  color: var(--theme-body-color);
+}
--- a/devtools/client/debugger/src/components/SecondaryPanes/Breakpoints/index.js
+++ b/devtools/client/debugger/src/components/SecondaryPanes/Breakpoints/index.js
@@ -17,26 +17,31 @@ import actions from "../../../actions";
 import { getDisplayPath } from "../../../utils/source";
 import { getSelectedLocation } from "../../../utils/source-maps";
 
 import {
   makeBreakpointId,
   sortSelectedBreakpoints
 } from "../../../utils/breakpoint";
 
-import { getSelectedSource, getBreakpointSources } from "../../../selectors";
+import {
+  getSelectedSource,
+  getBreakpointSources,
+  getSkipPausing
+} from "../../../selectors";
 
 import type { Source } from "../../../types";
 import type { BreakpointSources } from "../../../selectors/breakpointSources";
 
 import "./Breakpoints.css";
 
 type Props = {
   breakpointSources: BreakpointSources,
   selectedSource: Source,
+  skipPausing: boolean,
   shouldPauseOnExceptions: boolean,
   shouldPauseOnCaughtExceptions: boolean,
   pauseOnExceptions: Function
 };
 
 class Breakpoints extends Component<Props> {
   renderExceptionsOptions() {
     const {
@@ -113,28 +118,30 @@ class Breakpoints extends Component<Prop
             ))
           ];
         })}
       </div>
     );
   }
 
   render() {
+    const { skipPausing } = this.props;
     return (
-      <div>
+      <div className={classnames("pane", skipPausing && "skip-pausing")}>
         {this.renderExceptionsOptions()}
         {this.renderBreakpoints()}
       </div>
     );
   }
 }
 
 const mapStateToProps = state => ({
   breakpointSources: getBreakpointSources(state),
-  selectedSource: getSelectedSource(state)
+  selectedSource: getSelectedSource(state),
+  skipPausing: getSkipPausing(state)
 });
 
 export default connect(
   mapStateToProps,
   {
     pauseOnExceptions: actions.pauseOnExceptions
   }
 )(Breakpoints);
--- a/devtools/client/debugger/src/components/SecondaryPanes/CommandBar.js
+++ b/devtools/client/debugger/src/components/SecondaryPanes/CommandBar.js
@@ -276,17 +276,21 @@ class CommandBar extends Component<Props
       <button
         className={classnames(
           "command-bar-button",
           "command-bar-skip-pausing",
           {
             active: skipPausing
           }
         )}
-        title={L10N.getStr("skipPausingTooltip.label")}
+        title={
+          skipPausing
+            ? L10N.getStr("undoSkipPausingTooltip.label")
+            : L10N.getStr("skipPausingTooltip.label")
+        }
         onClick={toggleSkipPausing}
       >
         <AccessibleImage className="disable-pausing" />
       </button>
     );
   }
 
   render() {
--- a/devtools/client/debugger/src/components/SecondaryPanes/Expressions.css
+++ b/devtools/client/debugger/src/components/SecondaryPanes/Expressions.css
@@ -64,18 +64,18 @@
   border: 1px solid var(--blue-50);
 }
 
 .expression-input-container.error {
   border: 1px solid red;
 }
 
 .expression-container {
-  padding-top: 2px;
-  padding-bottom: 2px;
+  padding-top: 3px;
+  padding-bottom: 3px;
   padding-inline-start: 20px;
   padding-inline-end: 12px;
   width: 100%;
   color: var(--theme-body-color);
   background-color: var(--theme-body-background);
   display: block;
   position: relative;
   overflow: hidden;
--- a/devtools/client/debugger/test/mochitest/browser_dbg-breakpoints.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg-breakpoints.js
@@ -15,61 +15,81 @@ async function disableBreakpoint(dbg, in
 }
 
 async function enableBreakpoint(dbg, index) {
   const enabled = waitForDispatch(dbg, "SET_BREAKPOINT");
   toggleBreakpoint(dbg, index);
   await enabled;
 }
 
+async function cleanupBreakpoints(dbg) {
+  clickElement(dbg, "gutter", 3);
+  clickElement(dbg, "gutter", 5);
+  await waitForBreakpointRemoved(dbg, "simple2", 3);
+  await waitForBreakpointRemoved(dbg, "simple2", 5);
+}
+
 // Test enabling and disabling a breakpoint using the check boxes
 add_task(async function() {
   const dbg = await initDebugger("doc-scripts.html", "simple2");
 
   // Create two breakpoints
   await selectSource(dbg, "simple2");
   await addBreakpoint(dbg, "simple2", 3);
   await addBreakpoint(dbg, "simple2", 5);
 
   // Disable the first one
   await disableBreakpoint(dbg, 0);
-  const bp1 = findBreakpoint(dbg, "simple2", 3);
+  let bp1 = findBreakpoint(dbg, "simple2", 3);
   let bp2 = findBreakpoint(dbg, "simple2", 5);
   is(bp1.disabled, true, "first breakpoint is disabled");
   is(bp2.disabled, false, "second breakpoint is enabled");
 
   // Disable and Re-Enable the second one
   await disableBreakpoint(dbg, 1);
   await enableBreakpoint(dbg, 1);
   bp2 = findBreakpoint(dbg, "simple2", 5);
   is(bp2.disabled, false, "second breakpoint is enabled");
-});
 
-// Test enabling and disabling a breakpoint using the context menu
-add_task(async function() {
-  const dbg = await initDebugger("doc-scripts.html");
+  // Cleanup
+  await cleanupBreakpoints(dbg);
+
+  // Test enabling and disabling a breakpoint using the context menu
   await selectSource(dbg, "simple2");
   await addBreakpoint(dbg, "simple2", 3);
   await addBreakpoint(dbg, "simple2", 5);
 
   assertEmptyLines(dbg, [1, 2]);
   assertBreakpointSnippet(dbg, 3, "return x + y;");
 
   rightClickElement(dbg, "breakpointItem", 2);
   const disableBreakpointDispatch = waitForDispatch(dbg, "SET_BREAKPOINT");
   selectContextMenuItem(dbg, selectors.breakpointContextMenu.disableSelf);
   await disableBreakpointDispatch;
 
-  let bp1 = findBreakpoint(dbg, "simple2", 3);
-  let bp2 = findBreakpoint(dbg, "simple2", 5);
+  bp1 = findBreakpoint(dbg, "simple2", 3);
+  bp2 = findBreakpoint(dbg, "simple2", 5);
   is(bp1.disabled, true, "first breakpoint is disabled");
   is(bp2.disabled, false, "second breakpoint is enabled");
 
   rightClickElement(dbg, "breakpointItem", 2);
   const enableBreakpointDispatch = waitForDispatch(dbg, "SET_BREAKPOINT");
   selectContextMenuItem(dbg, selectors.breakpointContextMenu.enableSelf);
   await enableBreakpointDispatch;
 
   bp1 = findBreakpoint(dbg, "simple2", 3);
   bp2 = findBreakpoint(dbg, "simple2", 5);
   is(bp1.disabled, false, "first breakpoint is enabled");
   is(bp2.disabled, false, "second breakpoint is enabled");
+
+  // Cleanup
+  await cleanupBreakpoints(dbg);
+
+  // Test creation of disabled breakpoint with shift-click
+  await shiftClickElement(dbg, "gutter", 3);
+  await waitForBreakpoint(dbg, "simple2", 3);
+
+  const bp = findBreakpoint(dbg, "simple2", 3);
+  is(bp.disabled, true, "breakpoint is disabled");
+
+  // Cleanup
+  await cleanupBreakpoints(dbg);
 });
--- a/devtools/client/debugger/test/mochitest/helpers.js
+++ b/devtools/client/debugger/test/mochitest/helpers.js
@@ -445,16 +445,20 @@ function waitForBreakpointCount(dbg, cou
     state => dbg.selectors.getBreakpointCount() == count
   );
 }
 
 function waitForBreakpoint(dbg, url, line) {
   return waitForState(dbg, () => findBreakpoint(dbg, url, line));
 }
 
+function waitForBreakpointRemoved(dbg, url, line) {
+  return waitForState(dbg, () => !findBreakpoint(dbg, url, line));
+}
+
 /**
  * Waits for the debugger to be fully paused.
  *
  * @memberof mochitest/waits
  * @param {Object} dbg
  * @static
  */
 async function waitForPaused(dbg, url) {
@@ -1315,22 +1319,30 @@ function dblClickElement(dbg, elementNam
 
   return EventUtils.synthesizeMouseAtCenter(
     findElementWithSelector(dbg, selector),
     { clickCount: 2 },
     dbg.win
   );
 }
 
-function altClickElement(dbg, elementName, ...args) {
+function clickElementWithOptions(dbg, elementName, options, ...args) {
   const selector = getSelector(elementName, ...args);
   const el = findElementWithSelector(dbg, selector);
   el.scrollIntoView();
 
-  return EventUtils.synthesizeMouseAtCenter(el, { altKey: true }, dbg.win);
+  return EventUtils.synthesizeMouseAtCenter(el, options, dbg.win);
+}
+
+function altClickElement(dbg, elementName, ...args) {
+  return clickElementWithOptions(dbg, elementName, { altKey: true }, ...args);
+}
+
+function shiftClickElement(dbg, elementName, ...args) {
+  return clickElementWithOptions(dbg, elementName, { shiftKey: true }, ...args);
 }
 
 function rightClickElement(dbg, elementName, ...args) {
   const selector = getSelector(elementName, ...args);
   const doc = dbg.win.document;
   return rightClickEl(dbg, doc.querySelector(selector));
 }
 
--- a/devtools/client/jar.mn
+++ b/devtools/client/jar.mn
@@ -77,16 +77,17 @@ devtools.jar:
     skin/images/aboutdebugging-connect-icon.svg (themes/images/aboutdebugging-connect-icon.svg)
     skin/images/aboutdebugging-firefox-aurora.svg (themes/images/aboutdebugging-firefox-aurora.svg)
     skin/images/aboutdebugging-firefox-beta.svg (themes/images/aboutdebugging-firefox-beta.svg)
     skin/images/aboutdebugging-firefox-logo.svg (themes/images/aboutdebugging-firefox-logo.svg)
     skin/images/aboutdebugging-firefox-nightly.svg (themes/images/aboutdebugging-firefox-nightly.svg)
     skin/images/aboutdebugging-firefox-release.svg (themes/images/aboutdebugging-firefox-release.svg)
     skin/images/aboutdebugging-globe-icon.svg (themes/images/aboutdebugging-globe-icon.svg)
     skin/images/aboutdebugging-information.svg (themes/images/aboutdebugging-information.svg)
+    skin/images/aboutdebugging-process-icon.svg (themes/images/aboutdebugging-process-icon.svg)
     skin/images/aboutdebugging-usb-icon.svg (themes/images/aboutdebugging-usb-icon.svg)
     skin/images/fox-smiling.svg (themes/images/fox-smiling.svg)
     skin/images/grid.svg (themes/images/grid.svg)
     skin/images/angle-swatch.svg (themes/images/angle-swatch.svg)
     skin/images/flexbox-swatch.svg (themes/images/flexbox-swatch.svg)
     skin/images/pseudo-class.svg (themes/images/pseudo-class.svg)
     skin/images/copy.svg (themes/images/copy.svg)
     skin/images/animation-fast-track.svg (themes/images/animation-fast-track.svg)
--- a/devtools/client/locales/en-US/debugger.properties
+++ b/devtools/client/locales/en-US/debugger.properties
@@ -97,16 +97,20 @@ stepInTooltip=Step in %S
 # LOCALIZATION NOTE (stepOutTooltip): The label that is displayed on the
 # button that steps out of a function call.
 stepOutTooltip=Step out %S
 
 # LOCALIZATION NOTE (skipPausingTooltip.label): The tooltip text for disabling all
 # breakpoints and pausing triggers
 skipPausingTooltip.label=Deactivate breakpoints
 
+# LOCALIZATION NOTE (undoSkipPausingTooltip.label): The tooltip text for enabling all
+# breakpoints and pausing triggers
+undoSkipPausingTooltip.label=Activate breakpoints
+
 # LOCALIZATION NOTE (pauseButtonItem): The label that is displayed for the dropdown pause
 # list item when the debugger is in a running state.
 pauseButtonItem=Pause on Next Statement
 
 # LOCALIZATION NOTE (ignoreExceptionsItem): The pause on exceptions button description
 # when the debugger will not pause on exceptions.
 ignoreExceptionsItem=Ignore exceptions
 
new file mode 100644
--- /dev/null
+++ b/devtools/client/themes/images/aboutdebugging-process-icon.svg
@@ -0,0 +1,7 @@
+<!-- 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/. -->
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16" fill="context-fill #0c0c0d">
+  <path d="M13 1a3 3 0 0 1 3 3v8a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V4a3 3 0 0 1 3-3h10zm0 2H3c-.54 0-1 .46-1 1v1h12V4c0-.54-.46-1-1-1zm1 3H2v6c0 .54.46 1 1 1h10c.54 0 1-.46 1-1V6z"/>
+  <path d="M4.15 7.15c.2-.2.5-.2.7 0l2 2c.2.2.2.5 0 .7l-2 2a.5.5 0 0 1-.7-.7L5.79 9.5 4.15 7.85a.5.5 0 0 1 0-.7z"/>
+</svg>
--- a/devtools/client/webconsole/middleware/event-telemetry.js
+++ b/devtools/client/webconsole/middleware/event-telemetry.js
@@ -4,16 +4,17 @@
 
 "use strict";
 
 const {
   FILTER_TEXT_SET,
   FILTER_TOGGLE,
   DEFAULT_FILTERS_RESET,
   MESSAGES_ADD,
+  PERSIST_TOGGLE,
 } = require("devtools/client/webconsole/constants");
 
 /**
  * Event telemetry middleware is responsible for logging specific events to telemetry.
  */
 function eventTelemetryMiddleware(telemetry, sessionId, store) {
   return next => action => {
     const oldState = store.getState();
@@ -35,16 +36,21 @@ function eventTelemetryMiddleware(teleme
         action,
         state,
         oldState,
         telemetry,
         sessionId,
       });
     } else if (action.type === MESSAGES_ADD) {
       messagesAdd({ action, telemetry });
+    } else if (action.type === PERSIST_TOGGLE) {
+      telemetry.recordEvent("persist_changed", "webconsole", String(state.ui.persistLogs),
+        {
+        "session_id": sessionId,
+        });
     }
 
     return res;
   };
 }
 
 function filterChange({action, state, oldState, telemetry, sessionId}) {
   const oldFilterState = oldState.filters;
--- a/devtools/client/webconsole/test/mochitest/browser.ini
+++ b/devtools/client/webconsole/test/mochitest/browser.ini
@@ -389,16 +389,17 @@ skip-if = verify
 [browser_webconsole_split_focus.js]
 [browser_webconsole_split_persist.js]
 [browser_webconsole_stacktrace_location_debugger_link.js]
 [browser_webconsole_stacktrace_location_scratchpad_link.js]
 [browser_webconsole_strict_mode_errors.js]
 [browser_webconsole_string.js]
 [browser_webconsole_telemetry_js_errors.js]
 [browser_webconsole_telemetry_filters_changed.js]
+[browser_webconsole_telemetry_persist_toggle_changed.js]
 [browser_webconsole_telemetry_jump_to_definition.js]
 [browser_webconsole_telemetry_object_expanded.js]
 [browser_webconsole_time_methods.js]
 [browser_webconsole_timestamps.js]
 [browser_webconsole_trackingprotection_errors.js]
 tags = trackingprotection
 [browser_webconsole_view_source.js]
 [browser_webconsole_visibility_messages.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/test/mochitest/browser_webconsole_telemetry_persist_toggle_changed.js
@@ -0,0 +1,60 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests the log persistence telemetry event
+
+"use strict";
+
+const { TelemetryTestUtils } = ChromeUtils.import("resource://testing-common/TelemetryTestUtils.jsm");
+
+const TEST_URI = `data:text/html,<meta charset=utf8><script>
+  console.log("test message");
+</script>`;
+
+add_task(async function() {
+  // Let's reset the counts.
+  Services.telemetry.clearEvents();
+
+  // Ensure no events have been logged
+  TelemetryTestUtils.assertNumberOfEvents(0);
+
+  const hud = await openNewTabAndConsole(TEST_URI);
+
+  // Get log persistence toggle button
+  const logPersistToggle = await waitFor(() =>
+    hud.ui.window.document.querySelector(".filter-checkbox"));
+
+  // Click on the toggle - "true"
+  logPersistToggle.click();
+  await waitUntil(() => hud.ui.wrapper.getStore().getState().ui.persistLogs === true);
+
+  // Click a second time - "false"
+  logPersistToggle.click();
+  await waitUntil(() => hud.ui.wrapper.getStore().getState().ui.persistLogs === false);
+
+  const expectedEvents = [
+    {
+      category: "devtools.main",
+      method: "persist_changed",
+      object: "webconsole",
+      value: "true",
+    },
+    {
+      category: "devtools.main",
+      method: "persist_changed",
+      object: "webconsole",
+      value: "false",
+    },
+  ];
+
+  const filter = {
+    category: "devtools.main",
+    method: "persist_changed",
+    object: "webconsole",
+  };
+
+  // Will compare filtered events to event list above
+  await TelemetryTestUtils.assertEvents(expectedEvents, filter);
+});
--- a/devtools/client/webconsole/test/mochitest/browser_webconsole_warning_groups.js
+++ b/devtools/client/webconsole/test/mochitest/browser_webconsole_warning_groups.js
@@ -4,19 +4,19 @@
 // Test that warning messages can be grouped, per navigation and category, and that
 // interacting with these groups works as expected.
 
 "use strict";
 requestLongerTimeout(2);
 
 const TEST_FILE =
   "browser/devtools/client/webconsole/test/mochitest/test-warning-groups.html";
-const TEST_URI = "http://example.com/" + TEST_FILE;
+const TEST_URI = "http://example.org/" + TEST_FILE;
 
-const TRACKER_URL = "http://tracking.example.org/";
+const TRACKER_URL = "http://tracking.example.com/";
 const BLOCKED_URL = TRACKER_URL +
   "browser/devtools/client/webconsole/test/mochitest/test-image.png";
 
 const {UrlClassifierTestUtils} = ChromeUtils.import("resource://testing-common/UrlClassifierTestUtils.jsm");
 UrlClassifierTestUtils.addTestTrackers();
 registerCleanupFunction(function() {
   UrlClassifierTestUtils.cleanupTestTrackers();
 });
--- a/devtools/client/webconsole/test/mochitest/browser_webconsole_warning_groups_outside_console_group.js
+++ b/devtools/client/webconsole/test/mochitest/browser_webconsole_warning_groups_outside_console_group.js
@@ -3,19 +3,19 @@
 
 // Test that warning groups are not created outside console.group.
 
 "use strict";
 requestLongerTimeout(2);
 
 const TEST_FILE =
   "browser/devtools/client/webconsole/test/mochitest/test-warning-groups.html";
-const TEST_URI = "http://example.com/" + TEST_FILE;
+const TEST_URI = "http://example.org/" + TEST_FILE;
 
-const TRACKER_URL = "http://tracking.example.org/";
+const TRACKER_URL = "http://tracking.example.com/";
 const BLOCKED_URL = TRACKER_URL +
   "browser/devtools/client/webconsole/test/mochitest/test-image.png";
 
 const {UrlClassifierTestUtils} = ChromeUtils.import("resource://testing-common/UrlClassifierTestUtils.jsm");
 UrlClassifierTestUtils.addTestTrackers();
 registerCleanupFunction(function() {
   UrlClassifierTestUtils.cleanupTestTrackers();
 });
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -9895,18 +9895,21 @@ nsresult nsDocShell::DoURILoad(nsDocShel
 
   // Navigational requests that are same origin need to be upgraded in case
   // upgrade-insecure-requests is present. Please note that in that case
   // the triggeringPrincipal is holding the CSP that potentially
   // holds upgrade-insecure-requests.
   nsCOMPtr<nsIContentSecurityPolicy> csp;
   aLoadState->TriggeringPrincipal()->GetCsp(getter_AddRefs(csp));
 #ifdef DEBUG
-  nsCOMPtr<nsIContentSecurityPolicy> argsCSP = aLoadState->Csp();
-  MOZ_ASSERT(nsCSPContext::Equals(csp, argsCSP));
+  if (!aLoadState->TriggeringPrincipal()->GetIsNullPrincipal()) {
+    // After Bug 965637 we can remove that assertion anyway.
+    nsCOMPtr<nsIContentSecurityPolicy> argsCSP = aLoadState->Csp();
+    MOZ_ASSERT(nsCSPContext::Equals(csp, argsCSP));
+  }
 #endif
 
   if (csp) {
     bool upgradeInsecureRequests = false;
     csp->GetUpgradeInsecureRequests(&upgradeInsecureRequests);
     if (upgradeInsecureRequests) {
       // only upgrade if the navigation is same origin
       nsCOMPtr<nsIPrincipal> resultPrincipal;
--- a/dom/base/Document.cpp
+++ b/dom/base/Document.cpp
@@ -2337,29 +2337,29 @@ void Document::FillStyleSetUserAndUAShee
   // that non-XUL (typically HTML) documents commonly use.
   mStyleSet->AppendStyleSheet(SheetType::Agent, cache->MinimalXULSheet());
 
   // Only load the full XUL sheet if we'll need it.
   if (LoadsFullXULStyleSheetUpFront()) {
     mStyleSet->AppendStyleSheet(SheetType::Agent, cache->XULSheet());
   }
 
-  MOZ_ASSERT(!mQuirkSheetAdded);
-  if (mCompatMode == eCompatibility_NavQuirks) {
-    mStyleSet->AppendStyleSheet(SheetType::Agent, cache->QuirkSheet());
-    mQuirkSheetAdded = true;
-  }
-
   mStyleSet->AppendStyleSheet(SheetType::Agent, cache->FormsSheet());
   mStyleSet->AppendStyleSheet(SheetType::Agent, cache->ScrollbarsSheet());
   mStyleSet->AppendStyleSheet(SheetType::Agent, cache->PluginProblemSheet());
 
   for (StyleSheet* sheet : *sheetService->AgentStyleSheets()) {
     mStyleSet->AppendStyleSheet(SheetType::Agent, sheet);
   }
+
+  MOZ_ASSERT(!mQuirkSheetAdded);
+  if (NeedsQuirksSheet()) {
+    mStyleSet->AppendStyleSheet(SheetType::Agent, cache->QuirkSheet());
+    mQuirkSheetAdded = true;
+  }
 }
 
 void Document::FillStyleSet() {
   MOZ_ASSERT(!mStyleSetFilled);
   FillStyleSetUserAndUASheets();
   FillStyleSetDocumentSheets();
   mStyleSetFilled = true;
 }
--- a/dom/base/nsDOMDataChannel.cpp
+++ b/dom/base/nsDOMDataChannel.cpp
@@ -114,17 +114,23 @@ nsresult nsDOMDataChannel::Init(nsPIDOMW
 void nsDOMDataChannel::GetLabel(nsAString& aLabel) {
   mDataChannel->GetLabel(aLabel);
 }
 
 void nsDOMDataChannel::GetProtocol(nsAString& aProtocol) {
   mDataChannel->GetProtocol(aProtocol);
 }
 
-uint16_t nsDOMDataChannel::Id() const { return mDataChannel->GetStream(); }
+mozilla::dom::Nullable<uint16_t> nsDOMDataChannel::GetId() const {
+  mozilla::dom::Nullable<uint16_t> result = mDataChannel->GetStream();
+  if (result.Value() == 65535) {
+    result.SetNull();
+  }
+  return result;
+}
 
 // XXX should be GetType()?  Open question for the spec
 bool nsDOMDataChannel::Reliable() const {
   return mDataChannel->GetType() == mozilla::DataChannelConnection::RELIABLE;
 }
 
 mozilla::dom::Nullable<uint16_t> nsDOMDataChannel::GetMaxPacketLifeTime()
     const {
--- a/dom/base/nsDOMDataChannel.h
+++ b/dom/base/nsDOMDataChannel.h
@@ -71,17 +71,17 @@ class nsDOMDataChannel final : public mo
   }
   void Send(const nsAString& aData, mozilla::ErrorResult& aRv);
   void Send(mozilla::dom::Blob& aData, mozilla::ErrorResult& aRv);
   void Send(const mozilla::dom::ArrayBuffer& aData, mozilla::ErrorResult& aRv);
   void Send(const mozilla::dom::ArrayBufferView& aData,
             mozilla::ErrorResult& aRv);
 
   bool Ordered() const;
-  uint16_t Id() const;
+  mozilla::dom::Nullable<uint16_t> GetId() const;
 
   nsresult DoOnMessageAvailable(const nsACString& aMessage, bool aBinary);
 
   virtual nsresult OnMessageAvailable(nsISupports* aContext,
                                       const nsACString& aMessage) override;
 
   virtual nsresult OnBinaryMessageAvailable(
       nsISupports* aContext, const nsACString& aMessage) override;
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -4193,21 +4193,23 @@ void HTMLMediaElement::ReportTelemetry()
   LOG(LogLevel::Debug, ("%p VIDEO_UNLOAD_STATE = %d", this, state));
 
   FrameStatisticsData data;
 
   if (HTMLVideoElement* vid = HTMLVideoElement::FromNodeOrNull(this)) {
     FrameStatistics* stats = vid->GetFrameStatistics();
     if (stats) {
       data = stats->GetFrameStatisticsData();
-      if (data.mParsedFrames) {
-        MOZ_ASSERT(data.mDroppedFrames <= data.mParsedFrames);
+      uint64_t parsedFrames = stats->GetParsedFrames();
+      if (parsedFrames) {
+        uint64_t droppedFrames = stats->GetDroppedFrames();
+        MOZ_ASSERT(droppedFrames <= parsedFrames);
         // Dropped frames <= total frames, so 'percentage' cannot be higher than
         // 100 and therefore can fit in a uint32_t (that Telemetry takes).
-        uint32_t percentage = 100 * data.mDroppedFrames / data.mParsedFrames;
+        uint32_t percentage = 100 * droppedFrames / parsedFrames;
         LOG(LogLevel::Debug,
             ("Reporting telemetry DROPPED_FRAMES_IN_VIDEO_PLAYBACK"));
         Telemetry::Accumulate(Telemetry::VIDEO_DROPPED_FRAMES_PROPORTION,
                               percentage);
       }
     }
   }
 
--- a/dom/html/HTMLVideoElement.cpp
+++ b/dom/html/HTMLVideoElement.cpp
@@ -308,32 +308,31 @@ HTMLVideoElement::GetVideoPlaybackQualit
 
     if (mDecoder) {
       if (nsContentUtils::ShouldResistFingerprinting(OwnerDoc())) {
         totalFrames = nsRFPService::GetSpoofedTotalFrames(TotalPlayTime());
         droppedFrames = nsRFPService::GetSpoofedDroppedFrames(
             TotalPlayTime(), VideoWidth(), VideoHeight());
         corruptedFrames = 0;
       } else {
-        FrameStatisticsData stats =
-            mDecoder->GetFrameStatistics().GetFrameStatisticsData();
-        if (sizeof(totalFrames) >= sizeof(stats.mParsedFrames)) {
-          totalFrames = stats.mPresentedFrames + stats.mDroppedFrames;
-          droppedFrames = stats.mDroppedFrames;
+        FrameStatistics* stats = &mDecoder->GetFrameStatistics();
+        if (sizeof(totalFrames) >= sizeof(stats->GetParsedFrames())) {
+          totalFrames = stats->GetTotalFrames();
+          droppedFrames = stats->GetDroppedFrames();
         } else {
-          uint64_t total = stats.mPresentedFrames + stats.mDroppedFrames;
+          uint64_t total = stats->GetTotalFrames();
           const auto maxNumber = std::numeric_limits<uint32_t>::max();
           if (total <= maxNumber) {
             totalFrames = uint32_t(total);
-            droppedFrames = uint32_t(stats.mDroppedFrames);
+            droppedFrames = uint32_t(stats->GetDroppedFrames());
           } else {
             // Too big number(s) -> Resize everything to fit in 32 bits.
             double ratio = double(maxNumber) / double(total);
             totalFrames = maxNumber;  // === total * ratio
-            droppedFrames = uint32_t(double(stats.mDroppedFrames) * ratio);
+            droppedFrames = uint32_t(double(stats->GetDroppedFrames()) * ratio);
           }
         }
         corruptedFrames = 0;
       }
     }
   }
 
   RefPtr<VideoPlaybackQuality> playbackQuality = new VideoPlaybackQuality(
--- a/dom/ipc/tests/mochitest.ini
+++ b/dom/ipc/tests/mochitest.ini
@@ -19,11 +19,11 @@ skip-if = !(crashreporter && !e10s && (t
 [test_temporaryfile_stream.html]
 skip-if = !e10s || toolkit == 'android' || (os == "win" && processor == "aarch64") # Bug 1525959, aarch64 due to 1531150
 support-files =
   blob_verify.sjs
   !/dom/canvas/test/captureStream_common.js
 [test_Preallocated.html]
 skip-if = !e10s || toolkit == 'android' # Bug 1525959
 [test_force_oop_iframe.html]
-skip-if = !e10s || webrender  # oop-iframes trigger a debug assertion in webrender picture caching
+skip-if = !e10s
 support-files =
   file_dummy.html
--- a/dom/media/CubebUtils.cpp
+++ b/dom/media/CubebUtils.cpp
@@ -142,16 +142,24 @@ bool sCubebDisableDeviceSwitching = true
 #ifdef MOZ_CUBEB_REMOTING
 bool sCubebSandbox = false;
 size_t sAudioIPCPoolSize;
 size_t sAudioIPCStackSize;
 #endif
 StaticAutoPtr<char> sBrandName;
 StaticAutoPtr<char> sCubebBackendName;
 StaticAutoPtr<char> sCubebOutputDeviceName;
+#ifdef MOZ_WIDGET_ANDROID
+// Counts the number of time a request for switching to global "communication
+// mode" has been received. If this is > 0, global communication mode is to be
+// enabled. If it is 0, the global communication mode is to be disabled.
+// This allows to correctly track the global behaviour to adopt accross
+// asynchronous GraphDriver changes, on Android.
+int sInCommunicationCount = 0;
+#endif
 
 const char kBrandBundleURL[] = "chrome://branding/locale/brand.properties";
 
 const char* AUDIOSTREAM_BACKEND_ID_STR[] = {
     "jack",  "pulse",       "alsa",  "audiounit", "audioqueue", "wasapi",
     "winmm", "directsound", "sndio", "opensl",    "audiotrack", "kai"};
 /* Index for failures to create an audio stream the first time. */
 const int CUBEB_BACKEND_INIT_FAILURE_FIRST =
@@ -304,16 +312,34 @@ cubeb* GetCubebContext() {
 
 // This is only exported when running tests.
 void ForceSetCubebContext(cubeb* aCubebContext) {
   StaticMutexAutoLock lock(sMutex);
   sCubebContext = aCubebContext;
   sCubebState = CubebState::Initialized;
 }
 
+void SetInCommunication(bool aInCommunication) {
+#ifdef MOZ_WIDGET_ANDROID
+  StaticMutexAutoLock lock(sMutex);
+  if (aInCommunication) {
+    sInCommunicationCount++;
+  } else {
+    MOZ_ASSERT(sInCommunicationCount > 0);
+    sInCommunicationCount--;
+  }
+
+  if (sInCommunicationCount == 1) {
+    java::GeckoAppShell::SetCommunicationAudioModeOn(true);
+  } else if (sInCommunicationCount == 0) {
+    java::GeckoAppShell::SetCommunicationAudioModeOn(false);
+  }
+#endif
+}
+
 bool InitPreferredSampleRate() {
   StaticMutexAutoLock lock(sMutex);
   if (sPreferredSampleRate != 0) {
     return true;
   }
 #ifdef MOZ_WIDGET_ANDROID
   sPreferredSampleRate = AndroidGetAudioOutputSampleRate();
 #else
--- a/dom/media/CubebUtils.h
+++ b/dom/media/CubebUtils.h
@@ -8,16 +8,18 @@
 #  define CubebUtils_h_
 
 #  include "cubeb/cubeb.h"
 #  include "nsString.h"
 #  include "mozilla/RefPtr.h"
 
 class AudioDeviceInfo;
 
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(cubeb_stream_prefs)
+
 namespace mozilla {
 namespace CubebUtils {
 
 typedef cubeb_devid AudioDeviceID;
 
 // Initialize Audio Library. Some Audio backends require initializing the
 // library before using it.
 void InitLibrary();
@@ -40,16 +42,20 @@ cubeb* GetCubebContext();
 void ReportCubebStreamInitFailure(bool aIsFirstStream);
 void ReportCubebBackendUsed();
 uint32_t GetCubebPlaybackLatencyInMilliseconds();
 uint32_t GetCubebMSGLatencyInFrames(cubeb_stream_params* params);
 bool CubebLatencyPrefSet();
 void GetCurrentBackend(nsAString& aBackend);
 cubeb_stream_prefs GetDefaultStreamPrefs();
 char* GetForcedOutputDevice();
+// No-op on all platforms but Android, where it tells the device's AudioManager
+// to switch to "communication mode", which might change audio routing,
+// bluetooth communication type, etc.
+void SetInCommunication(bool aInCommunication);
 
 #  ifdef MOZ_WIDGET_ANDROID
 uint32_t AndroidGetAudioOutputSampleRate();
 uint32_t AndroidGetAudioOutputFramesPerBuffer();
 #  endif
 
 #  ifdef ENABLE_SET_CUBEB_BACKEND
 void ForceSetCubebContext(cubeb* aCubebContext);
--- a/dom/media/FrameStatistics.h
+++ b/dom/media/FrameStatistics.h
@@ -15,47 +15,61 @@ struct FrameStatisticsData {
   // Number of frames parsed and demuxed from media.
   // Access protected by mReentrantMonitor.
   uint64_t mParsedFrames = 0;
 
   // Number of parsed frames which were actually decoded.
   // Access protected by mReentrantMonitor.
   uint64_t mDecodedFrames = 0;
 
+  // Number of parsed frames which were dropped in the decoder.
+  // Access protected by mReentrantMonitor.
+  uint64_t mDroppedDecodedFrames = 0;
+
+  // Number of decoded frames which were dropped in the sink
+  // Access protected by mReentrantMonitor.
+  uint64_t mDroppedSinkFrames = 0;
+
+  // Number of sinked frames which were dropped in the compositor
+  // Access protected by mReentrantMonitor.
+  uint64_t mDroppedCompositorFrames = 0;
+
   // Number of decoded frames which were actually sent down the rendering
   // pipeline to be painted ("presented"). Access protected by
   // mReentrantMonitor.
   uint64_t mPresentedFrames = 0;
 
-  // Number of frames that have been skipped because they have missed their
-  // composition deadline.
-  uint64_t mDroppedFrames = 0;
-
   // Sum of all inter-keyframe segment durations, in microseconds.
   // Dividing by count will give the average inter-keyframe time.
   uint64_t mInterKeyframeSum_us = 0;
   // Number of inter-keyframe segments summed so far.
   size_t mInterKeyframeCount = 0;
 
   // Maximum inter-keyframe segment duration, in microseconds.
   uint64_t mInterKeyFrameMax_us = 0;
 
   FrameStatisticsData() = default;
-  FrameStatisticsData(uint64_t aParsed, uint64_t aDecoded, uint64_t aDropped,
-                      uint64_t aPresented)
+  FrameStatisticsData(uint64_t aParsed, uint64_t aDecoded, uint64_t aPresented,
+                      uint64_t aDroppedDecodedFrames,
+                      uint64_t aDroppedSinkFrames,
+                      uint64_t aDroppedCompositorFrames)
       : mParsedFrames(aParsed),
         mDecodedFrames(aDecoded),
-        mPresentedFrames(aPresented),
-        mDroppedFrames(aDropped) {}
+        mDroppedDecodedFrames(aDroppedDecodedFrames),
+        mDroppedSinkFrames(aDroppedSinkFrames),
+        mDroppedCompositorFrames(aDroppedCompositorFrames),
+        mPresentedFrames(aPresented) {}
 
   void Accumulate(const FrameStatisticsData& aStats) {
     mParsedFrames += aStats.mParsedFrames;
     mDecodedFrames += aStats.mDecodedFrames;
     mPresentedFrames += aStats.mPresentedFrames;
-    mDroppedFrames += aStats.mDroppedFrames;
+    mDroppedDecodedFrames += aStats.mDroppedDecodedFrames;
+    mDroppedSinkFrames += aStats.mDroppedSinkFrames;
+    mDroppedCompositorFrames += aStats.mDroppedCompositorFrames;
     mInterKeyframeSum_us += aStats.mInterKeyframeSum_us;
     mInterKeyframeCount += aStats.mInterKeyframeCount;
     // It doesn't make sense to add max numbers, instead keep the bigger one.
     if (mInterKeyFrameMax_us < aStats.mInterKeyFrameMax_us) {
       mInterKeyFrameMax_us = aStats.mInterKeyFrameMax_us;
     }
   }
 };
@@ -92,21 +106,30 @@ class FrameStatistics {
   // Returns the number of decoded frames which have been sent to the rendering
   // pipeline for painting ("presented").
   // Can be called on any thread.
   uint64_t GetPresentedFrames() const {
     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     return mFrameStatisticsData.mPresentedFrames;
   }
 
+  // Returns the number of presented and dropped frames
+  // Can be called on any thread.
+  uint64_t GetTotalFrames() const {
+    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+    return mFrameStatisticsData.mPresentedFrames + GetDroppedFrames();
+  }
+
   // Returns the number of frames that have been skipped because they have
   // missed their composition deadline.
   uint64_t GetDroppedFrames() const {
     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
-    return mFrameStatisticsData.mDroppedFrames;
+    return mFrameStatisticsData.mDroppedDecodedFrames +
+           mFrameStatisticsData.mDroppedSinkFrames +
+           mFrameStatisticsData.mDroppedCompositorFrames;
   }
 
   // Increments the parsed and decoded frame counters by the passed in counts.
   // Can be called on any thread.
   void Accumulate(const FrameStatisticsData& aStats) {
     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     mFrameStatisticsData.Accumulate(aStats);
   }
--- a/dom/media/GraphDriver.cpp
+++ b/dom/media/GraphDriver.cpp
@@ -464,17 +464,18 @@ StreamAndPromiseForOperation::StreamAndP
     MediaStream* aStream, void* aPromise, dom::AudioContextOperation aOperation,
     dom::AudioContextOperationFlags aFlags)
     : mStream(aStream),
       mPromise(aPromise),
       mOperation(aOperation),
       mFlags(aFlags) {}
 
 AudioCallbackDriver::AudioCallbackDriver(MediaStreamGraphImpl* aGraphImpl,
-                                         uint32_t aInputChannelCount)
+                                         uint32_t aInputChannelCount,
+                                         AudioInputType aAudioInputType)
     : GraphDriver(aGraphImpl),
       mOutputChannels(0),
       mSampleRate(0),
       mInputChannelCount(aInputChannelCount),
       mIterationDurationMS(MEDIA_GRAPH_TARGET_PERIOD_MS),
       mStarted(false),
       mInitShutdownThread(
           SharedThreadPool::Get(NS_LITERAL_CSTRING("CubebOperation"), 1)),
@@ -489,26 +490,36 @@ AudioCallbackDriver::AudioCallbackDriver
   mInitShutdownThread->SetIdleThreadTimeout(
       PR_MillisecondsToInterval(kIdleThreadTimeoutMs));
 
 #if defined(XP_WIN)
   if (XRE_IsContentProcess()) {
     audio::AudioNotificationReceiver::Register(this);
   }
 #endif
+  if (aAudioInputType == AudioInputType::Voice) {
+    LOG(LogLevel::Debug, ("VOICE."));
+    mInputDevicePreference = CUBEB_DEVICE_PREF_VOICE;
+    CubebUtils::SetInCommunication(true);
+  } else {
+    mInputDevicePreference = CUBEB_DEVICE_PREF_ALL;
+  }
 }
 
 AudioCallbackDriver::~AudioCallbackDriver() {
   MOZ_ASSERT(mPromisesForOperation.IsEmpty());
   MOZ_ASSERT(!mAddedMixer);
 #if defined(XP_WIN)
   if (XRE_IsContentProcess()) {
     audio::AudioNotificationReceiver::Unregister(this);
   }
 #endif
+  if (mInputDevicePreference == CUBEB_DEVICE_PREF_VOICE) {
+    CubebUtils::SetInCommunication(false);
+  }
 }
 
 bool IsMacbookOrMacbookAir() {
 #ifdef XP_MACOSX
   size_t len = 0;
   sysctlbyname("hw.model", NULL, &len, NULL, 0);
   if (len) {
     UniquePtr<char[]> model(new char[len]);
@@ -584,16 +595,19 @@ bool AudioCallbackDriver::Init() {
 
   mBuffer = AudioCallbackBufferWrapper<AudioDataValue>(mOutputChannels);
   mScratchBuffer =
       SpillBuffer<AudioDataValue, WEBAUDIO_BLOCK_SIZE * 2>(mOutputChannels);
 
   output.channels = mOutputChannels;
   output.layout = CUBEB_LAYOUT_UNDEFINED;
   output.prefs = CubebUtils::GetDefaultStreamPrefs();
+  if (mInputDevicePreference == CUBEB_DEVICE_PREF_VOICE) {
+    output.prefs |= static_cast<cubeb_stream_prefs>(CUBEB_STREAM_PREF_VOICE);
+  }
 
   uint32_t latency_frames = CubebUtils::GetCubebMSGLatencyInFrames(&output);
 
   // Macbook and MacBook air don't have enough CPU to run very low latency
   // MediaStreamGraphs, cap the minimal latency to 512 frames int this case.
   if (IsMacbookOrMacbookAir()) {
     latency_frames = std::max((uint32_t)512, latency_frames);
   }
--- a/dom/media/GraphDriver.h
+++ b/dom/media/GraphDriver.h
@@ -318,16 +318,17 @@ struct StreamAndPromiseForOperation {
                                dom::AudioContextOperationFlags aFlags);
   RefPtr<MediaStream> mStream;
   void* mPromise;
   dom::AudioContextOperation mOperation;
   dom::AudioContextOperationFlags mFlags;
 };
 
 enum AsyncCubebOperation { INIT, SHUTDOWN };
+enum class AudioInputType { Unknown, Voice };
 
 /**
  * This is a graph driver that is based on callback functions called by the
  * audio api. This ensures minimal audio latency, because it means there is no
  * buffering happening: the audio is generated inside the callback.
  *
  * This design is less flexible than running our own thread:
  * - We have no control over the thread:
@@ -349,17 +350,18 @@ class AudioCallbackDriver : public Graph
 #if defined(XP_WIN)
     ,
                             public audio::DeviceChangeListener
 #endif
 {
  public:
   /** If aInputChannelCount is zero, then this driver is output-only. */
   AudioCallbackDriver(MediaStreamGraphImpl* aGraphImpl,
-                      uint32_t aInputChannelCount);
+                      uint32_t aInputChannelCount,
+                      AudioInputType aAudioInputType);
   virtual ~AudioCallbackDriver();
 
   void Start() override;
   void Revive() override;
   void WaitForNextIteration() override;
   void WakeUp() override;
   void Shutdown() override;
 #if defined(XP_WIN)
@@ -397,16 +399,23 @@ class AudioCallbackDriver : public Graph
 
   uint32_t OutputChannelCount() {
     MOZ_ASSERT(mOutputChannels != 0 && mOutputChannels <= 8);
     return mOutputChannels;
   }
 
   uint32_t InputChannelCount() { return mInputChannelCount; }
 
+  AudioInputType InputDevicePreference() {
+    if (mInputDevicePreference == CUBEB_DEVICE_PREF_VOICE) {
+      return AudioInputType::Voice;
+    }
+    return AudioInputType::Unknown;
+  }
+
   /* Enqueue a promise that is going to be resolved when a specific operation
    * occurs on the cubeb stream. */
   void EnqueueStreamAndPromiseForOperation(
       MediaStream* aStream, void* aPromise,
       dom::AudioContextOperation aOperation,
       dom::AudioContextOperationFlags aFlags);
 
   std::thread::id ThreadId() { return mAudioThreadId.load(); }
@@ -498,22 +507,22 @@ class AudioCallbackDriver : public Graph
     AudioCallbackDriver* mDriver;
   };
 
   /* Shared thread pool with up to one thread for off-main-thread
    * initialization and shutdown of the audio stream via AsyncCubebTask. */
   const RefPtr<SharedThreadPool> mInitShutdownThread;
   /* This must be accessed with the graph monitor held. */
   AutoTArray<StreamAndPromiseForOperation, 1> mPromisesForOperation;
+  cubeb_device_pref mInputDevicePreference;
   /* This is used to signal adding the mixer callback on first run
    * of audio callback. This is atomic because it is touched from different
    * threads, the audio callback thread and the state change thread. However,
    * the order of the threads does not allow concurent access. */
   Atomic<bool> mAddedMixer;
-
   /* Contains the id of the audio thread for as long as the callback
    * is taking place, after that it is reseted to an invalid value. */
   std::atomic<std::thread::id> mAudioThreadId;
   /* True when audio thread is running. False before
    * starting and after stopping it the audio thread. */
   Atomic<bool> mAudioThreadRunning;
   /* Indication of whether a fallback SystemClockDriver should be started if
    * StateCallback() receives an error.  No mutex need be held during access.
--- a/dom/media/MediaData.cpp
+++ b/dom/media/MediaData.cpp
@@ -409,25 +409,44 @@ already_AddRefed<VideoData> VideoData::C
   if (!v->mImage) {
     return nullptr;
   }
   if (!videoImage->Allocate(
           IntSize(aBuffer.mPlanes[0].mWidth, aBuffer.mPlanes[0].mHeight),
           SurfaceFormat::B8G8R8A8)) {
     return nullptr;
   }
-  uint8_t* argb_buffer = videoImage->GetBuffer();
-  IntSize size = videoImage->GetSize();
+
+  RefPtr<layers::TextureClient> texture =
+      videoImage->GetTextureClient(/* aForwarder */ nullptr);
+  if (!texture) {
+    NS_WARNING("Failed to allocate TextureClient");
+    return nullptr;
+  }
+
+  layers::TextureClientAutoLock autoLock(texture,
+                                         layers::OpenMode::OPEN_WRITE_ONLY);
+  if (!autoLock.Succeeded()) {
+    NS_WARNING("Failed to lock TextureClient");
+    return nullptr;
+  }
+
+  layers::MappedTextureData buffer;
+  if (!texture->BorrowMappedData(buffer)) {
+    NS_WARNING("Failed to borrow mapped data");
+    return nullptr;
+  }
 
   // The naming convention for libyuv and associated utils is word-order.
   // The naming convention in the gfx stack is byte-order.
   ConvertYCbCrAToARGB(aBuffer.mPlanes[0].mData, aBuffer.mPlanes[1].mData,
                       aBuffer.mPlanes[2].mData, aAlphaPlane.mData,
                       aBuffer.mPlanes[0].mStride, aBuffer.mPlanes[1].mStride,
-                      argb_buffer, size.width * 4, size.width, size.height);
+                      buffer.data, buffer.stride, buffer.size.width,
+                      buffer.size.height);
 
   return v.forget();
 }
 
 /* static */
 already_AddRefed<VideoData> VideoData::CreateFromImage(
     const IntSize& aDisplay, int64_t aOffset, const TimeUnit& aTime,
     const TimeUnit& aDuration, const RefPtr<Image>& aImage, bool aKeyframe,
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -2502,17 +2502,17 @@ void MediaFormatReader::DropDecodedSampl
     if (time >= decoder.mTimeThreshold.ref().Time()) {
       // We would have reached our internal seek target.
       decoder.mTimeThreshold.reset();
     }
   }
   decoder.mOutput.Clear();
   decoder.mSizeOfQueue -= lengthDecodedQueue;
   if (aTrack == TrackInfo::kVideoTrack && mFrameStats) {
-    mFrameStats->Accumulate({0, 0, lengthDecodedQueue, 0});
+    mFrameStats->Accumulate({0, 0, 0, lengthDecodedQueue, 0, 0});
   }
 }
 
 void MediaFormatReader::SkipVideoDemuxToNextKeyFrame(TimeUnit aTimeThreshold) {
   MOZ_ASSERT(OnTaskQueue());
   LOG("Skipping up to %" PRId64, aTimeThreshold.ToMicroseconds());
 
   // We've reached SkipVideoDemuxToNextKeyFrame when our decoding is late.
@@ -2531,25 +2531,26 @@ void MediaFormatReader::VideoSkipReset(u
   PROFILER_ADD_MARKER("SkippedVideoDecode", GRAPHICS);
   MOZ_ASSERT(OnTaskQueue());
 
   // Some frames may have been output by the decoder since we initiated the
   // videoskip process and we know they would be late.
   DropDecodedSamples(TrackInfo::kVideoTrack);
   // Report the pending frames as dropped.
   if (mFrameStats) {
-    mFrameStats->Accumulate({0, 0, SizeOfVideoQueueInFrames(), 0});
+    uint32_t droppedDecoderCount = SizeOfVideoQueueInFrames();
+    mFrameStats->Accumulate({0, 0, 0, droppedDecoderCount, 0, 0});
   }
 
   // Cancel any pending demux request and pending demuxed samples.
   mVideo.mDemuxRequest.DisconnectIfExists();
   Reset(TrackType::kVideoTrack);
 
   if (mFrameStats) {
-    mFrameStats->Accumulate({aSkipped, 0, aSkipped, 0});
+    mFrameStats->Accumulate({aSkipped, 0, 0, aSkipped, 0, 0});
   }
 
   mVideo.mNumSamplesSkippedTotal += aSkipped;
 }
 
 void MediaFormatReader::OnVideoSkipCompleted(uint32_t aSkipped) {
   MOZ_ASSERT(OnTaskQueue());
   LOG("Skipping succeeded, skipped %u frames", aSkipped);
@@ -3030,16 +3031,25 @@ void MediaFormatReader::GetMozDebugReade
                               : -1.0,
         mVideo.mTimeThreshold ? mVideo.mTimeThreshold.ref().mHasSeeked : -1,
         mVideo.mNumSamplesInput, mVideo.mNumSamplesOutput,
         unsigned(size_t(mVideo.mSizeOfQueue)),
         unsigned(mVideo.mOutput.Length()), mVideo.mWaitingForData,
         mVideo.mDemuxEOS, int32_t(mVideo.mDrainState), mVideo.mWaitingForKey,
         mVideo.mLastStreamSourceID);
   }
+
+  // Looking at dropped frames in details.
+  FrameStatisticsData stats = mFrameStats->GetFrameStatisticsData();
+  result +=
+      nsPrintfCString("Dropped Frames: reader=%" PRIu64 " sink=%" PRIu64
+                      " compositor=%" PRIu64 "\n",
+                      stats.mDroppedDecodedFrames, stats.mDroppedSinkFrames,
+                      stats.mDroppedCompositorFrames);
+
   aString += result;
 }
 
 void MediaFormatReader::SetVideoNullDecode(bool aIsNullDecode) {
   MOZ_ASSERT(OnTaskQueue());
   return SetNullDecode(TrackType::kVideoTrack, aIsNullDecode);
 }
 
--- a/dom/media/MediaStreamGraph.cpp
+++ b/dom/media/MediaStreamGraph.cpp
@@ -363,18 +363,18 @@ void MediaStreamGraphImpl::UpdateStreamO
     MonitorAutoLock mon(mMonitor);
     switching = CurrentDriver()->Switching();
   }
 
   if (audioTrackPresent && mRealtime &&
       !CurrentDriver()->AsAudioCallbackDriver() && !switching) {
     MonitorAutoLock mon(mMonitor);
     if (LifecycleStateRef() == LIFECYCLE_RUNNING) {
-      AudioCallbackDriver* driver =
-          new AudioCallbackDriver(this, AudioInputChannelCount());
+      AudioCallbackDriver* driver = new AudioCallbackDriver(
+          this, AudioInputChannelCount(), AudioInputDevicePreference());
       CurrentDriver()->SwitchAtNextIteration(driver);
     }
   }
 
   if (!mStreamOrderDirty) {
     return;
   }
 
@@ -607,18 +607,18 @@ void MediaStreamGraphImpl::CreateOrDestr
       {
         MonitorAutoLock lock(mMonitor);
         switching = CurrentDriver()->Switching();
       }
 
       if (!CurrentDriver()->AsAudioCallbackDriver() && !switching) {
         MonitorAutoLock mon(mMonitor);
         if (LifecycleStateRef() == LIFECYCLE_RUNNING) {
-          AudioCallbackDriver* driver =
-              new AudioCallbackDriver(this, AudioInputChannelCount());
+          AudioCallbackDriver* driver = new AudioCallbackDriver(
+              this, AudioInputChannelCount(), AudioInputDevicePreference());
           CurrentDriver()->SwitchAtNextIteration(driver);
         }
       }
     }
   }
 
   for (int32_t i = audioOutputStreamsFound.Length() - 1; i >= 0; --i) {
     if (!audioOutputStreamsFound[i]) {
@@ -741,18 +741,18 @@ void MediaStreamGraphImpl::OpenAudioInpu
 
   listeners.AppendElement(aListener);
 
   if (listeners.Length() == 1) {  // first open for this device
     mInputDeviceID = aID;
     // Switch Drivers since we're adding input (to input-only or full-duplex)
     MonitorAutoLock mon(mMonitor);
     if (LifecycleStateRef() == LIFECYCLE_RUNNING) {
-      AudioCallbackDriver* driver =
-          new AudioCallbackDriver(this, AudioInputChannelCount());
+      AudioCallbackDriver* driver = new AudioCallbackDriver(
+          this, AudioInputChannelCount(), AudioInputDevicePreference());
       LOG(LogLevel::Debug,
           ("%p OpenAudioInput: starting new AudioCallbackDriver(input) %p",
            this, driver));
       CurrentDriver()->SwitchAtNextIteration(driver);
     } else {
       LOG(LogLevel::Error, ("OpenAudioInput in shutdown!"));
       MOZ_ASSERT_UNREACHABLE("Can't open cubeb inputs in shutdown");
     }
@@ -825,17 +825,18 @@ void MediaStreamGraphImpl::CloseAudioInp
   MonitorAutoLock mon(mMonitor);
   if (LifecycleStateRef() == LIFECYCLE_RUNNING) {
     GraphDriver* driver;
     if (audioTrackPresent) {
       // We still have audio output
       LOG(LogLevel::Debug,
           ("%p: CloseInput: output present (AudioCallback)", this));
 
-      driver = new AudioCallbackDriver(this, AudioInputChannelCount());
+      driver = new AudioCallbackDriver(this, AudioInputChannelCount(),
+                                       AudioInputDevicePreference());
       CurrentDriver()->SwitchAtNextIteration(driver);
     } else if (CurrentDriver()->AsAudioCallbackDriver()) {
       LOG(LogLevel::Debug,
           ("%p: CloseInput: no output present (SystemClockCallback)", this));
 
       driver = new SystemClockDriver(this);
       CurrentDriver()->SwitchAtNextIteration(driver);
     }  // else SystemClockDriver->SystemClockDriver, no switch
@@ -976,30 +977,34 @@ void MediaStreamGraphImpl::ReevaluateInp
   bool needToSwitch = false;
 
   if (CurrentDriver()->AsAudioCallbackDriver()) {
     AudioCallbackDriver* audioCallbackDriver =
         CurrentDriver()->AsAudioCallbackDriver();
     if (audioCallbackDriver->InputChannelCount() != AudioInputChannelCount()) {
       needToSwitch = true;
     }
+    if (audioCallbackDriver->InputDevicePreference() !=
+        AudioInputDevicePreference()) {
+      needToSwitch = true;
+    }
   } else {
     // We're already in the process of switching to a audio callback driver,
     // which will happen at the next iteration.
     // However, maybe it's not the correct number of channels. Re-query the
     // correct channel amount at this time.
 #ifdef DEBUG
     MonitorAutoLock lock(mMonitor);
     MOZ_ASSERT(CurrentDriver()->Switching());
 #endif
     needToSwitch = true;
   }
   if (needToSwitch) {
-    AudioCallbackDriver* newDriver =
-        new AudioCallbackDriver(this, AudioInputChannelCount());
+    AudioCallbackDriver* newDriver = new AudioCallbackDriver(
+        this, AudioInputChannelCount(), AudioInputDevicePreference());
     {
       MonitorAutoLock lock(mMonitor);
       CurrentDriver()->SwitchAtNextIteration(newDriver);
     }
   }
 }
 
 bool MediaStreamGraphImpl::OnGraphThreadOrNotRunning() const {
@@ -3148,18 +3153,19 @@ MediaStreamGraphImpl::MediaStreamGraphIm
 #ifdef DEBUG
       ,
       mCanRunMessagesSynchronously(false)
 #endif
       ,
       mMainThreadGraphTime(0, "MediaStreamGraphImpl::mMainThreadGraphTime") {
   if (mRealtime) {
     if (aDriverRequested == AUDIO_THREAD_DRIVER) {
-      // Always start with zero input channels.
-      mDriver = new AudioCallbackDriver(this, 0);
+      // Always start with zero input channels, and no particular preferences
+      // for the input channel.
+      mDriver = new AudioCallbackDriver(this, 0, AudioInputType::Unknown);
     } else {
       mDriver = new SystemClockDriver(this);
     }
 
 #ifdef TRACING
     // This is a noop if the logger has not been enabled.
     gMSGTraceLogger.Start();
     gMSGTraceLogger.Log("[");
@@ -3640,17 +3646,18 @@ void MediaStreamGraphImpl::ApplyAudioCon
   // anyways, but doing this now save some time.
   if (aOperation == AudioContextOperation::Resume) {
     if (!CurrentDriver()->AsAudioCallbackDriver()) {
       AudioCallbackDriver* driver;
       if (switching) {
         MOZ_ASSERT(nextDriver->AsAudioCallbackDriver());
         driver = nextDriver->AsAudioCallbackDriver();
       } else {
-        driver = new AudioCallbackDriver(this, AudioInputChannelCount());
+        driver = new AudioCallbackDriver(this, AudioInputChannelCount(),
+                                         AudioInputDevicePreference());
         MonitorAutoLock lock(mMonitor);
         CurrentDriver()->SwitchAtNextIteration(driver);
       }
       driver->EnqueueStreamAndPromiseForOperation(aDestinationStream, aPromise,
                                                   aOperation, aFlags);
     } else {
       // We are resuming a context, but we are already using an
       // AudioCallbackDriver, we can resolve the promise now.
--- a/dom/media/MediaStreamGraph.h
+++ b/dom/media/MediaStreamGraph.h
@@ -118,16 +118,20 @@ class AudioDataListenerInterface {
                                TrackRate aRate, uint32_t aChannels) = 0;
 
   /**
    * Number of audio input channels.
    */
   virtual uint32_t RequestedInputChannelCount(MediaStreamGraphImpl* aGraph) = 0;
 
   /**
+   * Whether the underlying audio device is used for voice input.
+   */
+  virtual bool IsVoiceInput(MediaStreamGraphImpl* aGraph) const = 0;
+  /**
    * Called when the underlying audio device has changed.
    */
   virtual void DeviceChanged(MediaStreamGraphImpl* aGraph) = 0;
 
   /**
    * Called when the underlying audio device is being closed.
    */
   virtual void Disconnect(MediaStreamGraphImpl* aGraph) = 0;
--- a/dom/media/MediaStreamGraphImpl.h
+++ b/dom/media/MediaStreamGraphImpl.h
@@ -393,16 +393,17 @@ class MediaStreamGraphImpl : public Medi
    * anymore, for a particular stream. It can be that other streams still need
    * audio from this audio input device. */
   virtual void CloseAudioInput(Maybe<CubebUtils::AudioDeviceID>& aID,
                                AudioDataListener* aListener) override;
   /* Called on the graph thread when the input device settings should be
    * reevaluated, for example, if the channel count of the input stream should
    * be changed. */
   void ReevaluateInputDevice();
+
   /* Called on the graph thread when there is new output data for listeners.
    * This is the mixed audio output of this MediaStreamGraph. */
   void NotifyOutputData(AudioDataValue* aBuffer, size_t aFrames,
                         TrackRate aRate, uint32_t aChannels);
   /* Called on the graph thread when there is new input data for listeners. This
    * is the raw audio input for this MediaStreamGraph. */
   void NotifyInputData(const AudioDataValue* aBuffer, size_t aFrames,
                        TrackRate aRate, uint32_t aChannels);
@@ -480,16 +481,39 @@ class MediaStreamGraphImpl : public Medi
     MOZ_ASSERT(listeners);
     for (const auto& listener : *listeners) {
       maxInputChannels = std::max(maxInputChannels,
                                   listener->RequestedInputChannelCount(this));
     }
     return maxInputChannels;
   }
 
+  AudioInputType AudioInputDevicePreference() {
+    MOZ_ASSERT(OnGraphThreadOrNotRunning());
+
+    if (!mInputDeviceUsers.GetValue(mInputDeviceID)) {
+      return AudioInputType::Unknown;
+    }
+    bool voiceInput = false;
+    // When/if we decide to support multiple input device per graph, this needs
+    // loop over them.
+    nsTArray<RefPtr<AudioDataListener>>* listeners =
+        mInputDeviceUsers.GetValue(mInputDeviceID);
+    MOZ_ASSERT(listeners);
+
+    // If at least one stream is considered to be voice,
+    for (const auto& listener : *listeners) {
+      voiceInput |= listener->IsVoiceInput(this);
+    }
+    if (voiceInput) {
+      return AudioInputType::Voice;
+    }
+    return AudioInputType::Unknown;
+  }
+
   CubebUtils::AudioDeviceID InputDeviceID() { return mInputDeviceID; }
 
   double MediaTimeToSeconds(GraphTime aTime) const {
     NS_ASSERTION(aTime > -STREAM_TIME_MAX && aTime <= STREAM_TIME_MAX,
                  "Bad time");
     return static_cast<double>(aTime) / GraphRate();
   }
 
--- a/dom/media/PeerConnection.jsm
+++ b/dom/media/PeerConnection.jsm
@@ -1600,35 +1600,45 @@ class RTCPeerConnection {
     return this._chain(() => new Promise((resolve, reject) => {
       this._onGetStatsSuccess = resolve;
       this._onGetStatsFailure = reject;
       this._impl.getStats(selector);
     }));
   }
 
   createDataChannel(label, {
-                      maxRetransmits, ordered, negotiated, id = 0xFFFF,
+                      maxRetransmits, ordered, negotiated, id = null,
                       maxRetransmitTime, maxPacketLifeTime,
                       protocol,
                     } = {}) {
     this._checkClosed();
     this._pcTelemetry.recordDataChannelInit(maxRetransmitTime, maxPacketLifeTime);
 
     if (maxPacketLifeTime === undefined) {
       maxPacketLifeTime = maxRetransmitTime;
     }
 
     if (maxRetransmitTime !== undefined) {
       this.logWarning("Use maxPacketLifeTime instead of deprecated maxRetransmitTime which will stop working soon in createDataChannel!");
     }
+    if (!negotiated) {
+      id = null;
+    } else if (id === null) {
+      throw new this._win.DOMException(
+          "id is required when negotiated is true", "TypeError");
+    }
     if (maxPacketLifeTime !== undefined && maxRetransmits !== undefined) {
       throw new this._win.DOMException(
           "Both maxPacketLifeTime and maxRetransmits cannot be provided",
           "InvalidParameterError");
     }
+    if (id == 65535) {
+      throw new this._win.DOMException(
+          "id cannot be 65535", "TypeError");
+    }
     // Must determine the type where we still know if entries are undefined.
     let type;
     if (maxPacketLifeTime !== undefined) {
       type = Ci.IPeerConnection.kDataChannelPartialReliableTimed;
     } else if (maxRetransmits !== undefined) {
       type = Ci.IPeerConnection.kDataChannelPartialReliableRexmit;
     } else {
       type = Ci.IPeerConnection.kDataChannelReliable;
--- a/dom/media/mediasink/VideoSink.cpp
+++ b/dom/media/mediasink/VideoSink.cpp
@@ -512,54 +512,54 @@ void VideoSink::UpdateRenderedVideoFrame
   MOZ_ASSERT(mAudioSink->IsPlaying(), "should be called while playing.");
 
   // Get the current playback position.
   TimeStamp nowTime;
   const auto clockTime = mAudioSink->GetPosition(&nowTime);
   MOZ_ASSERT(!clockTime.IsNegative(), "Should have positive clock time.");
 
   uint32_t sentToCompositorCount = 0;
-  uint32_t droppedCount = 0;
+  uint32_t droppedInSink = 0;
 
   // Skip frames up to the playback position.
   TimeUnit lastFrameEndTime;
   while (VideoQueue().GetSize() > mMinVideoQueueSize &&
          clockTime >= VideoQueue().PeekFront()->GetEndTime()) {
     RefPtr<VideoData> frame = VideoQueue().PopFront();
     lastFrameEndTime = frame->GetEndTime();
     if (frame->IsSentToCompositor()) {
       sentToCompositorCount++;
     } else {
-      droppedCount++;
+      droppedInSink++;
       VSINK_LOG_V("discarding video frame mTime=%" PRId64
                   " clock_time=%" PRId64,
                   frame->mTime.ToMicroseconds(), clockTime.ToMicroseconds());
       VSINK_ADD_PROFILER_MARKER("VideoSink: discard", nowTime,
                                 clockTime.ToMicroseconds(),
                                 frame->mTime.ToMicroseconds());
     }
   }
 
-  if (droppedCount || sentToCompositorCount) {
+  if (droppedInSink || sentToCompositorCount) {
     uint32_t totalCompositorDroppedCount = mContainer->GetDroppedImageCount();
-    uint32_t compositorDroppedCount =
+    uint32_t droppedInCompositor =
         totalCompositorDroppedCount - mOldCompositorDroppedCount;
-    if (compositorDroppedCount > 0) {
+    if (droppedInCompositor > 0) {
       mOldCompositorDroppedCount = totalCompositorDroppedCount;
       VSINK_LOG_V("%u video frame previously discarded by compositor",
-                  compositorDroppedCount);
+                  droppedInCompositor);
     }
-    mPendingDroppedCount += compositorDroppedCount;
+    mPendingDroppedCount += droppedInCompositor;
     uint32_t droppedReported = mPendingDroppedCount > sentToCompositorCount
                                    ? sentToCompositorCount
                                    : mPendingDroppedCount;
     mPendingDroppedCount -= droppedReported;
 
-    mFrameStats.Accumulate({0, 0, droppedCount + droppedReported,
-                            sentToCompositorCount - droppedReported});
+    mFrameStats.Accumulate({0, 0, sentToCompositorCount - droppedReported, 0,
+                            droppedInSink, droppedInCompositor});
   }
 
   // The presentation end time of the last video frame displayed is either
   // the end time of the current frame, or if we dropped all frames in the
   // queue, the end time of the last frame we removed from the queue.
   RefPtr<VideoData> currentFrame = VideoQueue().PeekFront();
   mVideoFrameEndTime =
       std::max(mVideoFrameEndTime,
@@ -596,17 +596,17 @@ void VideoSink::MaybeResolveEndPromise()
   AssertOwnerThread();
   // All frames are rendered, Let's resolve the promise.
   if (VideoQueue().IsFinished() && VideoQueue().GetSize() <= 1 &&
       !mVideoSinkEndRequest.Exists()) {
     if (VideoQueue().GetSize() == 1) {
       // Remove the last frame since we have sent it to compositor.
       RefPtr<VideoData> frame = VideoQueue().PopFront();
       if (mPendingDroppedCount > 0) {
-        mFrameStats.Accumulate({0, 0, 1, 0});
+        mFrameStats.Accumulate({0, 0, 0, 0, 0, 1});
         mPendingDroppedCount--;
       } else {
         mFrameStats.NotifyPresentedFrame();
       }
     }
     mEndPromiseHolder.ResolveIfExists(true, __func__);
   }
 }
--- a/dom/media/test/marionette/yttest/ytpage.py
+++ b/dom/media/test/marionette/yttest/ytpage.py
@@ -34,16 +34,17 @@ SPLIT_FIELD = (
     "AudioSink",
     "MDSM",
     "Video State",
     "Video Track Buffer Details",
     "Dumping Audio Track",
     "Dumping Video Track",
     "MediaDecoder",
     "VideoSink",
+    "Dropped Frames"
 )
 
 
 class YoutubePage:
     def __init__(self, video_id, marionette, **options):
         self.video_id = video_id
         self.marionette = marionette
         self.url = "https://www.youtube.com/watch?v=%s" % self.video_id
--- a/dom/media/webaudio/test/mochitest.ini
+++ b/dom/media/webaudio/test/mochitest.ini
@@ -192,16 +192,17 @@ skip-if = toolkit == 'android' # bug 114
 [test_mediaStreamAudioSourceNode.html]
 [test_mediaStreamAudioSourceNodeCrossOrigin.html]
 tags=capturestream
 [test_mediaStreamAudioSourceNodeNoGC.html]
 [test_mediaStreamAudioSourceNodePassThrough.html]
 [test_mediaStreamAudioSourceNodeResampling.html]
 tags=capturestream
 [test_mediaStreamTrackAudioSourceNode.html]
+skip-if = !debug #bug 1543496
 [test_mediaStreamTrackAudioSourceNodeVideo.html]
 [test_mediaStreamTrackAudioSourceNodeCrossOrigin.html]
 [test_mixingRules.html]
 skip-if = toolkit == 'android' # bug 1091965
 [test_nodeToParamConnection.html]
 [test_nodeCreationDocumentGone.html]
 [test_notAllowedToStartAudioContextGC.html]
 [test_OfflineAudioContext.html]
--- a/dom/media/webrtc/MediaEngineWebRTC.cpp
+++ b/dom/media/webrtc/MediaEngineWebRTC.cpp
@@ -175,19 +175,24 @@ void MediaEngineWebRTC::EnumerateMicroph
       RefPtr<MediaDevice> device = MakeRefPtr<MediaDevice>(
           source, source->GetName(), NS_ConvertUTF8toUTF16(source->GetUUID()),
           source->GetGroupId(), NS_LITERAL_STRING(""));
       if (devices[i]->Preferred()) {
 #ifdef DEBUG
         if (!foundPreferredDevice) {
           foundPreferredDevice = true;
         } else {
+          // This is possible on windows, there is a default communication
+          // device, and a default device:
+          // See https://bugzilla.mozilla.org/show_bug.cgi?id=1542739
+#ifndef XP_WIN
           MOZ_ASSERT(!foundPreferredDevice,
                      "Found more than one preferred audio input device"
                      "while enumerating");
+#endif
         }
 #endif
         aDevices->InsertElementAt(0, device);
       } else {
         aDevices->AppendElement(device);
       }
     }
   }
--- a/dom/media/webrtc/MediaEngineWebRTCAudio.h
+++ b/dom/media/webrtc/MediaEngineWebRTCAudio.h
@@ -155,16 +155,22 @@ class AudioInputProcessing : public Audi
   void Pull(StreamTime aEndOfAppendedData, StreamTime aDesiredTime);
 
   void NotifyOutputData(MediaStreamGraphImpl* aGraph, AudioDataValue* aBuffer,
                         size_t aFrames, TrackRate aRate,
                         uint32_t aChannels) override;
   void NotifyInputData(MediaStreamGraphImpl* aGraph,
                        const AudioDataValue* aBuffer, size_t aFrames,
                        TrackRate aRate, uint32_t aChannels) override;
+  bool IsVoiceInput(MediaStreamGraphImpl* aGraph) const override {
+    // If we're passing data directly without AEC or any other process, this
+    // means that all voice-processing has been disabled intentionaly. In this
+    // case, consider that the device is not used for voice input.
+    return !PassThrough(aGraph);
+  }
 
   void Start();
   void Stop();
 
   void DeviceChanged(MediaStreamGraphImpl* aGraph) override;
 
   uint32_t RequestedInputChannelCount(MediaStreamGraphImpl* aGraph) override {
     return GetRequestedInputChannelCount(aGraph);
--- a/dom/webidl/RTCDataChannel.webidl
+++ b/dom/webidl/RTCDataChannel.webidl
@@ -12,19 +12,22 @@ enum RTCDataChannelState {
 enum RTCDataChannelType {
   "arraybuffer",
   "blob"
 };
 
 interface RTCDataChannel : EventTarget
 {
   readonly attribute DOMString label;
+  readonly attribute boolean ordered;
   readonly attribute boolean reliable;
   readonly attribute unsigned short? maxPacketLifeTime;
   readonly attribute unsigned short? maxRetransmits;
+  readonly attribute USVString protocol;
+  readonly attribute unsigned short? id;
   readonly attribute RTCDataChannelState readyState;
   readonly attribute unsigned long bufferedAmount;
   attribute unsigned long bufferedAmountLowThreshold;
   attribute EventHandler onopen;
   attribute EventHandler onerror;
   attribute EventHandler onclose;
   void close();
   attribute EventHandler onmessage;
@@ -34,16 +37,8 @@ interface RTCDataChannel : EventTarget
   void send(DOMString data);
   [Throws]
   void send(Blob data);
   [Throws]
   void send(ArrayBuffer data);
   [Throws]
   void send(ArrayBufferView data);
 };
-
-// Mozilla extensions.
-partial interface RTCDataChannel
-{
-  readonly attribute DOMString protocol;
-  readonly attribute boolean ordered;
-  readonly attribute unsigned short id;
-};
--- a/dom/webidl/RTCPeerConnection.webidl
+++ b/dom/webidl/RTCPeerConnection.webidl
@@ -45,20 +45,23 @@ enum mozPacketDumpType {
 
 callback mozPacketCallback = void (unsigned long level,
                                    mozPacketDumpType type,
                                    boolean sending,
                                    ArrayBuffer packet);
 
 dictionary RTCDataChannelInit {
   boolean        ordered = true;
+  [EnforceRange]
   unsigned short maxPacketLifeTime;
+  [EnforceRange]
   unsigned short maxRetransmits;
   DOMString      protocol = "";
   boolean        negotiated = false;
+  [EnforceRange]
   unsigned short id;
 
   // These are deprecated due to renaming in the spec, but still supported for Fx53
   unsigned short maxRetransmitTime;
 };
 
 dictionary RTCOfferAnswerOptions {
 //  boolean voiceActivityDetection = true; // TODO: support this (Bug 1184712)
--- a/gfx/layers/ImageContainer.h
+++ b/gfx/layers/ImageContainer.h
@@ -212,18 +212,16 @@ class Image {
   }
 
   int32_t GetSerial() const { return mSerial; }
 
   virtual already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() = 0;
 
   virtual bool IsValid() const { return true; }
 
-  virtual uint8_t* GetBuffer() const { return nullptr; }
-
   /**
    * For use with the TextureForwarder only (so that the later can
    * synchronize the TextureClient with the TextureHost).
    */
   virtual TextureClient* GetTextureClient(KnowsCompositor* aForwarder) {
     return nullptr;
   }
 
--- a/gfx/layers/ipc/SharedPlanarYCbCrImage.cpp
+++ b/gfx/layers/ipc/SharedPlanarYCbCrImage.cpp
@@ -46,22 +46,16 @@ size_t SharedPlanarYCbCrImage::SizeOfExc
   return 0;
 }
 
 TextureClient* SharedPlanarYCbCrImage::GetTextureClient(
     KnowsCompositor* aForwarder) {
   return mTextureClient.get();
 }
 
-uint8_t* SharedPlanarYCbCrImage::GetBuffer() const {
-  // This should never be used
-  MOZ_ASSERT(false);
-  return nullptr;
-}
-
 already_AddRefed<gfx::SourceSurface>
 SharedPlanarYCbCrImage::GetAsSourceSurface() {
   if (!IsValid()) {
     NS_WARNING("Can't get as surface");
     return nullptr;
   }
   return PlanarYCbCrImage::GetAsSourceSurface();
 }
@@ -85,48 +79,18 @@ bool SharedPlanarYCbCrImage::CopyData(co
     MOZ_ASSERT(false, "Failed to copy YCbCr data into the TextureClient");
     return false;
   }
   mTextureClient->MarkImmutable();
   return true;
 }
 
 bool SharedPlanarYCbCrImage::AdoptData(const Data& aData) {
-  MOZ_ASSERT(mTextureClient, "This Image should have already allocated data");
-  if (!mTextureClient) {
-    return false;
-  }
-  mData = aData;
-  mSize = aData.mPicSize;
-  mOrigin = gfx::IntPoint(aData.mPicX, aData.mPicY);
-
-  uint8_t* base = GetBuffer();
-  uint32_t yOffset = aData.mYChannel - base;
-  uint32_t cbOffset = aData.mCbChannel - base;
-  uint32_t crOffset = aData.mCrChannel - base;
-
-  auto fwd = mCompositable->GetForwarder();
-  bool supportsTextureDirectMapping =
-      fwd->SupportsTextureDirectMapping() &&
-      std::max(
-          aData.mYSize.width,
-          std::max(aData.mYSize.height,
-                   std::max(aData.mCbCrSize.width, aData.mCbCrSize.height))) <=
-          fwd->GetMaxTextureSize();
-  bool hasIntermediateBuffer = ComputeHasIntermediateBuffer(
-      gfx::SurfaceFormat::YUV, fwd->GetCompositorBackendType(),
-      supportsTextureDirectMapping);
-
-  static_cast<BufferTextureData*>(mTextureClient->GetInternalData())
-      ->SetDescriptor(YCbCrDescriptor(
-          aData.mYSize, aData.mYStride, aData.mCbCrSize, aData.mCbCrStride,
-          yOffset, cbOffset, crOffset, aData.mStereoMode, aData.mColorDepth,
-          aData.mYUVColorSpace, hasIntermediateBuffer));
-
-  return true;
+  MOZ_ASSERT(false, "This shouldn't be used.");
+  return false;
 }
 
 bool SharedPlanarYCbCrImage::IsValid() const {
   return mTextureClient && mTextureClient->IsValid();
 }
 
 bool SharedPlanarYCbCrImage::Allocate(PlanarYCbCrData& aData) {
   MOZ_ASSERT(!mTextureClient, "This image already has allocated data");
--- a/gfx/layers/ipc/SharedPlanarYCbCrImage.h
+++ b/gfx/layers/ipc/SharedPlanarYCbCrImage.h
@@ -26,17 +26,16 @@ class SharedPlanarYCbCrImage : public Pl
  public:
   explicit SharedPlanarYCbCrImage(ImageClient* aCompositable);
 
  protected:
   virtual ~SharedPlanarYCbCrImage();
 
  public:
   TextureClient* GetTextureClient(KnowsCompositor* aForwarder) override;
-  uint8_t* GetBuffer() const override;
 
   already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() override;
   bool CopyData(const PlanarYCbCrData& aData) override;
   bool AdoptData(const Data& aData) override;
 
   bool Allocate(PlanarYCbCrData& aData);
 
   bool IsValid() const override;
--- a/gfx/layers/ipc/SharedRGBImage.cpp
+++ b/gfx/layers/ipc/SharedRGBImage.cpp
@@ -97,24 +97,16 @@ bool SharedRGBImage::Allocate(gfx::IntSi
         aFormat, aSize, mCompositable->GetTextureFlags());
     mTextureClient =
         mCompositable->GetTextureClientRecycler()->CreateOrRecycle(helper);
   }
 
   return !!mTextureClient;
 }
 
-uint8_t* SharedRGBImage::GetBuffer() const {
-  MappedTextureData mapped;
-  if (mTextureClient && mTextureClient->BorrowMappedData(mapped)) {
-    return mapped.data;
-  }
-  return 0;
-}
-
 gfx::IntSize SharedRGBImage::GetSize() const { return mSize; }
 
 TextureClient* SharedRGBImage::GetTextureClient(KnowsCompositor* aForwarder) {
   return mTextureClient.get();
 }
 
 static void ReleaseTextureClient(void* aData) {
   RELEASE_MANUALLY(static_cast<TextureClient*>(aData));
--- a/gfx/layers/ipc/SharedRGBImage.h
+++ b/gfx/layers/ipc/SharedRGBImage.h
@@ -36,18 +36,16 @@ class SharedRGBImage : public Image {
   explicit SharedRGBImage(ImageClient* aCompositable);
 
  protected:
   virtual ~SharedRGBImage();
 
  public:
   TextureClient* GetTextureClient(KnowsCompositor* aForwarder) override;
 
-  uint8_t* GetBuffer() const override;
-
   gfx::IntSize GetSize() const override;
 
   already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() override;
 
   bool Allocate(gfx::IntSize aSize, gfx::SurfaceFormat aFormat);
 
  private:
   gfx::IntSize mSize;
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -7020,18 +7020,18 @@ bool BytecodeEmitter::emitSelfHostedResu
 
   ParseNode* valNode = genNode->pn_next;
   if (!emitTree(valNode)) {
     return false;
   }
 
   ParseNode* kindNode = valNode->pn_next;
   MOZ_ASSERT(kindNode->isKind(ParseNodeKind::StringExpr));
-  uint8_t operand = AbstractGeneratorObject::getResumeKind(
-      cx, kindNode->as<NameNode>().atom());
+  uint8_t operand = uint8_t(AbstractGeneratorObject::getResumeKind(
+      cx, kindNode->as<NameNode>().atom()));
   MOZ_ASSERT(!kindNode->pn_next);
 
   if (!emit2(JSOP_RESUME, operand)) {
     return false;
   }
 
   return true;
 }
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -5738,17 +5738,17 @@ bool BaselineCompilerCodeGen::emit_JSOP_
     regs.add(initLength);
   }
 
   masm.bind(&noExprStack);
   masm.pushValue(retVal);
 
   masm.switchToObjectRealm(genObj, scratch2);
 
-  if (resumeKind == AbstractGeneratorObject::NEXT) {
+  if (resumeKind == GeneratorResumeKind::Next) {
     // Determine the resume address based on the resumeIndex and the
     // resumeIndex -> native table in the BaselineScript.
     masm.load32(
         Address(scratch1, BaselineScript::offsetOfResumeEntriesOffset()),
         scratch2);
     masm.addPtr(scratch2, scratch1);
     masm.unboxInt32(
         Address(genObj, AbstractGeneratorObject::offsetOfResumeIndexSlot()),
@@ -5758,30 +5758,30 @@ bool BaselineCompilerCodeGen::emit_JSOP_
         scratch1);
 
     // Mark as running and jump to the generator's JIT code.
     masm.storeValue(
         Int32Value(AbstractGeneratorObject::RESUME_INDEX_RUNNING),
         Address(genObj, AbstractGeneratorObject::offsetOfResumeIndexSlot()));
     masm.jump(scratch1);
   } else {
-    MOZ_ASSERT(resumeKind == AbstractGeneratorObject::THROW ||
-               resumeKind == AbstractGeneratorObject::RETURN);
+    MOZ_ASSERT(resumeKind == GeneratorResumeKind::Throw ||
+               resumeKind == GeneratorResumeKind::Return);
 
     // Update the frame's frameSize field.
     masm.computeEffectiveAddress(
         Address(BaselineFrameReg, BaselineFrame::FramePointerOffset), scratch2);
     masm.movePtr(scratch2, scratch1);
     masm.subStackPtrFrom(scratch2);
     masm.store32(scratch2, Address(BaselineFrameReg,
                                    BaselineFrame::reverseOffsetOfFrameSize()));
     masm.loadBaselineFramePtr(BaselineFrameReg, scratch2);
 
     prepareVMCall();
-    pushArg(Imm32(resumeKind));
+    pushArg(Imm32(int32_t(resumeKind)));
     pushArg(retVal);
     pushArg(genObj);
     pushArg(scratch2);
 
     using Fn =
         bool (*)(JSContext*, BaselineFrame*, Handle<AbstractGeneratorObject*>,
                  HandleValue, uint32_t);
     TailCallVMFunctionId id =
@@ -5816,22 +5816,22 @@ bool BaselineCompilerCodeGen::emit_JSOP_
     // framePushed.
     masm.implicitPop((fun.explicitStackSlots() + 1) * sizeof(void*));
   }
 
   // If the generator script has no JIT code, call into the VM.
   masm.bind(&interpret);
 
   prepareVMCall();
-  if (resumeKind == AbstractGeneratorObject::NEXT) {
+  if (resumeKind == GeneratorResumeKind::Next) {
     pushArg(ImmGCPtr(cx->names().next));
-  } else if (resumeKind == AbstractGeneratorObject::THROW) {
+  } else if (resumeKind == GeneratorResumeKind::Throw) {
     pushArg(ImmGCPtr(cx->names().throw_));
   } else {
-    MOZ_ASSERT(resumeKind == AbstractGeneratorObject::RETURN);
+    MOZ_ASSERT(resumeKind == GeneratorResumeKind::Return);
     pushArg(ImmGCPtr(cx->names().return_));
   }
 
   masm.loadValue(frame.addressOfStackValue(-1), retVal);
   pushArg(retVal);
   pushArg(genObj);
 
   using Fn = bool (*)(JSContext*, HandleObject, HandleValue, HandlePropertyName,
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -12050,42 +12050,47 @@ class OutOfLineSwitch : public OutOfLine
   void setOutOfLine() { isOutOfLine_ = true; }
 };
 
 template <SwitchTableType tableType>
 void CodeGenerator::visitOutOfLineSwitch(
     OutOfLineSwitch<tableType>* jumpTable) {
   jumpTable->setOutOfLine();
   auto& labels = jumpTable->labels();
-#if defined(JS_CODEGEN_ARM64)
-  AutoForbidPools afp(
-      &masm, (labels.length() + 1) * (sizeof(void*) / vixl::kInstructionSize));
-#endif
 
   if (tableType == SwitchTableType::OutOfLine) {
 #if defined(JS_CODEGEN_ARM)
     MOZ_CRASH("NYI: SwitchTableType::OutOfLine");
 #elif defined(JS_CODEGEN_NONE)
     MOZ_CRASH();
 #else
+
+#  if defined(JS_CODEGEN_ARM64)
+    AutoForbidPoolsAndNops afp(
+        &masm,
+        (labels.length() + 1) * (sizeof(void*) / vixl::kInstructionSize));
+#  endif
+
     masm.haltingAlign(sizeof(void*));
+
+    // Bind the address of the jump table and reserve the space for code
+    // pointers to jump in the newly generated code.
     masm.bind(jumpTable->start());
     masm.addCodeLabel(*jumpTable->start());
-#endif
-  }
-
-  // Add table entries if the table is inlined.
-  for (size_t i = 0, e = labels.length(); i < e; i++) {
-    jumpTable->addTableEntry(masm);
-  }
-
+    for (size_t i = 0, e = labels.length(); i < e; i++) {
+      jumpTable->addTableEntry(masm);
+    }
+#endif
+  }
+
+  // Register all reserved pointers of the jump table to target labels. The
+  // entries of the jump table need to be absolute addresses and thus must be
+  // patched after codegen is finished.
   auto& codeLabels = jumpTable->codeLabels();
   for (size_t i = 0, e = codeLabels.length(); i < e; i++) {
-    // The entries of the jump table need to be absolute addresses and thus
-    // must be patched after codegen is finished.
     auto& cl = codeLabels[i];
     cl.target()->bind(labels[i].offset());
     masm.addCodeLabel(cl);
   }
 }
 
 template void CodeGenerator::visitOutOfLineSwitch(
     OutOfLineSwitch<SwitchTableType::Inline>* jumpTable);
@@ -12114,17 +12119,17 @@ void CodeGenerator::visitLoadElementFrom
       new (alloc()) OutOfLineSwitch<SwitchTableType::OutOfLine>(alloc());
 #endif
 
   {
 #if defined(JS_CODEGEN_ARM)
     // Inhibit pools within the following sequence because we are indexing into
     // a pc relative table. The region will have one instruction for ma_ldr, one
     // for breakpoint, and each table case takes one word.
-    AutoForbidPools afp(&masm, 1 + 1 + array->numElements());
+    AutoForbidPoolsAndNops afp(&masm, 1 + 1 + array->numElements());
 #endif
     jumpTable->jumpToCodeEntries(masm, index, temp0);
 
     // Add table entries if the table is inlined.
     for (size_t i = 0, e = array->numElements(); i < e; i++) {
       jumpTable->addTableEntry(masm);
     }
   }
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -1018,35 +1018,37 @@ bool DebugAfterYield(JSContext* cx, Base
   }
 
   *mustReturn = false;
   return true;
 }
 
 bool GeneratorThrowOrReturn(JSContext* cx, BaselineFrame* frame,
                             Handle<AbstractGeneratorObject*> genObj,
-                            HandleValue arg, uint32_t resumeKind) {
+                            HandleValue arg, uint32_t resumeKindArg) {
   // Set the frame's pc to the current resume pc, so that frame iterators
   // work. This function always returns false, so we're guaranteed to enter
   // the exception handler where we will clear the pc.
   JSScript* script = frame->script();
   uint32_t offset = script->resumeOffsets()[genObj->resumeIndex()];
   jsbytecode* pc = script->offsetToPC(offset);
   frame->setOverridePc(pc);
 
   // In the interpreter, AbstractGeneratorObject::resume marks the generator as
   // running, so we do the same.
   genObj->setRunning();
 
   bool mustReturn = false;
   if (!DebugAfterYield(cx, frame, pc, &mustReturn)) {
     return false;
   }
+
+  GeneratorResumeKind resumeKind = GeneratorResumeKind(resumeKindArg);
   if (mustReturn) {
-    resumeKind = AbstractGeneratorObject::RETURN;
+    resumeKind = GeneratorResumeKind::Return;
   }
 
   MOZ_ALWAYS_FALSE(
       js::GeneratorThrowOrReturn(cx, frame, genObj, arg, resumeKind));
   return false;
 }
 
 bool GlobalNameConflictsCheckFromIon(JSContext* cx, HandleScript script) {
--- a/js/src/jit/VMFunctions.h
+++ b/js/src/jit/VMFunctions.h
@@ -939,17 +939,17 @@ MOZ_MUST_USE bool FinalSuspend(JSContext
 MOZ_MUST_USE bool InterpretResume(JSContext* cx, HandleObject obj,
                                   HandleValue val, HandlePropertyName kind,
                                   MutableHandleValue rval);
 MOZ_MUST_USE bool DebugAfterYield(JSContext* cx, BaselineFrame* frame,
                                   jsbytecode* pc, bool* mustReturn);
 MOZ_MUST_USE bool GeneratorThrowOrReturn(
     JSContext* cx, BaselineFrame* frame,
     Handle<AbstractGeneratorObject*> genObj, HandleValue arg,
-    uint32_t resumeKind);
+    uint32_t resumeKindArg);
 
 MOZ_MUST_USE bool GlobalNameConflictsCheckFromIon(JSContext* cx,
                                                   HandleScript script);
 MOZ_MUST_USE bool InitFunctionEnvironmentObjects(JSContext* cx,
                                                  BaselineFrame* frame);
 
 MOZ_MUST_USE bool NewArgumentsObject(JSContext* cx, BaselineFrame* frame,
                                      MutableHandleValue res);
--- a/js/src/jit/arm/Assembler-arm.cpp
+++ b/js/src/jit/arm/Assembler-arm.cpp
@@ -765,16 +765,17 @@ void Assembler::copyDataRelocationTable(
 
 void Assembler::processCodeLabels(uint8_t* rawCode) {
   for (const CodeLabel& label : codeLabels_) {
     Bind(rawCode, label);
   }
 }
 
 void Assembler::writeCodePointer(CodeLabel* label) {
+  m_buffer.assertNoPoolAndNoNops();
   BufferOffset off = writeInst(-1);
   label->patchAt()->bind(off.getOffset());
 }
 
 void Assembler::Bind(uint8_t* rawCode, const CodeLabel& label) {
   size_t offset = label.patchAt().offset();
   size_t target = label.target().offset();
   *reinterpret_cast<const void**>(rawCode + offset) = rawCode + target;
--- a/js/src/jit/arm/Assembler-arm.h
+++ b/js/src/jit/arm/Assembler-arm.h
@@ -2295,41 +2295,40 @@ class DoubleEncoder {
         *ret = table[i].data;
         return true;
       }
     }
     return false;
   }
 };
 
-class AutoForbidPools {
-  Assembler* masm_;
-
- public:
-  // The maxInst argument is the maximum number of word sized instructions
-  // that will be allocated within this context. It is used to determine if
-  // the pool needs to be dumped before entering this content. The debug code
-  // checks that no more than maxInst instructions are actually allocated.
-  //
-  // Allocation of pool entries is not supported within this content so the
-  // code can not use large integers or float constants etc.
-  AutoForbidPools(Assembler* masm, size_t maxInst) : masm_(masm) {
-    masm_->enterNoPool(maxInst);
-  }
-
-  ~AutoForbidPools() { masm_->leaveNoPool(); }
-};
-
 // Forbids nop filling for testing purposes. Not nestable.
 class AutoForbidNops {
+ protected:
   Assembler* masm_;
 
  public:
   explicit AutoForbidNops(Assembler* masm) : masm_(masm) {
     masm_->enterNoNops();
   }
   ~AutoForbidNops() { masm_->leaveNoNops(); }
 };
 
+class AutoForbidPoolsAndNops : public AutoForbidNops {
+ public:
+  // The maxInst argument is the maximum number of word sized instructions
+  // that will be allocated within this context. It is used to determine if
+  // the pool needs to be dumped before entering this content. The debug code
+  // checks that no more than maxInst instructions are actually allocated.
+  //
+  // Allocation of pool entries is not supported within this content so the
+  // code can not use large integers or float constants etc.
+  AutoForbidPoolsAndNops(Assembler* masm, size_t maxInst) : AutoForbidNops(masm) {
+    masm_->enterNoPool(maxInst);
+  }
+
+  ~AutoForbidPoolsAndNops() { masm_->leaveNoPool(); }
+};
+
 }  // namespace jit
 }  // namespace js
 
 #endif /* jit_arm_Assembler_arm_h */
--- a/js/src/jit/arm/CodeGenerator-arm.cpp
+++ b/js/src/jit/arm/CodeGenerator-arm.cpp
@@ -1143,17 +1143,17 @@ void CodeGeneratorARM::emitTableSwitchDi
   int32_t cases = mir->numCases();
   // Lower value with low value.
   masm.ma_sub(index, Imm32(mir->low()), index, scratch, SetCC);
   masm.ma_rsb(index, Imm32(cases - 1), index, scratch, SetCC,
               Assembler::NotSigned);
   // Inhibit pools within the following sequence because we are indexing into
   // a pc relative table. The region will have one instruction for ma_ldr, one
   // for ma_b, and each table case takes one word.
-  AutoForbidPools afp(&masm, 1 + 1 + cases);
+  AutoForbidPoolsAndNops afp(&masm, 1 + 1 + cases);
   masm.ma_ldr(DTRAddr(pc, DtrRegImmShift(index, LSL, 2)), pc, Offset,
               Assembler::NotSigned);
   masm.ma_b(defaultcase);
 
   // To fill in the CodeLabels for the case entries, we need to first generate
   // the case entries (we don't yet know their offsets in the instruction
   // stream).
   OutOfLineTableSwitch* ool = new (alloc()) OutOfLineTableSwitch(alloc(), mir);
--- a/js/src/jit/arm/MacroAssembler-arm.cpp
+++ b/js/src/jit/arm/MacroAssembler-arm.cpp
@@ -4256,17 +4256,17 @@ CodeOffset MacroAssembler::farJumpWithPa
   // The goal of the thunk is to be able to jump to any address without the
   // usual 32MiB branch range limitation. Additionally, to make the thunk
   // simple to use, the thunk does not use the constant pool or require
   // patching an absolute address. Instead, a relative offset is used which
   // can be patched during compilation.
 
   // Inhibit pools since these three words must be contiguous so that the offset
   // calculations below are valid.
-  AutoForbidPools afp(this, 3);
+  AutoForbidPoolsAndNops afp(this, 3);
 
   // When pc is used, the read value is the address of the instruction + 8.
   // This is exactly the address of the uint32 word we want to load.
   ScratchRegisterScope scratch(*this);
   ma_ldr(DTRAddr(pc, DtrOffImm(0)), scratch);
 
   // Branch by making pc the destination register.
   ma_add(pc, scratch, pc, LeaveCC, Always);
@@ -4287,17 +4287,17 @@ void MacroAssembler::patchFarJump(CodeOf
   MOZ_ASSERT(editSrc(BufferOffset(addOffset))->is<InstALU>());
 
   // When pc is read as the operand of the add, its value is the address of
   // the add instruction + 8.
   *u32 = (targetOffset - addOffset) - 8;
 }
 
 CodeOffset MacroAssembler::nopPatchableToCall(const wasm::CallSiteDesc& desc) {
-  AutoForbidPools afp(this, /* max number of instructions in scope = */ 1);
+  AutoForbidPoolsAndNops afp(this, /* max number of instructions in scope = */ 1);
   CodeOffset offset(currentOffset());
   ma_nop();
   append(desc, CodeOffset(currentOffset()));
   return offset;
 }
 
 void MacroAssembler::patchNopToCall(uint8_t* call, uint8_t* target) {
   uint8_t* inst = call - 4;
--- a/js/src/jit/arm/Trampoline-arm.cpp
+++ b/js/src/jit/arm/Trampoline-arm.cpp
@@ -231,17 +231,17 @@ void JitRuntime::generateEnterJIT(JSCont
     masm.load32(slot_numStackValues, numStackValues);
 
     // Write return address. On ARM, CodeLabel is only used for tableswitch,
     // so we can't use it here to get the return address. Instead, we use pc
     // + a fixed offset to a jump to returnLabel. The pc register holds pc +
     // 8, so we add the size of 2 instructions to skip the instructions
     // emitted by storePtr and jump(&skipJump).
     {
-      AutoForbidPools afp(&masm, 5);
+      AutoForbidPoolsAndNops afp(&masm, 5);
       Label skipJump;
       masm.mov(pc, scratch);
       masm.addPtr(Imm32(2 * sizeof(uint32_t)), scratch);
       masm.storePtr(scratch, Address(sp, 0));
       masm.jump(&skipJump);
       masm.jump(&returnLabel);
       masm.bind(&skipJump);
     }
@@ -680,17 +680,17 @@ static void GenerateBailoutThunk(MacroAs
 JitRuntime::BailoutTable JitRuntime::generateBailoutTable(MacroAssembler& masm,
                                                           Label* bailoutTail,
                                                           uint32_t frameClass) {
   uint32_t offset = startTrampolineCode(masm);
 
   {
     // Emit the table without any pools being inserted.
     Label bailout;
-    AutoForbidPools afp(&masm, BAILOUT_TABLE_SIZE);
+    AutoForbidPoolsAndNops afp(&masm, BAILOUT_TABLE_SIZE);
     for (size_t i = 0; i < BAILOUT_TABLE_SIZE; i++) {
       masm.ma_bl(&bailout);
     }
     masm.bind(&bailout);
   }
 
   GenerateBailoutThunk(masm, frameClass, bailoutTail);
 
--- a/js/src/jit/arm64/Assembler-arm64.h
+++ b/js/src/jit/arm64/Assembler-arm64.h
@@ -375,16 +375,17 @@ class Assembler : public vixl::Assembler
     Instruction* getLdr() { return reinterpret_cast<Instruction*>(&ldr); }
   };
 
   // Offset of the patchable target for the given entry.
   static const size_t OffsetOfJumpTableEntryPointer = 8;
 
  public:
   void writeCodePointer(CodeLabel* label) {
+    armbuffer_.assertNoPoolAndNoNops();
     uintptr_t x = uintptr_t(-1);
     BufferOffset off = EmitData(&x, sizeof(uintptr_t));
     label->patchAt()->bind(off.getOffset());
   }
 
   void verifyHeapAccessDisassembly(uint32_t begin, uint32_t end,
                                    const Disassembler::HeapAccess& heapAccess) {
     MOZ_CRASH("verifyHeapAccessDisassembly");
@@ -518,32 +519,31 @@ static inline bool GetTempRegForIntArg(u
 }
 
 inline Imm32 Imm64::firstHalf() const { return low(); }
 
 inline Imm32 Imm64::secondHalf() const { return hi(); }
 
 void PatchJump(CodeLocationJump& jump_, CodeLocationLabel label);
 
-// Forbids pool generation during a specified interval. Not nestable.
-class AutoForbidPools {
-  Assembler* asm_;
-
- public:
-  AutoForbidPools(Assembler* asm_, size_t maxInst) : asm_(asm_) {
-    asm_->enterNoPool(maxInst);
-  }
-  ~AutoForbidPools() { asm_->leaveNoPool(); }
-};
-
 // Forbids nop filling for testing purposes. Not nestable.
 class AutoForbidNops {
+ protected:
   Assembler* asm_;
 
  public:
   explicit AutoForbidNops(Assembler* asm_) : asm_(asm_) { asm_->enterNoNops(); }
   ~AutoForbidNops() { asm_->leaveNoNops(); }
 };
 
+// Forbids pool generation during a specified interval. Not nestable.
+class AutoForbidPoolsAndNops : public AutoForbidNops {
+ public:
+  AutoForbidPoolsAndNops(Assembler* asm_, size_t maxInst) : AutoForbidNops(asm_) {
+    asm_->enterNoPool(maxInst);
+  }
+  ~AutoForbidPoolsAndNops() { asm_->leaveNoPool(); }
+};
+
 }  // namespace jit
 }  // namespace js
 
 #endif  // A64_ASSEMBLER_A64_H_
--- a/js/src/jit/arm64/CodeGenerator-arm64.cpp
+++ b/js/src/jit/arm64/CodeGenerator-arm64.cpp
@@ -806,17 +806,18 @@ class js::jit::OutOfLineTableSwitch
   MTableSwitch* mir() const { return mir_; }
 
   CodeLabel* jumpLabel() { return &jumpLabel_; }
 };
 
 void CodeGeneratorARM64::visitOutOfLineTableSwitch(OutOfLineTableSwitch* ool) {
   MTableSwitch* mir = ool->mir();
 
-  AutoForbidPools afp(
+  // Prevent nop and pools sequences to appear in the jump table.
+  AutoForbidPoolsAndNops afp(
       &masm, (mir->numCases() + 1) * (sizeof(void*) / vixl::kInstructionSize));
   masm.haltingAlign(sizeof(void*));
   masm.bind(ool->jumpLabel());
   masm.addCodeLabel(*ool->jumpLabel());
 
   for (size_t i = 0; i < mir->numCases(); i++) {
     LBlock* caseblock = skipTrivialBlocks(mir->getCase(i))->lir();
     Label* caseheader = caseblock->label();
--- a/js/src/jit/arm64/MacroAssembler-arm64-inl.h
+++ b/js/src/jit/arm64/MacroAssembler-arm64-inl.h
@@ -271,17 +271,17 @@ void MacroAssembler::add64(Imm32 imm, Re
 
 void MacroAssembler::add64(Imm64 imm, Register64 dest) {
   Add(ARMRegister(dest.reg, 64), ARMRegister(dest.reg, 64), Operand(imm.value));
 }
 
 CodeOffset MacroAssembler::sub32FromStackPtrWithPatch(Register dest) {
   vixl::UseScratchRegisterScope temps(this);
   const ARMRegister scratch = temps.AcquireX();
-  AutoForbidPools afp(this, /* max number of instructions in scope = */ 3);
+  AutoForbidPoolsAndNops afp(this, /* max number of instructions in scope = */ 3);
   CodeOffset offs = CodeOffset(currentOffset());
   movz(scratch, 0, 0);
   movk(scratch, 0, 16);
   Sub(ARMRegister(dest, 64), sp, scratch);
   return offs;
 }
 
 void MacroAssembler::patchSub32FromStackPtr(CodeOffset offset, Imm32 imm) {
--- a/js/src/jit/arm64/MacroAssembler-arm64.cpp
+++ b/js/src/jit/arm64/MacroAssembler-arm64.cpp
@@ -304,22 +304,22 @@ void MacroAssemblerCompat::wasmLoadImpl(
   ARMRegister ptr(ptr_, 64);
   if (offset) {
     Add(ptr, ptr, Operand(offset));
   }
 
   asMasm().memoryBarrierBefore(access.sync());
 
   // Reg+Reg addressing is directly encodable in one Load instruction, hence
-  // the AutoForbidPools will ensure that the access metadata is emitted at
+  // the AutoForbidPoolsAndNops will ensure that the access metadata is emitted at
   // the address of the Load.
   MemOperand srcAddr(memoryBase, ptr);
 
   {
-    AutoForbidPools afp(this, /* max number of instructions in scope = */ 1);
+    AutoForbidPoolsAndNops afp(this, /* max number of instructions in scope = */ 1);
     append(access, asMasm().currentOffset());
     switch (access.type()) {
       case Scalar::Int8:
         Ldrsb(SelectGPReg(outany, out64), srcAddr);
         break;
       case Scalar::Uint8:
         Ldrb(SelectGPReg(outany, out64), srcAddr);
         break;
@@ -372,22 +372,22 @@ void MacroAssemblerCompat::wasmStoreImpl
   ARMRegister ptr(ptr_, 64);
   if (offset) {
     Add(ptr, ptr, Operand(offset));
   }
 
   asMasm().memoryBarrierBefore(access.sync());
 
   // Reg+Reg addressing is directly encodable in one Store instruction, hence
-  // the AutoForbidPools will ensure that the access metadata is emitted at
+  // the AutoForbidPoolsAndNops will ensure that the access metadata is emitted at
   // the address of the Store.
   MemOperand dstAddr(memoryBase, ptr);
 
   {
-    AutoForbidPools afp(this, /* max number of instructions in scope = */ 1);
+    AutoForbidPoolsAndNops afp(this, /* max number of instructions in scope = */ 1);
     append(access, asMasm().currentOffset());
     switch (access.type()) {
       case Scalar::Int8:
       case Scalar::Uint8:
         Strb(SelectGPReg(valany, val64), dstAddr);
         break;
       case Scalar::Int16:
       case Scalar::Uint16:
@@ -678,17 +678,17 @@ void MacroAssembler::patchCall(uint32_t 
   AutoFlushICache::flush(uintptr_t(inst), 4);
 }
 
 CodeOffset MacroAssembler::farJumpWithPatch() {
   vixl::UseScratchRegisterScope temps(this);
   const ARMRegister scratch = temps.AcquireX();
   const ARMRegister scratch2 = temps.AcquireX();
 
-  AutoForbidPools afp(this, /* max number of instructions in scope = */ 7);
+  AutoForbidPoolsAndNops afp(this, /* max number of instructions in scope = */ 7);
 
   mozilla::DebugOnly<uint32_t> before = currentOffset();
 
   align(8);  // At most one nop
 
   Label branch;
   adr(scratch2, &branch);
   ldr(scratch, vixl::MemOperand(scratch2, 4));
@@ -715,17 +715,17 @@ void MacroAssembler::patchFarJump(CodeOf
   MOZ_ASSERT(inst1->InstructionBits() == UINT32_MAX);
   MOZ_ASSERT(inst2->InstructionBits() == UINT32_MAX);
 
   inst1->SetInstructionBits((uint32_t)distance);
   inst2->SetInstructionBits((uint32_t)(distance >> 32));
 }
 
 CodeOffset MacroAssembler::nopPatchableToCall(const wasm::CallSiteDesc& desc) {
-  AutoForbidPools afp(this, /* max number of instructions in scope = */ 1);
+  AutoForbidPoolsAndNops afp(this, /* max number of instructions in scope = */ 1);
   CodeOffset offset(currentOffset());
   Nop();
   append(desc, CodeOffset(currentOffset()));
   return offset;
 }
 
 void MacroAssembler::patchNopToCall(uint8_t* call, uint8_t* target) {
   uint8_t* inst = call - 4;
@@ -1070,17 +1070,17 @@ template void MacroAssembler::storeUnbox
     const BaseObjectElementIndex& dest, MIRType slotType);
 
 void MacroAssembler::comment(const char* msg) { Assembler::comment(msg); }
 
 // ========================================================================
 // wasm support
 
 CodeOffset MacroAssembler::wasmTrapInstruction() {
-  AutoForbidPools afp(this, /* max number of instructions in scope = */ 1);
+  AutoForbidPoolsAndNops afp(this, /* max number of instructions in scope = */ 1);
   CodeOffset offs(currentOffset());
   Unreachable();
   return offs;
 }
 
 void MacroAssembler::wasmBoundsCheck(Condition cond, Register index,
                                      Register boundsCheckLimit, Label* label) {
   // Not used on ARM64, we rely on signal handling instead
@@ -1510,66 +1510,66 @@ static void SignOrZeroExtend(MacroAssemb
 
 static void LoadExclusive(MacroAssembler& masm,
                           const wasm::MemoryAccessDesc* access,
                           Scalar::Type srcType, Width targetWidth,
                           MemOperand ptr, Register dest) {
   bool signExtend = Scalar::isSignedIntType(srcType);
 
   // With this address form, a single native ldxr* will be emitted, and the
-  // AutoForbidPools ensures that the metadata is emitted at the address of
+  // AutoForbidPoolsAndNops ensures that the metadata is emitted at the address of
   // the ldxr*.
   MOZ_ASSERT(ptr.IsImmediateOffset() && ptr.offset() == 0);
 
   switch (Scalar::byteSize(srcType)) {
     case 1: {
       {
-        AutoForbidPools afp(&masm,
+        AutoForbidPoolsAndNops afp(&masm,
                             /* max number of instructions in scope = */ 1);
         if (access) {
           masm.append(*access, masm.currentOffset());
         }
         masm.Ldxrb(W(dest), ptr);
       }
       if (signExtend) {
         masm.Sbfm(R(dest, targetWidth), R(dest, targetWidth), 0, 7);
       }
       break;
     }
     case 2: {
       {
-        AutoForbidPools afp(&masm,
+        AutoForbidPoolsAndNops afp(&masm,
                             /* max number of instructions in scope = */ 1);
         if (access) {
           masm.append(*access, masm.currentOffset());
         }
         masm.Ldxrh(W(dest), ptr);
       }
       if (signExtend) {
         masm.Sbfm(R(dest, targetWidth), R(dest, targetWidth), 0, 15);
       }
       break;
     }
     case 4: {
       {
-        AutoForbidPools afp(&masm,
+        AutoForbidPoolsAndNops afp(&masm,
                             /* max number of instructions in scope = */ 1);
         if (access) {
           masm.append(*access, masm.currentOffset());
         }
         masm.Ldxr(W(dest), ptr);
       }
       if (targetWidth == Width::_64 && signExtend) {
         masm.Sbfm(X(dest), X(dest), 0, 31);
       }
       break;
     }
     case 8: {
       {
-        AutoForbidPools afp(&masm,
+        AutoForbidPoolsAndNops afp(&masm,
                             /* max number of instructions in scope = */ 1);
         if (access) {
           masm.append(*access, masm.currentOffset());
         }
         masm.Ldxr(X(dest), ptr);
       }
       break;
     }
--- a/js/src/jit/shared/IonAssemblerBufferWithConstantPools.h
+++ b/js/src/jit/shared/IonAssemblerBufferWithConstantPools.h
@@ -951,22 +951,25 @@ struct AssemblerBufferWithConstantPools
 
     // Include branches that would expire in the next N bytes.
     // The hysteresis avoids the needless creation of many tiny constant
     // pools.
     return this->nextOffset().getOffset() + ShortRangeBranchHysteresis >
            size_t(branchDeadlines_.earliestDeadline().getOffset());
   }
 
+  bool isPoolEmpty() const {
+    return pool_.numEntries() == 0 && !hasExpirableShortRangeBranches();
+  }
   void finishPool() {
     JitSpew(JitSpew_Pools,
             "[%d] Attempting to finish pool %zu with %u entries.", id,
             poolInfo_.length(), pool_.numEntries());
 
-    if (pool_.numEntries() == 0 && !hasExpirableShortRangeBranches()) {
+    if (isPoolEmpty()) {
       // If there is no data in the pool being dumped, don't dump anything.
       JitSpew(JitSpew_Pools, "[%d] Aborting because the pool is empty", id);
       return;
     }
 
     // Should not be placing a pool in a no-pool region, check.
     MOZ_ASSERT(!canNotPlacePool_);
 
@@ -1096,16 +1099,19 @@ struct AssemblerBufferWithConstantPools
   void enterNoNops() {
     MOZ_ASSERT(!inhibitNops_);
     inhibitNops_ = true;
   }
   void leaveNoNops() {
     MOZ_ASSERT(inhibitNops_);
     inhibitNops_ = false;
   }
+  void assertNoPoolAndNoNops() {
+    MOZ_ASSERT(inhibitNops_ && (isPoolEmpty() || canNotPlacePool_));
+  }
 
   void align(unsigned alignment) { align(alignment, alignFillInst_); }
 
   void align(unsigned alignment, uint32_t pattern) {
     MOZ_ASSERT(mozilla::IsPowerOfTwo(alignment));
     MOZ_ASSERT(alignment >= InstSize);
 
     // A pool many need to be dumped at this point, so insert NOP fill here.
@@ -1122,21 +1128,22 @@ struct AssemblerBufferWithConstantPools
     // dumped at the aligned code position.
     if (!hasSpaceForInsts(requiredFill / InstSize + 1, 0)) {
       // Alignment would cause a pool dump, so dump the pool now.
       JitSpew(JitSpew_Pools, "[%d] Alignment of %d at %zu caused a spill.", id,
               alignment, sizeExcludingCurrentPool());
       finishPool();
     }
 
+    bool prevInhibitNops = inhibitNops_;
     inhibitNops_ = true;
     while ((sizeExcludingCurrentPool() & (alignment - 1)) && !this->oom()) {
       putInt(pattern);
     }
-    inhibitNops_ = false;
+    inhibitNops_ = prevInhibitNops;
   }
 
  public:
   void executableCopy(uint8_t* dest) {
     if (this->oom()) {
       return;
     }
     // The pools should have all been flushed, check.
--- a/js/src/vm/GeneratorObject.cpp
+++ b/js/src/vm/GeneratorObject.cpp
@@ -132,21 +132,22 @@ void js::SetGeneratorClosed(JSContext* c
   Shape* shape = callObj.lookup(cx, cx->names().dotGenerator);
   auto& genObj =
       callObj.getSlot(shape->slot()).toObject().as<AbstractGeneratorObject>();
   genObj.setClosed();
 }
 
 bool js::GeneratorThrowOrReturn(JSContext* cx, AbstractFramePtr frame,
                                 Handle<AbstractGeneratorObject*> genObj,
-                                HandleValue arg, uint32_t resumeKind) {
-  if (resumeKind == AbstractGeneratorObject::THROW) {
+                                HandleValue arg,
+                                GeneratorResumeKind resumeKind) {
+  if (resumeKind == GeneratorResumeKind::Throw) {
     cx->setPendingExceptionAndCaptureStack(arg);
   } else {
-    MOZ_ASSERT(resumeKind == AbstractGeneratorObject::RETURN);
+    MOZ_ASSERT(resumeKind == GeneratorResumeKind::Return);
 
     MOZ_ASSERT_IF(genObj->is<GeneratorObject>(), arg.isObject());
     frame.setReturnValue(arg);
 
     RootedValue closing(cx, MagicValue(JS_GENERATOR_CLOSING));
     cx->setPendingException(closing, nullptr);
     genObj->setClosing();
   }
--- a/js/src/vm/GeneratorObject.h
+++ b/js/src/vm/GeneratorObject.h
@@ -11,55 +11,55 @@
 #include "vm/ArgumentsObject.h"
 #include "vm/ArrayObject.h"
 #include "vm/JSContext.h"
 #include "vm/JSObject.h"
 #include "vm/Stack.h"
 
 namespace js {
 
+enum class GeneratorResumeKind { Next, Throw, Return };
+
 class AbstractGeneratorObject : public NativeObject {
  public:
   // Magic values stored in the resumeIndex slot when the generator is
   // running or closing. See the resumeIndex comment below.
   static const int32_t RESUME_INDEX_RUNNING = INT32_MAX;
   static const int32_t RESUME_INDEX_CLOSING = INT32_MAX - 1;
 
   enum {
     CALLEE_SLOT = 0,
     ENV_CHAIN_SLOT,
     ARGS_OBJ_SLOT,
     EXPRESSION_STACK_SLOT,
     RESUME_INDEX_SLOT,
     RESERVED_SLOTS
   };
 
-  enum ResumeKind { NEXT, THROW, RETURN };
-
  private:
   static bool suspend(JSContext* cx, HandleObject obj, AbstractFramePtr frame,
                       jsbytecode* pc, Value* vp, unsigned nvalues);
 
  public:
-  static inline ResumeKind getResumeKind(jsbytecode* pc) {
+  static GeneratorResumeKind getResumeKind(jsbytecode* pc) {
     MOZ_ASSERT(*pc == JSOP_RESUME);
     unsigned arg = GET_UINT8(pc);
-    MOZ_ASSERT(arg <= RETURN);
-    return static_cast<ResumeKind>(arg);
+    MOZ_ASSERT(arg <= unsigned(GeneratorResumeKind::Return));
+    return static_cast<GeneratorResumeKind>(arg);
   }
 
-  static inline ResumeKind getResumeKind(JSContext* cx, JSAtom* atom) {
+  static GeneratorResumeKind getResumeKind(JSContext* cx, JSAtom* atom) {
     if (atom == cx->names().next) {
-      return NEXT;
+      return GeneratorResumeKind::Next;
     }
     if (atom == cx->names().throw_) {
-      return THROW;
+      return GeneratorResumeKind::Throw;
     }
     MOZ_ASSERT(atom == cx->names().return_);
-    return RETURN;
+    return GeneratorResumeKind::Return;
   }
 
   static JSObject* create(JSContext* cx, AbstractFramePtr frame);
 
   static bool resume(JSContext* cx, InterpreterActivation& activation,
                      Handle<AbstractGeneratorObject*> genObj, HandleValue arg);
 
   static bool initialSuspend(JSContext* cx, HandleObject obj,
@@ -208,17 +208,17 @@ class GeneratorObject : public AbstractG
 
   static const Class class_;
 
   static GeneratorObject* create(JSContext* cx, HandleFunction fun);
 };
 
 bool GeneratorThrowOrReturn(JSContext* cx, AbstractFramePtr frame,
                             Handle<AbstractGeneratorObject*> obj,
-                            HandleValue val, uint32_t resumeKind);
+                            HandleValue val, GeneratorResumeKind resumeKind);
 
 /**
  * Return the generator object associated with the given frame. The frame must
  * be a call frame for a generator. If the generator object hasn't been created
  * yet, or hasn't been stored in the stack slot yet, this returns null.
  */
 AbstractGeneratorObject* GetGeneratorObjectForFrame(JSContext* cx,
                                                     AbstractFramePtr frame);
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -4103,20 +4103,20 @@ static MOZ_NEVER_INLINE JS_HAZ_JSNATIVE_
                 gen->isClosed());
             if (!ForcedReturn(cx, REGS)) {
               goto error;
             }
             goto successful_return_continuation;
         }
 
         switch (resumeKind) {
-          case AbstractGeneratorObject::NEXT:
+          case GeneratorResumeKind::Next:
             break;
-          case AbstractGeneratorObject::THROW:
-          case AbstractGeneratorObject::RETURN:
+          case GeneratorResumeKind::Throw:
+          case GeneratorResumeKind::Return:
             MOZ_ALWAYS_FALSE(GeneratorThrowOrReturn(cx, activation.regs().fp(),
                                                     gen, val, resumeKind));
             goto error;
           default:
             MOZ_CRASH("bad resumeKind");
         }
       }
       ADVANCE_AND_DISPATCH(0);
--- a/js/src/wasm/WasmBaselineCompile.cpp
+++ b/js/src/wasm/WasmBaselineCompile.cpp
@@ -4690,17 +4690,17 @@ class BaseCompiler final : public BaseCo
     tableCl.target()->bind(theTable->offset());
     masm.addCodeLabel(tableCl);
 
     masm.jmp(Operand(scratch, switchValue, ScalePointer));
 #elif defined(JS_CODEGEN_ARM)
     // Flush constant pools: offset must reflect the distance from the MOV
     // to the start of the table; as the address of the MOV is given by the
     // label, nothing must come between the bind() and the ma_mov().
-    AutoForbidPools afp(&masm, /* number of instructions in scope = */ 5);
+    AutoForbidPoolsAndNops afp(&masm, /* number of instructions in scope = */ 5);
 
     ScratchI32 scratch(*this);
 
     // Compute the offset from the ma_mov instruction to the jump table.
     Label here;
     masm.bind(&here);
     uint32_t offset = here.offset() - theTable->offset();
 
@@ -4723,17 +4723,17 @@ class BaseCompiler final : public BaseCo
 
     masm.ma_li(scratch, &tableCl);
 
     tableCl.target()->bind(theTable->offset());
     masm.addCodeLabel(tableCl);
 
     masm.branchToComputedAddress(BaseIndex(scratch, switchValue, ScalePointer));
 #elif defined(JS_CODEGEN_ARM64)
-    AutoForbidPools afp(&masm, /* number of instructions in scope = */ 4);
+    AutoForbidPoolsAndNops afp(&masm, /* number of instructions in scope = */ 4);
 
     ScratchI32 scratch(*this);
 
     ARMRegister s(scratch, 64);
     ARMRegister v(switchValue, 64);
     masm.Adr(s, theTable);
     masm.Add(s, s, Operand(v, vixl::LSL, 3));
     masm.Ldr(s, MemOperand(s, 0));
--- a/js/src/wasm/WasmFrameIter.cpp
+++ b/js/src/wasm/WasmFrameIter.cpp
@@ -412,17 +412,17 @@ void wasm::ClearExitFP(MacroAssembler& m
 }
 
 static void GenerateCallablePrologue(MacroAssembler& masm, uint32_t* entry) {
   masm.setFramePushed(0);
 
   // ProfilingFrameIterator needs to know the offsets of several key
   // instructions from entry. To save space, we make these offsets static
   // constants and assert that they match the actual codegen below. On ARM,
-  // this requires AutoForbidPools to prevent a constant pool from being
+  // this requires AutoForbidPoolsAndNops to prevent a constant pool from being
   // randomly inserted between two instructions.
 #if defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
   {
     *entry = masm.currentOffset();
 
     masm.subFromStackPtr(Imm32(sizeof(Frame)));
     masm.storePtr(ra, Address(StackPointer, offsetof(Frame, returnAddress)));
     MOZ_ASSERT_IF(!masm.oom(), PushedRetAddr == masm.currentOffset() - *entry);
@@ -434,17 +434,17 @@ static void GenerateCallablePrologue(Mac
     masm.moveStackPtrTo(FramePointer);
     MOZ_ASSERT_IF(!masm.oom(), SetFP == masm.currentOffset() - *entry);
   }
 #elif defined(JS_CODEGEN_ARM64)
   {
     // We do not use the PseudoStackPointer.
     MOZ_ASSERT(masm.GetStackPointer64().code() == sp.code());
 
-    AutoForbidPools afp(&masm, /* number of instructions in scope = */ 5);
+    AutoForbidPoolsAndNops afp(&masm, /* number of instructions in scope = */ 5);
 
     *entry = masm.currentOffset();
 
     masm.Sub(sp, sp, sizeof(Frame));
     masm.Str(ARMRegister(lr, 64),
              MemOperand(sp, offsetof(Frame, returnAddress)));
     MOZ_ASSERT_IF(!masm.oom(), PushedRetAddr == masm.currentOffset() - *entry);
     masm.Str(ARMRegister(WasmTlsReg, 64), MemOperand(sp, offsetof(Frame, tls)));
@@ -453,17 +453,17 @@ static void GenerateCallablePrologue(Mac
              MemOperand(sp, offsetof(Frame, callerFP)));
     MOZ_ASSERT_IF(!masm.oom(), PushedFP == masm.currentOffset() - *entry);
     masm.Mov(ARMRegister(FramePointer, 64), sp);
     MOZ_ASSERT_IF(!masm.oom(), SetFP == masm.currentOffset() - *entry);
   }
 #else
   {
 #  if defined(JS_CODEGEN_ARM)
-    AutoForbidPools afp(&masm, /* number of instructions in scope = */ 7);
+    AutoForbidPoolsAndNops afp(&masm, /* number of instructions in scope = */ 7);
 
     *entry = masm.currentOffset();
 
     MOZ_ASSERT(BeforePushRetAddr == 0);
     masm.push(lr);
 #  else
     *entry = masm.currentOffset();
     // The x86/x64 call instruction pushes the return address.
@@ -505,17 +505,17 @@ static void GenerateCallableEpilogue(Mac
   masm.as_jr(ra);
   masm.addToStackPtr(Imm32(sizeof(Frame)));
 
 #elif defined(JS_CODEGEN_ARM64)
 
   // We do not use the PseudoStackPointer.
   MOZ_ASSERT(masm.GetStackPointer64().code() == sp.code());
 
-  AutoForbidPools afp(&masm, /* number of instructions in scope = */ 5);
+  AutoForbidPoolsAndNops afp(&masm, /* number of instructions in scope = */ 5);
 
   masm.Ldr(ARMRegister(FramePointer, 64),
            MemOperand(sp, offsetof(Frame, callerFP)));
   poppedFP = masm.currentOffset();
 
   masm.Ldr(ARMRegister(WasmTlsReg, 64), MemOperand(sp, offsetof(Frame, tls)));
   poppedTlsReg = masm.currentOffset();
 
@@ -523,17 +523,17 @@ static void GenerateCallableEpilogue(Mac
   *ret = masm.currentOffset();
 
   masm.Add(sp, sp, sizeof(Frame));
   masm.Ret(ARMRegister(lr, 64));
 
 #else
   // Forbid pools for the same reason as described in GenerateCallablePrologue.
 #  if defined(JS_CODEGEN_ARM)
-  AutoForbidPools afp(&masm, /* number of instructions in scope = */ 7);
+  AutoForbidPoolsAndNops afp(&masm, /* number of instructions in scope = */ 7);
 #  endif
 
   // There is an important ordering constraint here: fp must be repointed to
   // the caller's frame before any field of the frame currently pointed to by
   // fp is popped: asynchronous signal handlers (which use stack space
   // starting at sp) could otherwise clobber these fields while they are still
   // accessible via fp (fp fields are read during frame iteration which is
   // *also* done asynchronously).
@@ -692,25 +692,25 @@ void wasm::GenerateJitExitEpilogue(Macro
   MOZ_ASSERT(masm.framePushed() == 0);
 }
 
 void wasm::GenerateJitEntryPrologue(MacroAssembler& masm, Offsets* offsets) {
   masm.haltingAlign(CodeAlignment);
 
   {
 #if defined(JS_CODEGEN_ARM)
-    AutoForbidPools afp(&masm, /* number of instructions in scope = */ 2);
+    AutoForbidPoolsAndNops afp(&masm, /* number of instructions in scope = */ 2);
     offsets->begin = masm.currentOffset();
     MOZ_ASSERT(BeforePushRetAddr == 0);
     masm.push(lr);
 #elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
     offsets->begin = masm.currentOffset();
     masm.push(ra);
 #elif defined(JS_CODEGEN_ARM64)
-    AutoForbidPools afp(&masm, /* number of instructions in scope = */ 3);
+    AutoForbidPoolsAndNops afp(&masm, /* number of instructions in scope = */ 3);
     offsets->begin = masm.currentOffset();
     MOZ_ASSERT(BeforePushRetAddr == 0);
     // Subtract from SP first as SP must be aligned before offsetting.
     masm.Sub(sp, sp, 8);
     masm.storePtr(lr, Address(masm.getStackPointer(), 0));
     masm.adjustFrame(8);
 #else
     // The x86/x64 call instruction pushes the return address.
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -2099,11 +2099,11 @@ fuzzy(0-1,0-625) == 1466638-1.html 14666
 == 1483649-1.xul 1483649-1-ref.xul
 test-pref(layout.css.contain.enabled,true) == 1483946.html 1483946-ref.html
 test-pref(layout.css.visited_links_enabled,false) == 1488155.html 1488155-ref.html
 == 1492660-1.html 1492660-1-ref.html
 pref(layout.css.supports-selector.enabled,true) == 1499386.html 1499386-ref.html
 pref(layout.css.supports-selector.enabled,false) != 1499386.html 1499386-ref.html
 == 1509425-1.html 1509425-1-ref.html
 == 1511570.html 1511570-ref.html
-fuzzy-if(!webrender,1-5,144-547) == 1529992-1.html 1529992-1-ref.html
+fuzzy-if(!webrender,1-5,66-547) == 1529992-1.html 1529992-1-ref.html
 fuzzy-if(!webrender,0-6,0-34) fails-if(webrender) == 1529992-2.html 1529992-2-ref.html
 == 1535040-1.html 1535040-1-ref.html
--- a/layout/reftests/webm-video/reftest.list
+++ b/layout/reftests/webm-video/reftest.list
@@ -29,16 +29,17 @@ fails-if(Android) == poster-7.html poste
 fuzzy-if(Android,0-2,0-14000) == poster-8.html poster-ref-black140x100.html
 random == poster-10.html poster-ref-blue125x100.html
 random == poster-11.html poster-ref-blue140x100.html
 random == poster-12.html poster-ref-blue140x100.html
 random-if(Android) == poster-13.html poster-ref-blue400x300.html
 random-if(Android) == poster-15.html poster-ref-green70x30.html
 random-if(winWidget) random-if(cocoaWidget) skip-if(Android) == bug686957.html bug686957-ref.html # bug 922951 for OS X
 == webm-alpha.html webm-alpha-ref.html
+fuzzy(0-5,0-111556) == webm-alpha-2.html webm-alpha-2-ref.html
 
 # Tests for <video src> with 'object-fit' & 'object-position':
 # These tests should be very similar to tests in our w3c-css/submitted/images3
 # reftest directory. They live here because they use WebM video (VP9), and it
 # wouldn't be fair of us to make a W3C testsuite implicitly depend on any
 # particular (non-spec-mandated) video codec.
 default-preferences test-pref(gfx.ycbcr.accurate-conversion,true)
 fails-if(layersGPUAccelerated) skip-if(Android) == object-fit-contain-webm-001.html object-fit-contain-webm-001-ref.html # Bug 1083516 for layersGPUAccelerated failures, Bug 1084564 for Android failures
new file mode 100644
--- /dev/null
+++ b/layout/reftests/webm-video/webm-alpha-2-ref.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html lang="en">
+<meta charset="utf-8">
+<title>Reference for reftest that checks whether WebM video with alpha is rendered with the correct stride</title>
+
+<style>
+.edgeCover {
+  position: absolute;
+  box-shadow: 0 0 0 4px magenta, inset 0 0 0 4px magenta;
+}
+</style>
+
+<div style="position: relative">
+  <div class="edgeCover" style="background: red; left: 0; top: 0; width: 64px; height: 64px;"></div>
+  <div class="edgeCover" style="background: blue; left: 64px; top: 0; width: 64px; height: 64px;"></div>
+  <div class="edgeCover" style="background: yellow; left: 0; top: 64px; width: 64px; height: 64px;"></div>
+  <div class="edgeCover" style="background: lime; left: 64px; top: 64px; width: 64px; height: 64px;"></div>
+  <div class="edgeCover" style="background: black; left: 196px; top: 0; width: 16px; height: 334px;"></div>
+</div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/webm-video/webm-alpha-2.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html lang="en">
+<meta charset="utf-8">
+<title>Reftest that checks whether WebM video with alpha is rendered with the correct stride</title>
+
+<style>
+.edgeCover {
+  position: absolute;
+  box-shadow: 0 0 0 4px magenta, inset 0 0 0 4px magenta;
+}
+</style>
+
+<div style="position: relative">
+<!--
+  The video below was created as follows:
+
+  1. Run this code on the web console:
+
+var c = document.createElement("canvas")
+c.width = 334;
+c.height = 334;
+var ctx = c.getContext("2d")
+ctx.fillStyle = "white"
+ctx.fillRect(0, 0, 334, 334)
+ctx.fillStyle = "red"
+ctx.fillRect(0, 0, 64, 64)
+ctx.fillStyle = "blue"
+ctx.fillRect(64, 0, 64, 64)
+ctx.fillStyle = "yellow"
+ctx.fillRect(0, 64, 64, 64)
+ctx.fillStyle = "lime"
+ctx.fillRect(64, 64, 64, 64)
+ctx.fillStyle = "black"
+ctx.fillRect(196, 0, 16, 334)
+c.toDataURL()
+
+  2. Save the PNG to a file called "frame.png".
+  3. Convert it to a WebM file using:
+
+ffmpeg -framerate 25 -f image2 -i frame.png -c:v libvpx -auto-alt-ref 0 -pix_fmt yuva420p output.webm
+
+    (The "a" in "yuva420p" gives the video an alpha plane.)
+
+  4. Convert the WebM file to a data url.
+
+  The video size is 334x334. 334 is not divisible by 4. But 336 is divisible by 4 (and by 16).
+-->
+  <video autoplay src="data:video/webm;base64,GkXfowEAAAAAAAAfQoaBAUL3gQFC8oEEQvOBCEKChHdlYm1Ch4ECQoWBAhhTgGcBAAAAAAAFFxFNm3RAO027i1OrhBVJqWZTrIHlTbuMU6uEFlSua1OsggEjTbuMU6uEElTDZ1OsggFwTbuMU6uEHFO7a1OsggT67AEAAAAAAACbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVSalmAQAAAAAAADIq17GDD0JATYCNTGF2ZjU4LjEyLjEwMFdBjUxhdmY1OC4xMi4xMDBEiYhARAAAAAAAABZUrmsBAAAAAAAAQa4BAAAAAAAAONeBAXPFgQGcgQAitZyDdW5khoVWX1ZQOIOBASPjg4QCYloA4AEAAAAAAAAMsIIBTrqCAU5TwIEBElTDZwEAAAAAAAC/c3MBAAAAAAAALmPAAQAAAAAAAABnyAEAAAAAAAAaRaOHRU5DT0RFUkSHjUxhdmY1OC4xMi4xMDBzcwEAAAAAAAA5Y8ABAAAAAAAABGPFgQFnyAEAAAAAAAAhRaOHRU5DT0RFUkSHlExhdmM1OC4xOC4xMDAgbGlidnB4c3MBAAAAAAAAOmPAAQAAAAAAAARjxYEBZ8gBAAAAAAAAIkWjiERVUkFUSU9ORIeUMDA6MDA6MDAuMDQwMDAwMDAwAAAfQ7Z1AQAAAAAAArPngQCgAQAAAAAAAqehQa+BAAAAECMAnQEqTgFOAQBHCIWFiIWEiAICIcB/gIxxQufgP4A9Lt4jh/4A/rBxoPYH9XeeA//cV4YH+A/zP1/7vF6v8OfS+b7aiY5ZAVRd7+B19e+R7TWdOyggwiXRbKr6e46ES8nODRmmsdHCSb2Ryky5CBdA85lugf/bcPq9FrVGK6E1Eq803lynCCqlTKr6e5sQVvvWkT2mnsucsPMn40e9aRPaae5uje+FHBKKfBFvcaiVeaazp2HiP9OnTp06dOw8R/p06dOnTp2HiP9OnTp06dOw8R/p06dOnTp2HiP9OnTp06dOw8R/p06dOnTp2HiP9OnTp06dOw8R/p06dOnTp2HiP9OnTp06dOw8R/p06dOnTp2HiP9OnTp06dOw8R/pvgD98nbATdAYGPgGEfqzwHaRDoh/yUJISQz2Qf1XrtcNhRYp///IKASH//40AA+zYdKbVnQlfxAlilLFiSS41KVx1DTCUxV9DmTS53pH2k1nrQQPyJ6ZE0aSsttOFtwAJ8cYL6BIAjpRqJ11YtY+TPfKFKdft0Uxv+1K+yvSpmFBL/U1Vr1TG8yIAHWhAQAAAAAAAOumAQAAAAAAAOLugQGlQNywGQCdASpOAU4BAEcIhYWIhYSIAgIABhYE9waBZJ9r25snOHsnOHsnOHsnOHsnOHsnOHsnOHsnOHsnOHsnOHsnOHsnOHsnOHsnOHsnOHsnOHsnOHsnOHsnOHsnOHsnOHsnOHsnOHsnOHsnOHsnOHsnOHsnOHsnOHsnOHsnOHsnOHsnOHsnOHsnOHsnOHsnOHsnOHsnOHsnOHsnOHsnOHsnOHsnOHsnOHsnOHsnOHsnOHsnOHsnOHsnOHsnOHsnOHsnOHsnOHsnOHsnOHsnOHsnOHsnOHsigP7/uoMAHFO7awEAAAAAAAARu4+zgQC3iveBAfGCAjvwgQM="></video>
+  <div class="edgeCover" style="left: 0; top: 0; width: 64px; height: 64px;"></div>
+  <div class="edgeCover" style="left: 64px; top: 0; width: 64px; height: 64px;"></div>
+  <div class="edgeCover" style="left: 0; top: 64px; width: 64px; height: 64px;"></div>
+  <div class="edgeCover" style="left: 64px; top: 64px; width: 64px; height: 64px;"></div>
+  <div class="edgeCover" style="left: 196px; top: 0; width: 16px; height: 334px;"></div>
+</div>
--- a/media/libcubeb/disable-device-switching.patch
+++ b/media/libcubeb/disable-device-switching.patch
@@ -1,39 +1,8 @@
-diff --git a/media/libcubeb/include/cubeb.h b/media/libcubeb/include/cubeb.h
---- a/media/libcubeb/include/cubeb.h
-+++ b/media/libcubeb/include/cubeb.h
-@@ -216,20 +216,23 @@ enum {
-                          CHANNEL_FRONT_CENTER | CHANNEL_LOW_FREQUENCY |
-                          CHANNEL_BACK_LEFT | CHANNEL_BACK_RIGHT |
-                          CHANNEL_SIDE_LEFT | CHANNEL_SIDE_RIGHT,
- };
- 
- /** Miscellaneous stream preferences. */
- typedef enum {
-   CUBEB_STREAM_PREF_NONE     = 0x00, /**< No stream preferences are requested. */
--  CUBEB_STREAM_PREF_LOOPBACK = 0x01 /**< Request a loopback stream. Should be
--                                         specified on the input params and an
--                                         output device to loopback from should
--                                         be passed in place of an input device. */
-+  CUBEB_STREAM_PREF_LOOPBACK = 0x01, /**< Request a loopback stream. Should be
-+                                          specified on the input params and an
-+                                          output device to loopback from should
-+                                          be passed in place of an input device. */
-+  CUBEB_STREAM_PREF_DISABLE_DEVICE_SWITCHING = 0x02, /**< Disable switching
-+                                                          default device on OS
-+                                                          changes. */
- } cubeb_stream_prefs;
- 
- /** Stream format initialization parameters. */
- typedef struct {
-   cubeb_sample_format format;   /**< Requested sample format.  One of
-                                      #cubeb_sample_format. */
-   uint32_t rate;                /**< Requested sample rate.  Valid range is [1000, 192000]. */
-   uint32_t channels;            /**< Requested channel count.  Valid range is [1, 8]. */
 diff --git a/media/libcubeb/src/cubeb_wasapi.cpp b/media/libcubeb/src/cubeb_wasapi.cpp
 --- a/media/libcubeb/src/cubeb_wasapi.cpp
 +++ b/media/libcubeb/src/cubeb_wasapi.cpp
 @@ -1829,21 +1829,26 @@ wasapi_stream_init(cubeb * context, cube
         assert that the lock is held in the function. */
      auto_lock lock(stm->stream_reset_lock);
      rv = setup_wasapi_stream(stm.get());
    }
@@ -80,8 +49,31 @@ diff --git a/media/libcubeb/src/cubeb_wa
  
    CloseHandle(stm->reconfigure_event);
    CloseHandle(stm->refill_event);
    CloseHandle(stm->input_available_event);
  
    // The variables intialized in wasapi_stream_init,
    // must be destroyed in wasapi_stream_destroy.
    stm->linear_input_buffer.reset();
+diff --git a/media/libcubeb/include/cubeb.h b/media/libcubeb/include/cubeb.h
+--- a/media/libcubeb/include/cubeb.h
++++ a/media/libcubeb/include/cubeb.h
+@@ -222,16 +222,19 @@
+ 
+ /** Miscellaneous stream preferences. */
+ typedef enum {
+   CUBEB_STREAM_PREF_NONE     = 0x00, /**< No stream preferences are requested. */
+   CUBEB_STREAM_PREF_LOOPBACK = 0x01, /**< Request a loopback stream. Should be
+                                          specified on the input params and an
+                                          output device to loopback from should
+                                          be passed in place of an input device. */
++  CUBEB_STREAM_PREF_DISABLE_DEVICE_SWITCHING = 0x02, /**< Disable switching
++                                                          default device on OS
++                                                          changes. */
+   CUBEB_STREAM_PREF_VOICE = 0x04  /**< This stream is going to transport voice data.
+                                        Depending on the backend and platform, this can
+                                        change the audio input or output devices
+                                        selected, as well as the quality of the stream,
+                                        for example to accomodate bluetooth SCO modes on
+                                        bluetooth devices. */
+ } cubeb_stream_prefs;
+ 
--- a/media/libcubeb/include/cubeb.h
+++ b/media/libcubeb/include/cubeb.h
@@ -219,22 +219,28 @@ enum {
                          CHANNEL_BACK_LEFT | CHANNEL_BACK_RIGHT |
                          CHANNEL_SIDE_LEFT | CHANNEL_SIDE_RIGHT,
 };
 
 /** Miscellaneous stream preferences. */
 typedef enum {
   CUBEB_STREAM_PREF_NONE     = 0x00, /**< No stream preferences are requested. */
   CUBEB_STREAM_PREF_LOOPBACK = 0x01, /**< Request a loopback stream. Should be
-                                          specified on the input params and an
-                                          output device to loopback from should
-                                          be passed in place of an input device. */
+                                         specified on the input params and an
+                                         output device to loopback from should
+                                         be passed in place of an input device. */
   CUBEB_STREAM_PREF_DISABLE_DEVICE_SWITCHING = 0x02, /**< Disable switching
                                                           default device on OS
                                                           changes. */
+  CUBEB_STREAM_PREF_VOICE = 0x04  /**< This stream is going to transport voice data.
+                                       Depending on the backend and platform, this can
+                                       change the audio input or output devices
+                                       selected, as well as the quality of the stream,
+                                       for example to accomodate bluetooth SCO modes on
+                                       bluetooth devices. */
 } cubeb_stream_prefs;
 
 /** Stream format initialization parameters. */
 typedef struct {
   cubeb_sample_format format;   /**< Requested sample format.  One of
                                      #cubeb_sample_format. */
   uint32_t rate;                /**< Requested sample rate.  Valid range is [1000, 192000]. */
   uint32_t channels;            /**< Requested channel count.  Valid range is [1, 8]. */
--- a/media/libcubeb/moz.yaml
+++ b/media/libcubeb/moz.yaml
@@ -14,10 +14,10 @@ bugzilla:
 origin:
   name: "cubeb"
   description: "Cross platform audio library"
 
   url: "https://github.com/kinetiknz/cubeb"
   license: "ISC"
 
   # update.sh will update this value
-  release: "66d9c48d916f00c396482f9c5075feacc2bc0db8 (2019-04-03 12:41:20 +0300)"
+  release: "241e3c7b8a6ce76ad9e075ee5761cd4d0906bc16 (2019-04-16 17:39:01 +0200)"
 
--- a/media/libcubeb/src/android/sles_definitions.h
+++ b/media/libcubeb/src/android/sles_definitions.h
@@ -38,20 +38,19 @@
 #define SL_ANDROID_RECORDING_PRESET_GENERIC             ((SLuint32) 0x00000001)
 /**   uses the microphone audio source with the same orientation as the camera
  *     if available, the main device microphone otherwise */
 #define SL_ANDROID_RECORDING_PRESET_CAMCORDER           ((SLuint32) 0x00000002)
 /**   uses the main microphone tuned for voice recognition */
 #define SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION   ((SLuint32) 0x00000003)
 /**   uses the main microphone tuned for audio communications */
 #define SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION ((SLuint32) 0x00000004)
+/**   uses the main microphone unprocessed */
+#define SL_ANDROID_RECORDING_PRESET_UNPROCESSED         ((SLuint32) 0x00000005)
 
-/** Audio recording get session ID (read only) */
-/** Audio recording get session ID key */
-#define SL_ANDROID_KEY_RECORDING_SESSION_ID ((const SLchar*) "androidRecordingSessionId")
 
 /*---------------------------------------------------------------------------*/
 /* Android AudioPlayer configuration                                         */
 /*---------------------------------------------------------------------------*/
 
 /** Audio playback stream type */
 /** Audio playback stream type key */
 #define SL_ANDROID_KEY_STREAM_TYPE ((const SLchar*) "androidPlaybackStreamType")
@@ -64,14 +63,40 @@
 /*      same as android.media.AudioManager.STREAM_RING */
 #define SL_ANDROID_STREAM_RING         ((SLint32) 0x00000002)
 /*      same as android.media.AudioManager.STREAM_MUSIC */
 #define SL_ANDROID_STREAM_MEDIA        ((SLint32) 0x00000003)
 /*      same as android.media.AudioManager.STREAM_ALARM */
 #define SL_ANDROID_STREAM_ALARM        ((SLint32) 0x00000004)
 /*      same as android.media.AudioManager.STREAM_NOTIFICATION */
 #define SL_ANDROID_STREAM_NOTIFICATION ((SLint32) 0x00000005)
-/*      same as android.media.AudioManager.STREAM_BLUETOOTH_SCO */
-#define SL_ANDROID_STREAM_BLUETOOTH_SCO ((SLint32) 0x00000006)
-/*      same as android.media.AudioManager.STREAM_SYSTEM_ENFORCED */
-#define SL_ANDROID_STREAM_SYSTEM_ENFORCED ((SLint32) 0x00000007)
+
+
+/*---------------------------------------------------------------------------*/
+/* Android AudioPlayer and AudioRecorder configuration                       */
+/*---------------------------------------------------------------------------*/
+
+/** Audio Performance mode.
+ * Performance mode tells the framework how to configure the audio path
+ * for a player or recorder according to application performance and
+ * functional requirements.
+ * It affects the output or input latency based on acceptable tradeoffs on
+ * battery drain and use of pre or post processing effects.
+ * Performance mode should be set before realizing the object and should be
+ * read after realizing the object to check if the requested mode could be
+ * granted or not.
+ */
+/** Audio Performance mode key */
+#define SL_ANDROID_KEY_PERFORMANCE_MODE ((const SLchar*) "androidPerformanceMode")
+
+/** Audio performance values */
+/*      No specific performance requirement. Allows HW and SW pre/post processing. */
+#define SL_ANDROID_PERFORMANCE_NONE ((SLuint32) 0x00000000)
+/*      Priority given to latency. No HW or software pre/post processing.
+ *      This is the default if no performance mode is specified. */
+#define SL_ANDROID_PERFORMANCE_LATENCY ((SLuint32) 0x00000001)
+/*      Priority given to latency while still allowing HW pre and post processing. */
+#define SL_ANDROID_PERFORMANCE_LATENCY_EFFECTS ((SLuint32) 0x00000002)
+/*      Priority given to power saving if latency is not a concern.
+ *      Allows HW and SW pre/post processing. */
+#define SL_ANDROID_PERFORMANCE_POWER_SAVING ((SLuint32) 0x00000003)
 
 #endif /* OPENSL_ES_ANDROIDCONFIGURATION_H_ */
--- a/media/libcubeb/src/cubeb_audiounit.cpp
+++ b/media/libcubeb/src/cubeb_audiounit.cpp
@@ -241,18 +241,18 @@ struct cubeb_stream {
   /* Latency requested by the user. */
   uint32_t latency_frames = 0;
   atomic<uint32_t> current_latency_frames{ 0 };
   atomic<float> panning{ 0 };
   unique_ptr<cubeb_resampler, decltype(&cubeb_resampler_destroy)> resampler;
   /* This is true if a device change callback is currently running.  */
   atomic<bool> switching_device{ false };
   atomic<bool> buffer_size_change_state{ false };
-  AudioDeviceID aggregate_device_id = 0;    // the aggregate device id
-  AudioObjectID plugin_id = 0;              // used to create aggregate device
+  AudioDeviceID aggregate_device_id = kAudioObjectUnknown;  // the aggregate device id
+  AudioObjectID plugin_id = kAudioObjectUnknown;            // used to create aggregate device
   /* Mixer interface */
   unique_ptr<cubeb_mixer, decltype(&cubeb_mixer_destroy)> mixer;
   /* Buffer where remixing/resampling will occur when upmixing is required */
   /* Only accessed from callback thread */
   unique_ptr<uint8_t[]> temp_buffer;
   size_t temp_buffer_size = 0; // size in bytes.
   /* Listeners indicating what system events are monitored. */
   unique_ptr<property_listener> default_input_listener;
@@ -811,41 +811,41 @@ audiounit_reinit_stream(cubeb_stream * s
     audiounit_close_stream(stm);
 
     /* Reinit occurs in one of the following case:
      * - When the device is not alive any more
      * - When the default system device change.
      * - The bluetooth device changed from A2DP to/from HFP/HSP profile
      * We first attempt to re-use the same device id, should that fail we will
      * default to the (potentially new) default device. */
-    AudioDeviceID input_device = flags & DEV_INPUT ? stm->input_device.id : 0;
+    AudioDeviceID input_device = flags & DEV_INPUT ? stm->input_device.id : kAudioObjectUnknown;
     if (flags & DEV_INPUT) {
       r = audiounit_set_device_info(stm, input_device, io_side::INPUT);
       if (r != CUBEB_OK) {
         LOG("(%p) Set input device info failed. This can happen when last media device is unplugged", stm);
         return CUBEB_ERROR;
       }
     }
 
     /* Always use the default output on reinit. This is not correct in every
      * case but it is sufficient for Firefox and prevent reinit from reporting
      * failures. It will change soon when reinit mechanism will be updated. */
-    r = audiounit_set_device_info(stm, 0, io_side::OUTPUT);
+    r = audiounit_set_device_info(stm, kAudioObjectUnknown, io_side::OUTPUT);
     if (r != CUBEB_OK) {
       LOG("(%p) Set output device info failed. This can happen when last media device is unplugged", stm);
       return CUBEB_ERROR;
     }
 
     if (audiounit_setup_stream(stm) != CUBEB_OK) {
       LOG("(%p) Stream reinit failed.", stm);
-      if (flags & DEV_INPUT && input_device != 0) {
+      if (flags & DEV_INPUT && input_device != kAudioObjectUnknown) {
         // Attempt to re-use the same device-id failed, so attempt again with
         // default input device.
         audiounit_close_stream(stm);
-        if (audiounit_set_device_info(stm, 0, io_side::INPUT) != CUBEB_OK ||
+        if (audiounit_set_device_info(stm, kAudioObjectUnknown, io_side::INPUT) != CUBEB_OK ||
             audiounit_setup_stream(stm) != CUBEB_OK) {
           LOG("(%p) Second stream reinit failed.", stm);
           return CUBEB_ERROR;
         }
       }
     }
 
     if (vol_rv == CUBEB_OK) {
@@ -863,17 +863,17 @@ audiounit_reinit_stream(cubeb_stream * s
   return CUBEB_OK;
 }
 
 static void
 audiounit_reinit_stream_async(cubeb_stream * stm, device_flags_value flags)
 {
   if (std::atomic_exchange(&stm->reinit_pending, true)) {
     // A reinit task is already pending, nothing more to do.
-    ALOG("(%p) re-init stream task already pending, cancelling request ", stm);
+    ALOG("(%p) re-init stream task already pending, cancelling request", stm);
     return;
   }
 
   // Use a new thread, through the queue, to avoid deadlock when calling
   // Get/SetProperties method from inside notify callback
   dispatch_async(stm->context->serial_queue, ^() {
     if (stm->destroy_pending) {
       ALOG("(%p) stream pending destroy, cancelling reinit task", stm);
@@ -939,17 +939,17 @@ audiounit_property_listener_callback(Aud
           if (stm->input_device.flags & DEV_SYSTEM_DEFAULT) {
             LOG("It's the default input device, ignore the event");
             stm->switching_device = false;
             return noErr;
           }
         }
         break;
       case kAudioDevicePropertyDataSource: {
-          LOG("Event[%u] - mSelector == kAudioHardwarePropertyDataSource for id=%d", (unsigned int) i, id);
+          LOG("Event[%u] - mSelector == kAudioDevicePropertyDataSource for id=%d", (unsigned int) i, id);
         }
         break;
       default:
         LOG("Event[%u] - mSelector == Unexpected Event id %d, return", (unsigned int) i, addresses[i].mSelector);
         stm->switching_device = false;
         return noErr;
     }
   }
@@ -1309,19 +1309,19 @@ audiounit_get_preferred_sample_rate(cube
   *rate = static_cast<uint32_t>(fsamplerate);
 #endif
   return CUBEB_OK;
 }
 
 static cubeb_channel_layout
 audiounit_convert_channel_layout(AudioChannelLayout * layout)
 {
-  // When having on or two channel, force mono or stereo. Some devices (namely,
-  //  Bose QC35, mark 1 and 2), expose a single channel mapped to the right for
-  //  some reason.
+  // When having one or two channel, force mono or stereo. Some devices (namely,
+  // Bose QC35, mark 1 and 2), expose a single channel mapped to the right for
+  // some reason.
   if (layout->mNumberChannelDescriptions == 1) {
     return CUBEB_LAYOUT_MONO;
   } else if (layout->mNumberChannelDescriptions == 2) {
     return CUBEB_LAYOUT_STEREO;
   }
 
   if (layout->mChannelLayoutTag != kAudioChannelLayoutTag_UseChannelDescriptions) {
     // kAudioChannelLayoutTag_UseChannelBitmap
@@ -1606,17 +1606,17 @@ audiounit_create_blank_aggregate_device(
                                                           kAudioObjectPropertyScopeGlobal,
                                                           kAudioObjectPropertyElementMaster };
   UInt32 size = 0;
   OSStatus r = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject,
                                               &address_plugin_bundle_id,
                                               0, NULL,
                                               &size);
   if (r != noErr) {
-    LOG("AudioHardwareGetPropertyInfo/kAudioHardwarePropertyPlugInForBundleID, rv=%d", r);
+    LOG("AudioObjectGetPropertyDataSize/kAudioHardwarePropertyPlugInForBundleID, rv=%d", r);
     return CUBEB_ERROR;
   }
 
   AudioValueTranslation translation_value;
   CFStringRef in_bundle_ref = CFSTR("com.apple.audio.CoreAudio");
   translation_value.mInputData = &in_bundle_ref;
   translation_value.mInputDataSize = sizeof(in_bundle_ref);
   translation_value.mOutputData = plugin_id;
@@ -1624,17 +1624,17 @@ audiounit_create_blank_aggregate_device(
 
   r = AudioObjectGetPropertyData(kAudioObjectSystemObject,
                                  &address_plugin_bundle_id,
                                  0,
                                  nullptr,
                                  &size,
                                  &translation_value);
   if (r != noErr) {
-    LOG("AudioHardwareGetProperty/kAudioHardwarePropertyPlugInForBundleID, rv=%d", r);
+    LOG("AudioObjectGetPropertyData/kAudioHardwarePropertyPlugInForBundleID, rv=%d", r);
     return CUBEB_ERROR;
   }
 
   AudioObjectPropertyAddress create_aggregate_device_address = { kAudioPlugInCreateAggregateDevice,
                                                                  kAudioObjectPropertyScopeGlobal,
                                                                  kAudioObjectPropertyElementMaster };
   r = AudioObjectGetPropertyDataSize(*plugin_id,
                                      &create_aggregate_device_address,
@@ -1750,17 +1750,17 @@ audiounit_set_aggregate_sub_device_list(
   }
 
   return CUBEB_OK;
 }
 
 static int
 audiounit_set_master_aggregate_device(const AudioDeviceID aggregate_device_id)
 {
-  assert(aggregate_device_id);
+  assert(aggregate_device_id != kAudioObjectUnknown);
   AudioObjectPropertyAddress master_aggregate_sub_device =  { kAudioAggregateDevicePropertyMasterSubDevice,
                                                               kAudioObjectPropertyScopeGlobal,
                                                               kAudioObjectPropertyElementMaster };
 
   // Master become the 1st output sub device
   AudioDeviceID output_device_id = audiounit_get_default_device_id(CUBEB_DEVICE_TYPE_OUTPUT);
   const vector<AudioDeviceID> output_sub_devices = audiounit_get_sub_devices(output_device_id);
   CFStringRef master_sub_device = get_device_name(output_sub_devices[0]);
@@ -1781,17 +1781,17 @@ audiounit_set_master_aggregate_device(co
   }
 
   return CUBEB_OK;
 }
 
 static int
 audiounit_activate_clock_drift_compensation(const AudioDeviceID aggregate_device_id)
 {
-  assert(aggregate_device_id);
+  assert(aggregate_device_id != kAudioObjectUnknown);
   AudioObjectPropertyAddress address_owned = { kAudioObjectPropertyOwnedObjects,
                                                kAudioObjectPropertyScopeGlobal,
                                                kAudioObjectPropertyElementMaster };
 
   UInt32 qualifier_data_size = sizeof(AudioObjectID);
   AudioClassID class_id = kAudioSubDeviceClassID;
   void * qualifier_data = &class_id;
   UInt32 size = 0;
@@ -1978,17 +1978,17 @@ audiounit_destroy_aggregate_device(Audio
                                   &size,
                                   aggregate_device_id);
   if (rv != noErr) {
     LOG("AudioObjectGetPropertyData/kAudioPlugInDestroyAggregateDevice, rv=%d", rv);
     return CUBEB_ERROR;
   }
 
   LOG("Destroyed aggregate device %d", *aggregate_device_id);
-  *aggregate_device_id = 0;
+  *aggregate_device_id = kAudioObjectUnknown;
   return CUBEB_OK;
 }
 
 static int
 audiounit_new_unit_instance(AudioUnit * unit, device_info * device)
 {
   AudioComponentDescription desc;
   AudioComponent comp;
@@ -2067,33 +2067,33 @@ audiounit_create_unit(AudioUnit * unit, 
       (device->flags & DEV_OUTPUT)) {
     return CUBEB_OK;
   }
 
 
   if (device->flags & DEV_INPUT) {
     r = audiounit_enable_unit_scope(unit, io_side::INPUT, ENABLE);
     if (r != CUBEB_OK) {
-      LOG("Failed to enable audiounit input scope ");
+      LOG("Failed to enable audiounit input scope");
       return r;
     }
     r = audiounit_enable_unit_scope(unit, io_side::OUTPUT, DISABLE);
     if (r != CUBEB_OK) {
-      LOG("Failed to disable audiounit output scope ");
+      LOG("Failed to disable audiounit output scope");
       return r;
     }
   } else if (device->flags & DEV_OUTPUT) {
     r = audiounit_enable_unit_scope(unit, io_side::OUTPUT, ENABLE);
     if (r != CUBEB_OK) {
-      LOG("Failed to enable audiounit output scope ");
+      LOG("Failed to enable audiounit output scope");
       return r;
     }
     r = audiounit_enable_unit_scope(unit, io_side::INPUT, DISABLE);
     if (r != CUBEB_OK) {
-      LOG("Failed to disable audiounit input scope ");
+      LOG("Failed to disable audiounit input scope");
       return r;
     }
   } else {
     assert(false);
   }
 
   rv = AudioUnitSetProperty(*unit,
                             kAudioOutputUnitProperty_CurrentDevice,
@@ -2552,17 +2552,17 @@ audiounit_setup_stream(cubeb_stream * st
 
   device_info in_dev_info = stm->input_device;
   device_info out_dev_info = stm->output_device;
 
   if (has_input(stm) && has_output(stm) &&
       stm->input_device.id != stm->output_device.id) {
     r = audiounit_create_aggregate_device(stm);
     if (r != CUBEB_OK) {
-      stm->aggregate_device_id = 0;
+      stm->aggregate_device_id = kAudioObjectUnknown;
       LOG("(%p) Create aggregate devices failed.", stm);
       // !!!NOTE: It is not necessary to return here. If it does not
       // return it will fallback to the old implementation. The intention
       // is to investigate how often it fails. I plan to remove
       // it after a couple of weeks.
       return r;
     } else {
       in_dev_info.id = out_dev_info.id = stm->aggregate_device_id;
@@ -2583,26 +2583,26 @@ audiounit_setup_stream(cubeb_stream * st
     r = audiounit_create_unit(&stm->output_unit, &out_dev_info);
     if (r != CUBEB_OK) {
       LOG("(%p) AudioUnit creation for output failed.", stm);
       return r;
     }
   }
 
   /* Latency cannot change if another stream is operating in parallel. In this case
-  * latecy is set to the other stream value. */
+   * latency is set to the other stream value. */
   if (audiounit_active_streams(stm->context) > 1) {
     LOG("(%p) More than one active stream, use global latency.", stm);
     stm->latency_frames = stm->context->global_latency_frames;
   } else {
     /* Silently clamp the latency down to the platform default, because we
-    * synthetize the clock from the callbacks, and we want the clock to update
-    * often. */
+     * synthetize the clock from the callbacks, and we want the clock to update
+     * often. */
     stm->latency_frames = audiounit_clamp_latency(stm, stm->latency_frames);
-    assert(stm->latency_frames); // Ungly error check
+    assert(stm->latency_frames); // Ugly error check
     audiounit_set_global_latency(stm->context, stm->latency_frames);
   }
 
   /* Configure I/O stream */
   if (has_input(stm)) {
     r = audiounit_configure_input(stm);
     if (r != CUBEB_OK) {
       LOG("(%p) Configure audiounit input failed.", stm);
@@ -2835,19 +2835,19 @@ audiounit_close_stream(cubeb_stream *stm
     AudioUnitUninitialize(stm->output_unit);
     AudioComponentInstanceDispose(stm->output_unit);
     stm->output_unit = nullptr;
   }
 
   stm->resampler.reset();
   stm->mixer.reset();
 
-  if (stm->aggregate_device_id) {
+  if (stm->aggregate_device_id != kAudioObjectUnknown) {
     audiounit_destroy_aggregate_device(stm->plugin_id, &stm->aggregate_device_id);
-    stm->aggregate_device_id = 0;
+    stm->aggregate_device_id = kAudioObjectUnknown;
   }
 }
 
 static void
 audiounit_stream_destroy_internal(cubeb_stream *stm)
 {
   stm->context->mutex.assert_current_thread_owns();
 
--- a/media/libcubeb/src/cubeb_opensl.c
+++ b/media/libcubeb/src/cubeb_opensl.c
@@ -52,22 +52,29 @@
     LOG_TS("Error: %s (%s %s:%d) - %s", msg, __FUNCTION__, FILENAME, __LINE__);\
   }                                                   \
 } while(0)
 #else
 #define TIMESTAMP(...)
 #endif
 
 #define ANDROID_VERSION_GINGERBREAD_MR1 10
+#define ANDROID_VERSION_JELLY_BEAN 18
 #define ANDROID_VERSION_LOLLIPOP 21
 #define ANDROID_VERSION_MARSHMALLOW 23
+#define ANDROID_VERSION_N_MR1 25
 #endif
 
 #define DEFAULT_SAMPLE_RATE 48000
 #define DEFAULT_NUM_OF_FRAMES 480
+// If the latency requested is above this threshold, this stream is considered
+// intended for playback (vs. real-time). Tell Android it should favor saving
+// power over performance or latency.
+// This is around 100ms at 44100 or 48000
+#define POWERSAVE_LATENCY_FRAMES_THRESHOLD 4000
 
 static struct cubeb_ops const opensl_ops;
 
 struct cubeb {
   struct cubeb_ops const * ops;
   void * lib;
   SLInterfaceID SL_IID_BUFFERQUEUE;
   SLInterfaceID SL_IID_PLAY;
@@ -79,17 +86,17 @@ struct cubeb {
   SLInterfaceID SL_IID_RECORD;
   SLObjectItf engObj;
   SLEngineItf eng;
   SLObjectItf outmixObj;
   output_latency_function * p_output_latency_function;
 };
 
 #define NELEMS(A) (sizeof(A) / sizeof A[0])
-#define NBUFS 4
+#define NBUFS 2
 
 struct cubeb_stream {
   /* Note: Must match cubeb_stream layout in cubeb.c. */
   cubeb * context;
   void * user_ptr;
   /**/
   pthread_mutex_t mutex;
   SLObjectItf playerObj;
@@ -150,20 +157,23 @@ struct cubeb_stream {
   /* Store user callback. */
   cubeb_data_callback data_callback;
   /* Store state callback. */
   cubeb_state_callback state_callback;
 
   cubeb_resampler * resampler;
   unsigned int user_output_rate;
   unsigned int output_configured_rate;
-  unsigned int latency_frames;
+  unsigned int buffer_size_frames;
+  // Audio output latency used in cubeb_stream_get_position().
+  unsigned int output_latency_ms;
   int64_t lastPosition;
   int64_t lastPositionTimeStamp;
   int64_t lastCompensativePosition;
+  int voice;
 };
 
 /* Forward declaration. */
 static int opensl_stop_player(cubeb_stream * stm);
 static int opensl_stop_recorder(cubeb_stream * stm);
 
 static int
 opensl_get_draining(cubeb_stream * stm)
@@ -841,26 +851,27 @@ opensl_configure_capture(cubeb_stream * 
   lDataLocatorIn.deviceType = SL_IODEVICE_AUDIOINPUT;
   lDataLocatorIn.deviceID = SL_DEFAULTDEVICEID_AUDIOINPUT;
   lDataLocatorIn.device = NULL;
 
   SLDataSource lDataSource;
   lDataSource.pLocator = &lDataLocatorIn;
   lDataSource.pFormat = NULL;
 
-  const SLuint32 lSoundRecorderIIDCount = 2;
   const SLInterfaceID lSoundRecorderIIDs[] = { stm->context->SL_IID_RECORD,
-                                               stm->context->SL_IID_ANDROIDSIMPLEBUFFERQUEUE };
-  const SLboolean lSoundRecorderReqs[] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE };
+                                               stm->context->SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
+                                               stm->context->SL_IID_ANDROIDCONFIGURATION };
+
+  const SLboolean lSoundRecorderReqs[] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE };
   // create the audio recorder abstract object
   SLresult res = (*stm->context->eng)->CreateAudioRecorder(stm->context->eng,
                                                            &stm->recorderObj,
                                                            &lDataSource,
                                                            &lDataSink,
-                                                           lSoundRecorderIIDCount,
+                                                           NELEMS(lSoundRecorderIIDs),
                                                            lSoundRecorderIIDs,
                                                            lSoundRecorderReqs);
   // Sample rate not supported. Try again with default sample rate!
   if (res == SL_RESULT_CONTENT_UNSUPPORTED) {
     if (stm->output_enabled && stm->output_configured_rate != 0) {
       // Set the same with the player. Since there is no
       // api for input device this is a safe choice.
       stm->input_device_rate = stm->output_configured_rate;
@@ -869,26 +880,56 @@ opensl_configure_capture(cubeb_stream * 
       // The default rate expected to be supported from all android devices.
       stm->input_device_rate = DEFAULT_SAMPLE_RATE;
     }
     lDataFormat.samplesPerSec = stm->input_device_rate * 1000;
     res = (*stm->context->eng)->CreateAudioRecorder(stm->context->eng,
                                                     &stm->recorderObj,
                                                     &lDataSource,
                                                     &lDataSink,
-                                                    lSoundRecorderIIDCount,
+                                                    NELEMS(lSoundRecorderIIDs),
                                                     lSoundRecorderIIDs,
                                                     lSoundRecorderReqs);
 
     if (res != SL_RESULT_SUCCESS) {
       LOG("Failed to create recorder. Error code: %lu", res);
       return CUBEB_ERROR;
     }
   }
 
+
+  if (get_android_version() > ANDROID_VERSION_JELLY_BEAN) {
+    SLAndroidConfigurationItf recorderConfig;
+    res = (*stm->recorderObj)
+              ->GetInterface(stm->recorderObj,
+                             stm->context->SL_IID_ANDROIDCONFIGURATION,
+                             &recorderConfig);
+
+    if (res != SL_RESULT_SUCCESS) {
+      LOG("Failed to get the android configuration interface for recorder. Error "
+          "code: %lu",
+          res);
+      return CUBEB_ERROR;
+    }
+
+    // Voice recognition is the lowest latency, according to the docs. Camcorder
+    // uses a microphone that is in the same direction as the camera.
+    SLint32 streamType = stm->voice ? SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION
+                                    : SL_ANDROID_RECORDING_PRESET_CAMCORDER;
+
+    res = (*recorderConfig)
+              ->SetConfiguration(recorderConfig, SL_ANDROID_KEY_RECORDING_PRESET,
+                                 &streamType, sizeof(SLint32));
+
+    if (res != SL_RESULT_SUCCESS) {
+      LOG("Failed to set the android configuration to VOICE for the recorder. "
+          "Error code: %lu", res);
+      return CUBEB_ERROR;
+    }
+  }
   // realize the audio recorder
   res = (*stm->recorderObj)->Realize(stm->recorderObj, SL_BOOLEAN_FALSE);
   if (res != SL_RESULT_SUCCESS) {
     LOG("Failed to realize recorder. Error code: %lu", res);
     return CUBEB_ERROR;
   }
   // get the record interface
   res = (*stm->recorderObj)->GetInterface(stm->recorderObj,
@@ -932,17 +973,17 @@ opensl_configure_capture(cubeb_stream * 
                                                          stm);
   if (res != SL_RESULT_SUCCESS) {
     LOG("Failed to register recorder buffer queue callback. Error code: %lu", res);
     return CUBEB_ERROR;
   }
 
   // Calculate length of input buffer according to requested latency
   stm->input_frame_size = params->channels * sizeof(int16_t);
-  stm->input_buffer_length = (stm->input_frame_size * stm->latency_frames);
+  stm->input_buffer_length = (stm->input_frame_size * stm->buffer_size_frames);
 
   // Calculate the capacity of input array
   stm->input_array_capacity = NBUFS;
   if (stm->output_enabled) {
     // Full duplex, update capacity to hold 1 sec of data
     stm->input_array_capacity = 1 * stm->input_device_rate / stm->input_buffer_length;
   }
   // Allocate input array
@@ -1043,37 +1084,116 @@ opensl_configure_playback(cubeb_stream *
 
   if (res != SL_RESULT_SUCCESS) {
     LOG("Failed to create audio player. Error code: %lu", res);
     return CUBEB_ERROR;
   }
 
   stm->output_configured_rate = preferred_sampling_rate;
   stm->bytespersec = stm->output_configured_rate * stm->framesize;
-  stm->queuebuf_len = stm->framesize * stm->latency_frames;
+  stm->queuebuf_len = stm->framesize * stm->buffer_size_frames;
 
   // Calculate the capacity of input array
   stm->queuebuf_capacity = NBUFS;
   if (stm->output_enabled) {
     // Full duplex, update capacity to hold 1 sec of data
     stm->queuebuf_capacity = 1 * stm->output_configured_rate / stm->queuebuf_len;
   }
   // Allocate input array
   stm->queuebuf = (void**)calloc(1, sizeof(void*) * stm->queuebuf_capacity);
   for (uint32_t i = 0; i < stm->queuebuf_capacity; ++i) {
     stm->queuebuf[i] = calloc(1, stm->queuebuf_len);
     assert(stm->queuebuf[i]);
   }
 
+  SLAndroidConfigurationItf playerConfig = NULL;
+
+  if (get_android_version() >= ANDROID_VERSION_N_MR1) {
+    res = (*stm->playerObj)
+              ->GetInterface(stm->playerObj,
+                             stm->context->SL_IID_ANDROIDCONFIGURATION,
+                             &playerConfig);
+    if (res != SL_RESULT_SUCCESS) {
+      LOG("Failed to get Android configuration interface. Error code: %lu", res);
+      return CUBEB_ERROR;
+    }
+
+    SLint32 streamType = SL_ANDROID_STREAM_MEDIA;
+    if (stm->voice) {
+      streamType = SL_ANDROID_STREAM_VOICE;
+    }
+    res = (*playerConfig)->SetConfiguration(playerConfig,
+                                            SL_ANDROID_KEY_STREAM_TYPE,
+                                            &streamType,
+                                            sizeof(streamType));
+    if (res != SL_RESULT_SUCCESS) {
+      LOG("Failed to set Android configuration to %d Error code: %lu",
+          streamType, res);
+    }
+
+    SLuint32 performanceMode = SL_ANDROID_PERFORMANCE_LATENCY;
+    if (stm->buffer_size_frames > POWERSAVE_LATENCY_FRAMES_THRESHOLD) {
+      performanceMode = SL_ANDROID_PERFORMANCE_POWER_SAVING;
+    }
+
+    res = (*playerConfig)->SetConfiguration(playerConfig,
+                                            SL_ANDROID_KEY_PERFORMANCE_MODE,
+                                            &performanceMode,
+                                            sizeof(performanceMode));
+    if (res != SL_RESULT_SUCCESS) {
+      LOG("Failed to set Android performance mode to %d Error code: %lu. This is"
+          " not fatal", performanceMode, res);
+    }
+  }
+
   res = (*stm->playerObj)->Realize(stm->playerObj, SL_BOOLEAN_FALSE);
   if (res != SL_RESULT_SUCCESS) {
     LOG("Failed to realize player object. Error code: %lu", res);
     return CUBEB_ERROR;
   }
 
+  // There are two ways of getting the audio output latency:
+  // - a configuration value, only available on some devices (notably devices
+  // running FireOS)
+  // - A Java method, that we call using JNI.
+  //
+  // The first method is prefered, if available, because it can account for more
+  // latency causes, and is more precise.
+
+  // Latency has to be queried after the realization of the interface, when
+  // using SL_IID_ANDROIDCONFIGURATION.
+  SLuint32 audioLatency = 0;
+  SLuint32 paramSize = sizeof(SLuint32);
+  // The reported latency is in milliseconds.
+  if (playerConfig) {
+    res = (*playerConfig)->GetConfiguration(playerConfig,
+                                            (const SLchar *)"androidGetAudioLatency",
+                                            &paramSize,
+                                            &audioLatency);
+    if (res == SL_RESULT_SUCCESS) {
+      LOG("Got playback latency using android configuration extension");
+      stm->output_latency_ms = audioLatency;
+    }
+  }
+  // `playerConfig` is available, but the above failed, or `playerConfig` is not
+  // available. In both cases, we need to acquire the output latency by an other
+  // mean.
+  if ((playerConfig && res != SL_RESULT_SUCCESS) ||
+      !playerConfig) {
+    if (cubeb_output_latency_method_is_loaded(stm->context->p_output_latency_function)) {
+      LOG("Got playback latency using JNI");
+      stm->output_latency_ms = cubeb_get_output_latency(stm->context->p_output_latency_function);
+    } else {
+      LOG("No alternate latency querying method loaded, A/V sync will be off.");
+      stm->output_latency_ms = 0;
+    }
+  }
+
+  LOG("Audio output latency: %dms", stm->output_latency_ms);
+
   res = (*stm->playerObj)->GetInterface(stm->playerObj,
                                         stm->context->SL_IID_PLAY,
                                         &stm->play);
   if (res != SL_RESULT_SUCCESS) {
     LOG("Failed to get play interface. Error code: %lu", res);
     return CUBEB_ERROR;
   }
 
@@ -1143,16 +1263,24 @@ opensl_validate_stream_param(cubeb_strea
   if ((stream_params &&
        (stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK))) {
     LOG("Loopback is not supported");
     return CUBEB_ERROR_NOT_SUPPORTED;
   }
   return CUBEB_OK;
 }
 
+int has_pref_set(cubeb_stream_params* input_params,
+                 cubeb_stream_params* output_params,
+                 cubeb_stream_prefs pref)
+{
+  return (input_params && input_params->prefs & pref) ||
+         (output_params && output_params->prefs & pref);
+}
+
 static int
 opensl_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name,
                    cubeb_devid input_device,
                    cubeb_stream_params * input_stream_params,
                    cubeb_devid output_device,
                    cubeb_stream_params * output_stream_params,
                    unsigned int latency_frames,
                    cubeb_data_callback data_callback, cubeb_state_callback state_callback,
@@ -1180,46 +1308,50 @@ opensl_stream_init(cubeb * ctx, cubeb_st
 
   stm = calloc(1, sizeof(*stm));
   assert(stm);
 
   stm->context = ctx;
   stm->data_callback = data_callback;
   stm->state_callback = state_callback;
   stm->user_ptr = user_ptr;
-  stm->latency_frames = latency_frames ? latency_frames : DEFAULT_NUM_OF_FRAMES;
+  stm->buffer_size_frames = latency_frames ? latency_frames : DEFAULT_NUM_OF_FRAMES;
   stm->input_enabled = (input_stream_params) ? 1 : 0;
   stm->output_enabled = (output_stream_params) ? 1 : 0;
   stm->shutdown = 1;
+  stm->voice = has_pref_set(input_stream_params, output_stream_params, CUBEB_STREAM_PREF_VOICE);
+
+  LOG("cubeb stream prefs: voice: %s", stm->voice ? "true" : "false");
+
 
 #ifdef DEBUG
   pthread_mutexattr_t attr;
   pthread_mutexattr_init(&attr);
   pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
   r = pthread_mutex_init(&stm->mutex, &attr);
 #else
   r = pthread_mutex_init(&stm->mutex, NULL);
 #endif
   assert(r == 0);
 
   if (output_stream_params) {
     LOG("Playback params: Rate %d, channels %d, format %d, latency in frames %d.",
         output_stream_params->rate, output_stream_params->channels,
-        output_stream_params->format, stm->latency_frames);
+        output_stream_params->format, stm->buffer_size_frames);
     r = opensl_configure_playback(stm, output_stream_params);
     if (r != CUBEB_OK) {
       opensl_stream_destroy(stm);
       return r;
     }
   }
 
   if (input_stream_params) {
     LOG("Capture params: Rate %d, channels %d, format %d, latency in frames %d.",
         input_stream_params->rate, input_stream_params->channels,
-        input_stream_params->format, stm->latency_frames);
+        input_stream_params->format, stm->buffer_size_frames);
     r = opensl_configure_capture(stm, input_stream_params);
     if (r != CUBEB_OK) {
       opensl_stream_destroy(stm);
       return r;
     }
   }
 
   /* Configure resampler*/
@@ -1447,51 +1579,47 @@ opensl_stream_destroy(cubeb_stream * stm
 
 static int
 opensl_stream_get_position(cubeb_stream * stm, uint64_t * position)
 {
   SLmillisecond msec;
   uint32_t compensation_msec = 0;
   SLresult res;
 
-  if (!cubeb_output_latency_method_is_loaded(stm->context->p_output_latency_function)) {
-    return CUBEB_ERROR_NOT_SUPPORTED;
-  }
-
   res = (*stm->play)->GetPosition(stm->play, &msec);
   if (res != SL_RESULT_SUCCESS)
     return CUBEB_ERROR;
 
   struct timespec t;
   clock_gettime(CLOCK_MONOTONIC, &t);
   if(stm->lastPosition == msec) {
     compensation_msec =
       (t.tv_sec*1000000000LL + t.tv_nsec - stm->lastPositionTimeStamp) / 1000000;
   } else {
     stm->lastPositionTimeStamp = t.tv_sec*1000000000LL + t.tv_nsec;
     stm->lastPosition = msec;
   }
 
   uint64_t samplerate = stm->user_output_rate;
-  uint32_t mixer_latency = cubeb_get_output_latency(stm->context->p_output_latency_function);
+  uint32_t output_latency = stm->output_latency_ms;
 
   pthread_mutex_lock(&stm->mutex);
   int64_t maximum_position = stm->written * (int64_t)stm->user_output_rate / stm->output_configured_rate;
   pthread_mutex_unlock(&stm->mutex);
   assert(maximum_position >= 0);
 
-  if (msec > mixer_latency) {
+  if (msec > output_latency) {
     int64_t unadjusted_position;
     if (stm->lastCompensativePosition > msec + compensation_msec) {
       // Over compensation, use lastCompensativePosition.
       unadjusted_position =
-        samplerate * (stm->lastCompensativePosition - mixer_latency) / 1000;
+        samplerate * (stm->lastCompensativePosition - output_latency) / 1000;
     } else {
       unadjusted_position =
-        samplerate * (msec - mixer_latency + compensation_msec) / 1000;
+        samplerate * (msec - output_latency + compensation_msec) / 1000;
       stm->lastCompensativePosition = msec + compensation_msec;
     }
     *position = unadjusted_position < maximum_position ?
       unadjusted_position : maximum_position;
   } else {
     *position = 0;
   }
   return CUBEB_OK;
--- a/media/libcubeb/src/cubeb_wasapi.cpp
+++ b/media/libcubeb/src/cubeb_wasapi.cpp
@@ -136,16 +136,17 @@ private:
 };
 
 extern cubeb_ops const wasapi_ops;
 
 int wasapi_stream_stop(cubeb_stream * stm);
 int wasapi_stream_start(cubeb_stream * stm);
 void close_wasapi_stream(cubeb_stream * stm);
 int setup_wasapi_stream(cubeb_stream * stm);
+ERole pref_to_role(cubeb_stream_prefs param);
 static char const * wstr_to_utf8(wchar_t const * str);
 static std::unique_ptr<wchar_t const []> utf8_to_wstr(char const * str);
 
 }
 
 class wasapi_collection_notification_client;
 class monitor_device_notifications;
 
@@ -186,16 +187,18 @@ struct cubeb_stream {
      samplerate/channel layout, as WASAPI does not resample nor upmix
      itself. */
   cubeb_stream_params input_mix_params = { CUBEB_SAMPLE_FLOAT32NE, 0, 0, CUBEB_LAYOUT_UNDEFINED, CUBEB_STREAM_PREF_NONE };
   cubeb_stream_params output_mix_params = { CUBEB_SAMPLE_FLOAT32NE, 0, 0, CUBEB_LAYOUT_UNDEFINED, CUBEB_STREAM_PREF_NONE };
   /* Stream parameters. This is what the client requested,
    * and what will be presented in the callback. */
   cubeb_stream_params input_stream_params = { CUBEB_SAMPLE_FLOAT32NE, 0, 0, CUBEB_LAYOUT_UNDEFINED, CUBEB_STREAM_PREF_NONE };
   cubeb_stream_params output_stream_params = { CUBEB_SAMPLE_FLOAT32NE, 0, 0, CUBEB_LAYOUT_UNDEFINED, CUBEB_STREAM_PREF_NONE };
+  /* A MMDevice role for this stream: either communication or console here. */
+  ERole role;
   /* The input and output device, or NULL for default. */
   std::unique_ptr<const wchar_t[]> input_device;
   std::unique_ptr<const wchar_t[]> output_device;
   /* The latency initially requested for this stream, in frames. */
   unsigned latency = 0;
   cubeb_state_callback state_callback = nullptr;
   cubeb_data_callback data_callback = nullptr;
   wasapi_refill_callback refill_callback = nullptr;
@@ -560,31 +563,32 @@ public:
       *ppvInterface = (IMMNotificationClient*)this;
     } else {
       *ppvInterface = NULL;
       return E_NOINTERFACE;
     }
     return S_OK;
   }
 
-  wasapi_endpoint_notification_client(HANDLE event)
+  wasapi_endpoint_notification_client(HANDLE event, ERole role)
     : ref_count(1)
     , reconfigure_event(event)
+    , role(role)
   { }
 
   virtual ~wasapi_endpoint_notification_client()
   { }
 
   HRESULT STDMETHODCALLTYPE
   OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR device_id)
   {
     LOG("endpoint: Audio device default changed.");
 
     /* we only support a single stream type for now. */
-    if (flow != eRender && role != eConsole) {
+    if (flow != eRender && role != this->role) {
       return S_OK;
     }
 
     BOOL ok = SetEvent(reconfigure_event);
     if (!ok) {
       LOG("endpoint: SetEvent on reconfigure_event failed: %lx", GetLastError());
     }
 
@@ -617,16 +621,17 @@ public:
   {
     //Audio device property value changed.
     return S_OK;
   }
 private:
   /* refcount for this instance, necessary to implement MSCOM semantics. */
   LONG ref_count;
   HANDLE reconfigure_event;
+  ERole role;
 };
 
 namespace {
 
 char const *
 intern_device_id(cubeb * ctx, wchar_t const * id)
 {
   XASSERT(id);
@@ -1251,17 +1256,17 @@ HRESULT register_notification_client(cub
   HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
                                 NULL, CLSCTX_INPROC_SERVER,
                                 IID_PPV_ARGS(stm->device_enumerator.receive()));
   if (FAILED(hr)) {
     LOG("Could not get device enumerator: %lx", hr);
     return hr;
   }
 
-  stm->notification_client.reset(new wasapi_endpoint_notification_client(stm->reconfigure_event));
+  stm->notification_client.reset(new wasapi_endpoint_notification_client(stm->reconfigure_event, stm->role));
 
   hr = stm->device_enumerator->RegisterEndpointNotificationCallback(stm->notification_client.get());
   if (FAILED(hr)) {
     LOG("Could not register endpoint notification callback: %lx", hr);
     stm->notification_client = nullptr;
     stm->device_enumerator = nullptr;
   }
 
@@ -1346,27 +1351,27 @@ HRESULT unregister_collection_notificati
 
   context->collection_notification_client = nullptr;
   context->device_collection_enumerator = nullptr;
 
   context->monitor_notifications.reset();
   return hr;
 }
 
-HRESULT get_default_endpoint(com_ptr<IMMDevice> & device, EDataFlow direction)
+HRESULT get_default_endpoint(com_ptr<IMMDevice> & device, EDataFlow direction, ERole role)
 {
   com_ptr<IMMDeviceEnumerator> enumerator;
   HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
                                 NULL, CLSCTX_INPROC_SERVER,
                                 IID_PPV_ARGS(enumerator.receive()));
   if (FAILED(hr)) {
     LOG("Could not get device enumerator: %lx", hr);
     return hr;
   }
-  hr = enumerator->GetDefaultAudioEndpoint(direction, eConsole, device.receive());
+  hr = enumerator->GetDefaultAudioEndpoint(direction, role, device.receive());
   if (FAILED(hr)) {
     LOG("Could not get default audio endpoint: %lx", hr);
     return hr;
   }
 
   return ERROR_SUCCESS;
 }
 
@@ -1442,21 +1447,21 @@ stream_set_volume(cubeb_stream * stm, fl
 
 extern "C" {
 int wasapi_init(cubeb ** context, char const * context_name)
 {
   /* We don't use the device yet, but need to make sure we can initialize one
      so that this backend is not incorrectly enabled on platforms that don't
      support WASAPI. */
   com_ptr<IMMDevice> device;
-  HRESULT hr = get_default_endpoint(device, eRender);
+  HRESULT hr = get_default_endpoint(device, eRender, eConsole);
   if (FAILED(hr)) {
     XASSERT(hr != CO_E_NOTINITIALIZED);
     LOG("It wasn't able to find a default rendering device: %lx", hr);
-    hr = get_default_endpoint(device, eCapture);
+    hr = get_default_endpoint(device, eCapture, eConsole);
     if (FAILED(hr)) {
       LOG("It wasn't able to find a default capture device: %lx", hr);
       return CUBEB_ERROR;
     }
   }
 
   cubeb * ctx = new cubeb();
 
@@ -1545,17 +1550,17 @@ char const * wasapi_get_backend_id(cubeb
 }
 
 int
 wasapi_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
 {
   XASSERT(ctx && max_channels);
 
   com_ptr<IMMDevice> device;
-  HRESULT hr = get_default_endpoint(device, eRender);
+  HRESULT hr = get_default_endpoint(device, eRender, eConsole);
   if (FAILED(hr)) {
     return CUBEB_ERROR;
   }
 
   com_ptr<IAudioClient> client;
   hr = device->Activate(__uuidof(IAudioClient),
                         CLSCTX_INPROC_SERVER,
                         NULL, client.receive_vpp());
@@ -1577,18 +1582,20 @@ wasapi_get_max_channel_count(cubeb * ctx
 
 int
 wasapi_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_frames)
 {
   if (params.format != CUBEB_SAMPLE_FLOAT32NE && params.format != CUBEB_SAMPLE_S16NE) {
     return CUBEB_ERROR_INVALID_FORMAT;
   }
 
+  ERole role = pref_to_role(params.prefs);
+
   com_ptr<IMMDevice> device;
-  HRESULT hr = get_default_endpoint(device, eRender);
+  HRESULT hr = get_default_endpoint(device, eRender, role);
   if (FAILED(hr)) {
     LOG("Could not get default endpoint: %lx", hr);
     return CUBEB_ERROR;
   }
 
   com_ptr<IAudioClient> client;
   hr = device->Activate(__uuidof(IAudioClient),
                         CLSCTX_INPROC_SERVER,
@@ -1618,17 +1625,17 @@ wasapi_get_min_latency(cubeb * ctx, cube
 
   return CUBEB_OK;
 }
 
 int
 wasapi_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
 {
   com_ptr<IMMDevice> device;
-  HRESULT hr = get_default_endpoint(device, eRender);
+  HRESULT hr = get_default_endpoint(device, eRender, eConsole);
   if (FAILED(hr)) {
     return CUBEB_ERROR;
   }
 
   com_ptr<IAudioClient> client;
   hr = device->Activate(__uuidof(IAudioClient),
                         CLSCTX_INPROC_SERVER,
                         NULL, client.receive_vpp());
@@ -1750,17 +1757,17 @@ int setup_wasapi_stream_one_side(cubeb_s
       if (FAILED(hr)) {
         LOG("Could not get %s endpoint, error: %lx\n", DIRECTION_NAME, hr);
         return CUBEB_ERROR;
       }
     } else {
       // If caller has requested loopback but not specified a device, look for
       // the default render device. Otherwise look for the default device
       // appropriate to the direction.
-      hr = get_default_endpoint(device, is_loopback ? eRender : direction);
+      hr = get_default_endpoint(device, is_loopback ? eRender : direction, pref_to_role(stream_params->prefs));
       if (FAILED(hr)) {
         if (is_loopback) {
           LOG("Could not get default render endpoint for loopback, error: %lx\n", hr);
         } else {
           LOG("Could not get default %s endpoint, error: %lx\n", DIRECTION_NAME, hr);
         }
         return CUBEB_ERROR;
       }
@@ -2052,16 +2059,26 @@ int setup_wasapi_stream(cubeb_stream * s
     // Input is up/down mixed when depacketized in get_input_buffer.
     stm->mix_buffer.resize(
       frames_to_bytes_before_mix(stm, stm->output_buffer_frame_count));
   }
 
   return CUBEB_OK;
 }
 
+ERole
+pref_to_role(cubeb_stream_prefs prefs)
+{
+  if (prefs & CUBEB_STREAM_PREF_VOICE) {
+    return eCommunications;
+  }
+
+  return eConsole;
+}
+
 int
 wasapi_stream_init(cubeb * context, cubeb_stream ** stream,
                    char const * stream_name,
                    cubeb_devid input_device,
                    cubeb_stream_params * input_stream_params,
                    cubeb_devid output_device,
                    cubeb_stream_params * output_stream_params,
                    unsigned int latency_frames, cubeb_data_callback data_callback,
@@ -2077,16 +2094,24 @@ wasapi_stream_init(cubeb * context, cube
   }
 
   std::unique_ptr<cubeb_stream, decltype(&wasapi_stream_destroy)> stm(new cubeb_stream(), wasapi_stream_destroy);
 
   stm->context = context;
   stm->data_callback = data_callback;
   stm->state_callback = state_callback;
   stm->user_ptr = user_ptr;
+
+  if (stm->output_stream_params.prefs & CUBEB_STREAM_PREF_VOICE ||
+      stm->input_stream_params.prefs & CUBEB_STREAM_PREF_VOICE) {
+    stm->role = eCommunications;
+  } else {
+    stm->role = eConsole;
+  }
+
   if (input_stream_params) {
     stm->input_stream_params = *input_stream_params;
     stm->input_device = utf8_to_wstr(reinterpret_cast<char const *>(input_device));
   }
   if (output_stream_params) {
     stm->output_stream_params = *output_stream_params;
     stm->output_device = utf8_to_wstr(reinterpret_cast<char const *>(output_device));
   }
--- a/media/webrtc/signaling/src/jsep/JsepTransceiver.h
+++ b/media/webrtc/signaling/src/jsep/JsepTransceiver.h
@@ -63,20 +63,19 @@ class JsepTransceiver {
                !HasLevel() || oldTransceiver.GetLevel() == GetLevel());
     mTransport = oldTransceiver.mTransport;
     if (rollbackLevel) {
       mLevel = oldTransceiver.mLevel;
       mBundleLevel = oldTransceiver.mBundleLevel;
     }
     mRecvTrack = oldTransceiver.mRecvTrack;
 
-    // stop() caused by a disabled m-section in a remote offer cannot be
-    // rolled back.
-    if (!IsStopped()) {
-      mMid = oldTransceiver.mMid;
+    // Don't allow rollback to re-associate a transceiver.
+    if (!oldTransceiver.IsAssociated()) {
+      Disassociate();
     }
   }
 
   bool IsAssociated() const { return !mMid.empty(); }
 
   const std::string& GetMid() const {
     MOZ_ASSERT(IsAssociated());
     return mMid;
@@ -94,17 +93,20 @@ class JsepTransceiver {
   void SetLevel(size_t level) {
     MOZ_ASSERT(level != SIZE_MAX);
     MOZ_ASSERT(!HasLevel());
     MOZ_ASSERT(!IsStopped());
 
     mLevel = level;
   }
 
-  void ClearLevel() { mLevel = SIZE_MAX; }
+  void ClearLevel() {
+    MOZ_ASSERT(!IsAssociated());
+    mLevel = SIZE_MAX;
+  }
 
   size_t GetLevel() const {
     MOZ_ASSERT(HasLevel());
     return mLevel;
   }
 
   void Stop() { mStopped = true; }
 
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -5658,17 +5658,17 @@ var IdentityHandler = {
     if (aState & Ci.nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL) {
       return this.IDENTITY_MODE_VERIFIED;
     }
 
     if (aState & Ci.nsIWebProgressListener.STATE_IS_SECURE) {
       return this.IDENTITY_MODE_IDENTIFIED;
     }
 
-    let whitelist = /^about:(about|accounts|addons|buildconfig|cache|config|crashes|devices|downloads|experiments|fennec|firefox|feedback|home|license|logins|logo|memory|mozilla|networking|privatebrowsing|rights|serviceworkers|support|telemetry|webrtc)($|\?)/i;
+    let whitelist = /^about:(about|accounts|addons|buildconfig|cache|compat|config|crashes|devices|downloads|experiments|fennec|firefox|feedback|home|license|logins|logo|memory|mozilla|networking|privatebrowsing|rights|serviceworkers|support|telemetry|webrtc)($|\?)/i;
     if (uri.schemeIs("about") && whitelist.test(uri.spec)) {
         return this.IDENTITY_MODE_CHROMEUI;
     }
 
     return this.IDENTITY_MODE_UNKNOWN;
   },
 
   getMixedDisplayMode: function getMixedDisplayMode(aState) {
--- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/PermissionDelegateTest.kt
+++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/PermissionDelegateTest.kt
@@ -29,16 +29,20 @@ class PermissionDelegateTest : BaseSessi
     private fun hasPermission(permission: String): Boolean {
         if (Build.VERSION.SDK_INT < 23) {
             return true
         }
         return PackageManager.PERMISSION_GRANTED ==
                 InstrumentationRegistry.getTargetContext().checkSelfPermission(permission)
     }
 
+    private fun isEmulator(): Boolean {
+        return "generic".equals(Build.DEVICE) || Build.DEVICE.startsWith("generic_")
+    }
+
     @WithDevToolsAPI
     @Test fun media() {
         assertInAutomationThat("Should have camera permission",
                                hasPermission(Manifest.permission.CAMERA), equalTo(true))
 
         assertInAutomationThat("Should have microphone permission",
                                hasPermission(Manifest.permission.RECORD_AUDIO),
                                equalTo(true))
@@ -63,27 +67,39 @@ class PermissionDelegateTest : BaseSessi
             @AssertCalled(count = 1)
             override fun onMediaPermissionRequest(
                     session: GeckoSession, uri: String,
                     video: Array<out GeckoSession.PermissionDelegate.MediaSource>?,
                     audio: Array<out GeckoSession.PermissionDelegate.MediaSource>?,
                     callback: GeckoSession.PermissionDelegate.MediaCallback) {
                 assertThat("URI should match", uri, endsWith(HELLO_HTML_PATH))
                 assertThat("Video source should be valid", video, not(emptyArray()))
-                assertThat("Audio source should be valid", audio, not(emptyArray()))
-                callback.grant(video!![0], audio!![0])
+
+                if (isEmulator()) {
+                  callback.grant(video!![0], null)
+                } else {
+                  assertThat("Audio source should be valid", audio, not(emptyArray()))
+                  callback.grant(video!![0], audio!![0])
+                }
             }
         })
 
-        // Start a video/audio stream.
-        val stream = mainSession.waitForJS(
-                """window.navigator.mediaDevices.getUserMedia({
+        // Start a video stream, with audio if on a real device.
+        var code: String?
+        if (isEmulator()) {
+          code = """window.navigator.mediaDevices.getUserMedia({
                        video: { width: 320, height: 240, frameRate: 10 },
-                       audio: true,
-                   })""")
+                   })"""
+        } else {
+          code = """window.navigator.mediaDevices.getUserMedia({
+                       video: { width: 320, height: 240, frameRate: 10 },
+                       audio: true
+                   })"""
+        }
+        val stream = mainSession.waitForJS(code)
 
         assertThat("Stream should be active", stream.asJSMap(),
                    hasEntry("active", true))
         assertThat("Stream should have ID", stream.asJSMap(),
                    hasEntry(equalTo("id"), not(isEmptyString())))
 
         // Stop the stream.
         mainSession.waitForJS(
@@ -98,18 +114,23 @@ class PermissionDelegateTest : BaseSessi
                     video: Array<out GeckoSession.PermissionDelegate.MediaSource>?,
                     audio: Array<out GeckoSession.PermissionDelegate.MediaSource>?,
                     callback: GeckoSession.PermissionDelegate.MediaCallback) {
                 callback.reject()
             }
         })
 
         try {
-            mainSession.waitForJS("""
-                    window.navigator.mediaDevices.getUserMedia({ audio: true, video: true })""")
+            if (isEmulator()) {
+                mainSession.waitForJS("""
+                        window.navigator.mediaDevices.getUserMedia({ video: true })""")
+            } else {
+                mainSession.waitForJS("""
+                        window.navigator.mediaDevices.getUserMedia({ audio: true: video: true })""")
+            }
             fail("Request should have failed")
         } catch (e: RejectedPromiseException) {
             assertThat("Error should be correct",
                        e.reason.asJSMap(), hasEntry("name", "NotAllowedError"))
         }
     }
 
     @WithDevToolsAPI
--- a/mobile/android/geckoview/src/main/AndroidManifest.xml
+++ b/mobile/android/geckoview/src/main/AndroidManifest.xml
@@ -1,15 +1,16 @@
 <?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           package="org.mozilla.geckoview">
 
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
     <uses-permission android:name="android.permission.INTERNET"/>
     <uses-permission android:name="android.permission.WAKE_LOCK"/>
+    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
 
     <uses-feature
             android:name="android.hardware.location"
             android:required="false"/>
     <uses-feature
             android:name="android.hardware.location.gps"
             android:required="false"/>
     <uses-feature
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java
@@ -1991,16 +1991,51 @@ public class GeckoAppShell {
         }
         final String prop = am.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE);
         if (prop == null) {
             return DEFAULT;
         }
         return Integer.parseInt(prop);
     }
 
+    static private int sPreviousAudioMode = -2;
+
+    @WrapForJNI(calledFrom = "any")
+    public static void setCommunicationAudioModeOn(final boolean on) {
+        final AudioManager am = (AudioManager)getApplicationContext()
+                                .getSystemService(Context.AUDIO_SERVICE);
+        if (am == null) {
+            return;
+        }
+
+        if (sPreviousAudioMode == AudioManager.MODE_INVALID) {
+            sPreviousAudioMode = am.getMode();
+        }
+
+        try {
+            if (on)  {
+                Log.e(LOGTAG, "Setting communication mode ON");
+                sPreviousAudioMode = am.getMode();
+                am.startBluetoothSco();
+                am.setBluetoothScoOn(true);
+                am.setMode(AudioManager.MODE_IN_COMMUNICATION);
+            } else {
+                Log.e(LOGTAG, "Setting communication mode OFF");
+                am.setMode(sPreviousAudioMode);
+                sPreviousAudioMode = AudioManager.MODE_INVALID;
+                am.stopBluetoothSco();
+                am.setBluetoothScoOn(false);
+            }
+        } catch (SecurityException e) {
+            Log.e(LOGTAG, "could not set communication mode", e);
+        }
+
+        am.setSpeakerphoneOn(!on);
+    }
+
     private static String getLanguageTag(final Locale locale) {
         final StringBuilder out = new StringBuilder(locale.getLanguage());
         final String country = locale.getCountry();
         final String variant = locale.getVariant();
         if (!TextUtils.isEmpty(country)) {
             out.append('-').append(country);
         }
         if (!TextUtils.isEmpty(variant)) {
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -2696,17 +2696,17 @@ pref("security.notification_enable_delay
 
 #if defined(DEBUG) && !defined(ANDROID)
 pref("csp.about_uris_without_csp", "blank,printpreview,srcdoc,about,addons,cache-entry,config,crashes,debugging,devtools,downloads,home,memory,networking,newtab,performance,plugins,policies,profiles,restartrequired,serviceworkers,sessionrestore,support,sync-log,telemetry,url-classifier,webrtc,welcomeback");
 // the following prefs are for testing purposes only.
 pref("csp.overrule_about_uris_without_csp_whitelist", false);
 pref("csp.skip_about_page_has_csp_assert", false);
 // assertion flag will be set to false after fixing Bug 1473549
 pref("security.allow_eval_with_system_principal", false);
-pref("security.uris_using_eval_with_system_principal", "autocomplete.xml,redux.js,react-redux.js,content-task.js,preferencesbindings.js,lodash.js,jszip.js,sinon-7.2.7.js,ajv-4.1.1.js,updates.js,setup,jsol.js,parent_utils.js,chrometask_chromescript");
+pref("security.uris_using_eval_with_system_principal", "autocomplete.xml,redux.js,react-redux.js,content-task.js,preferencesbindings.js,lodash.js,jszip.js,sinon-7.2.7.js,ajv-4.1.1.js,setup,jsol.js,parent_utils.js,chrometask_chromescript");
 #endif
 
 #if defined(DEBUG) || defined(FUZZING)
 // Disallow web documents loaded with the SystemPrincipal
 pref("security.disallow_non_local_systemprincipal_in_tests", false);
 #endif
 
 // Default Content Security Policy to apply to signed contents.
@@ -6009,24 +6009,16 @@ pref("dom.payments.defaults.saveAddress"
 pref("dom.payments.request.supportedRegions", "US,CA");
 
 #ifdef MOZ_ASAN_REPORTER
 pref("asanreporter.apiurl", "https://anf1.fuzzing.mozilla.org/crashproxy/submit/");
 pref("asanreporter.clientid", "unknown");
 pref("toolkit.telemetry.overrideUpdateChannel", "nightly-asan");
 #endif
 
-#if defined(XP_WIN)
-pref("layers.mlgpu.enabled", true);
-
-// Both this and the master "enabled" pref must be on to use Advanced Layers
-// on Windows 7.
-pref("layers.mlgpu.enable-on-windows7", true);
-#endif
-
 // Enable lowercased response header name
 pref("dom.xhr.lowercase_header.enabled", true);
 
 // Control whether clients.openWindow() opens windows in the same process
 // that called the API vs following our normal multi-process selection
 // algorithm.  Restricting openWindow to same process improves service worker
 // web compat in the short term.  Once the SW multi-e10s refactor is complete
 // this can be removed.
--- a/netwerk/ipc/NeckoChannelParams.ipdlh
+++ b/netwerk/ipc/NeckoChannelParams.ipdlh
@@ -212,16 +212,17 @@ struct HttpChannelOpenArgs
 {
   URIParams                   uri;
   // - TODO: bug 571161: unclear if any HTTP channel clients ever
   // set originalURI != uri (about:credits?); also not clear if
   // chrome channel would ever need to know.  Get rid of next arg?
   URIParams?                  original;
   URIParams?                  doc;
   URIParams?                  originalReferrer;
+  uint32_t                    originalReferrerPolicy;
   uint32_t                    referrerPolicy;
   URIParams?                  apiRedirectTo;
   URIParams?                  topWindowURI;
   uint32_t                    loadFlags;
   RequestHeaderTuples         requestHeaders;
   nsCString                   requestMethod;
   IPCStream?                  uploadStream;
   bool                        uploadStreamHasHeaders;
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -206,17 +206,18 @@ HttpBaseChannel::HttpBaseChannel()
       mAddedAsNonTailRequest(false),
       mAsyncOpenWaitingForStreamLength(false),
       mUpgradableToSecure(true),
       mTlsFlags(0),
       mSuspendCount(0),
       mInitialRwin(0),
       mProxyResolveFlags(0),
       mContentDispositionHint(UINT32_MAX),
-      mReferrerPolicy(NS_GetDefaultReferrerPolicy()),
+      mOriginalReferrerPolicy(NS_GetDefaultReferrerPolicy()),
+      mReferrerPolicy(mOriginalReferrerPolicy),
       mCorsMode(nsIHttpChannelInternal::CORS_MODE_NO_CORS),
       mRedirectMode(nsIHttpChannelInternal::REDIRECT_MODE_FOLLOW),
       mLastRedirectFlags(0),
       mPriority(PRIORITY_NORMAL),
       mRedirectionLimit(gHttpHandler->RedirectionLimit()),
       mRedirectCount(0),
       mInternalRedirectCount(0),
       mAsyncOpenTimeOverriden(false),
@@ -1643,16 +1644,17 @@ bool HttpBaseChannel::IsCrossOriginWithR
 }
 
 NS_IMETHODIMP
 HttpBaseChannel::SetReferrerWithPolicy(nsIURI* referrer,
                                        uint32_t referrerPolicy) {
   ENSURE_CALLED_BEFORE_CONNECT();
 
   nsIURI* originalReferrer = referrer;
+  uint32_t originalReferrerPolicy = referrerPolicy;
 
   mReferrerPolicy = referrerPolicy;
 
   // clear existing referrer, if any
   mReferrer = nullptr;
   nsresult rv = mRequestHead.ClearHeader(nsHttp::Referer);
   if (NS_FAILED(rv)) {
     return rv;
@@ -1925,16 +1927,17 @@ HttpBaseChannel::SetReferrerWithPolicy(n
     rv = clone->GetAsciiSpec(spec);
     if (NS_FAILED(rv)) return rv;
   }
 
   // finally, remember the referrer URI and set the Referer header.
   rv = SetRequestHeader(NS_LITERAL_CSTRING("Referer"), spec, false);
   if (NS_FAILED(rv)) return rv;
 
+  mOriginalReferrerPolicy = originalReferrerPolicy;
   mOriginalReferrer = originalReferrer;
   mReferrer = clone;
   return NS_OK;
 }
 
 // Return the channel's proxy URI, or if it doesn't exist, the
 // channel's main URI.
 NS_IMETHODIMP
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -439,25 +439,27 @@ class HttpBaseChannel : public nsHashPro
   void InternalSetUploadStreamLength(uint64_t aLength) {
     mReqContentLength = aLength;
   }
 
   void SetUploadStreamHasHeaders(bool hasHeaders) {
     mUploadStreamHasHeaders = hasHeaders;
   }
 
-  MOZ_MUST_USE nsresult SetReferrerWithPolicyInternal(nsIURI *referrer,
-                                                      uint32_t referrerPolicy) {
+  MOZ_MUST_USE nsresult SetReferrerWithPolicyInternal(
+      nsIURI *referrer, uint32_t originalReferrerPolicy,
+      uint32_t referrerPolicy) {
     nsAutoCString spec;
     nsresult rv = referrer->GetAsciiSpec(spec);
     if (NS_FAILED(rv)) {
       return rv;
     }
     mOriginalReferrer = referrer;
     mReferrer = referrer;
+    mOriginalReferrerPolicy = originalReferrerPolicy;
     mReferrerPolicy = referrerPolicy;
     rv = mRequestHead.SetHeader(nsHttp::Referer, spec);
     return rv;
   }
 
   MOZ_MUST_USE nsresult SetTopWindowURI(nsIURI *aTopWindowURI) {
     mTopWindowURI = aTopWindowURI;
     return NS_OK;
@@ -742,16 +744,17 @@ class HttpBaseChannel : public nsHashPro
   uint32_t mSuspendCount;
 
   // Per channel transport window override (0 means no override)
   uint32_t mInitialRwin;
 
   uint32_t mProxyResolveFlags;
 
   uint32_t mContentDispositionHint;
+  uint32_t mOriginalReferrerPolicy;
   uint32_t mReferrerPolicy;
 
   uint32_t mCorsMode;
   uint32_t mRedirectMode;
 
   // If this channel was created as the result of a redirect, then this value
   // will reflect the redirect flags passed to the SetupReplacementChannel()
   // method.
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -2659,16 +2659,17 @@ nsresult HttpChannelChild::ContinueAsync
 
   HttpChannelOpenArgs openArgs;
   // No access to HttpChannelOpenArgs members, but they each have a
   // function with the struct name that returns a ref.
   SerializeURI(mURI, openArgs.uri());
   SerializeURI(mOriginalURI, openArgs.original());
   SerializeURI(mDocumentURI, openArgs.doc());
   SerializeURI(mOriginalReferrer, openArgs.originalReferrer());
+  openArgs.originalReferrerPolicy() = mOriginalReferrerPolicy;
   openArgs.referrerPolicy() = mReferrerPolicy;
   SerializeURI(mAPIRedirectToURI, openArgs.apiRedirectTo());
   openArgs.loadFlags() = mLoadFlags;
   openArgs.requestHeaders() = mClientSetRequestHeaders;
   mRequestHead.Method(openArgs.requestMethod());
   openArgs.preferredAlternativeTypes() = mPreferredCachedAltDataTypes;
 
   AutoIPCStream autoStream(openArgs.uploadStream());
--- a/netwerk/protocol/http/HttpChannelParent.cpp
+++ b/netwerk/protocol/http/HttpChannelParent.cpp
@@ -129,20 +129,20 @@ void HttpChannelParent::ActorDestroy(Act
 bool HttpChannelParent::Init(const HttpChannelCreationArgs& aArgs) {
   LOG(("HttpChannelParent::Init [this=%p]\n", this));
   AUTO_PROFILER_LABEL("HttpChannelParent::Init", NETWORK);
   switch (aArgs.type()) {
     case HttpChannelCreationArgs::THttpChannelOpenArgs: {
       const HttpChannelOpenArgs& a = aArgs.get_HttpChannelOpenArgs();
       return DoAsyncOpen(
           a.uri(), a.original(), a.doc(), a.originalReferrer(),
-          a.referrerPolicy(), a.apiRedirectTo(), a.topWindowURI(),
-          a.loadFlags(), a.requestHeaders(), a.requestMethod(),
-          a.uploadStream(), a.uploadStreamHasHeaders(), a.priority(),
-          a.classOfService(), a.redirectionLimit(), a.allowSTS(),
+          a.originalReferrerPolicy(), a.referrerPolicy(), a.apiRedirectTo(),
+          a.topWindowURI(), a.loadFlags(), a.requestHeaders(),
+          a.requestMethod(), a.uploadStream(), a.uploadStreamHasHeaders(),
+          a.priority(), a.classOfService(), a.redirectionLimit(), a.allowSTS(),
           a.thirdPartyFlags(), a.resumeAt(), a.startPos(), a.entityID(),
           a.chooseApplicationCache(), a.appCacheClientID(), a.allowSpdy(),
           a.allowAltSvc(), a.beConservative(), a.tlsFlags(), a.loadInfo(),
           a.synthesizedResponseHead(), a.synthesizedSecurityInfoSerialization(),
           a.cacheKey(), a.requestContextID(), a.preflightArgs(),
           a.initialRwin(), a.blockAuthPrompt(),
           a.suspendAfterSynthesizeResponse(), a.allowStaleCacheContent(),
           a.contentTypeHint(), a.corsMode(), a.redirectMode(), a.channelId(),
@@ -380,17 +380,18 @@ void HttpChannelParent::InvokeAsyncOpen(
     AsyncOpenFailed(rv);
   }
 }
 
 bool HttpChannelParent::DoAsyncOpen(
     const URIParams& aURI, const Maybe<URIParams>& aOriginalURI,
     const Maybe<URIParams>& aDocURI,
     const Maybe<URIParams>& aOriginalReferrerURI,
-    const uint32_t& aReferrerPolicy, const Maybe<URIParams>& aAPIRedirectToURI,
+    const uint32_t& aOriginalReferrerPolicy, const uint32_t& aReferrerPolicy,
+    const Maybe<URIParams>& aAPIRedirectToURI,
     const Maybe<URIParams>& aTopWindowURI, const uint32_t& aLoadFlags,
     const RequestHeaderTuples& requestHeaders, const nsCString& requestMethod,
     const Maybe<IPCStream>& uploadStream, const bool& uploadStreamHasHeaders,
     const int16_t& priority, const uint32_t& classOfService,
     const uint8_t& redirectionLimit, const bool& allowSTS,
     const uint32_t& thirdPartyFlags, const bool& doResumeAt,
     const uint64_t& startPos, const nsCString& entityID,
     const bool& chooseApplicationCache, const nsCString& appCacheClientID,
@@ -478,18 +479,18 @@ bool HttpChannelParent::DoAsyncOpen(
     httpChannel->SetPrivate(mPBOverride == kPBOverride_Private ? true : false);
   }
 
   if (doResumeAt) httpChannel->ResumeAt(startPos, entityID);
 
   if (originalUri) httpChannel->SetOriginalURI(originalUri);
   if (docUri) httpChannel->SetDocumentURI(docUri);
   if (referrerUri) {
-    rv = httpChannel->SetReferrerWithPolicyInternal(referrerUri,
-                                                    aReferrerPolicy);
+    rv = httpChannel->SetReferrerWithPolicyInternal(
+        referrerUri, aOriginalReferrerPolicy, aReferrerPolicy);
     MOZ_ASSERT(NS_SUCCEEDED(rv));
   }
   if (apiRedirectToUri) httpChannel->RedirectTo(apiRedirectToUri);
   if (topWindowUri) {
     rv = httpChannel->SetTopWindowURI(topWindowUri);
     MOZ_ASSERT(NS_SUCCEEDED(rv));
   }
 
--- a/netwerk/protocol/http/HttpChannelParent.h
+++ b/netwerk/protocol/http/HttpChannelParent.h
@@ -136,17 +136,17 @@ class HttpChannelParent final : public n
   // ChildChannel.  Used during redirects.
   MOZ_MUST_USE bool ConnectChannel(const uint32_t& channelId,
                                    const bool& shouldIntercept);
 
   MOZ_MUST_USE bool DoAsyncOpen(
       const URIParams& uri, const Maybe<URIParams>& originalUri,
       const Maybe<URIParams>& docUri,
       const Maybe<URIParams>& originalReferrerUri,
-      const uint32_t& referrerPolicy,
+      const uint32_t& originalReferrerPolicy, const uint32_t& referrerPolicy,
       const Maybe<URIParams>& internalRedirectUri,
       const Maybe<URIParams>& topWindowUri, const uint32_t& loadFlags,
       const RequestHeaderTuples& requestHeaders, const nsCString& requestMethod,
       const Maybe<IPCStream>& uploadStream, const bool& uploadStreamHasHeaders,
       const int16_t& priority, const uint32_t& classOfService,
       const uint8_t& redirectionLimit, const bool& allowSTS,
       const uint32_t& thirdPartyFlags, const bool& doResumeAt,
       const uint64_t& startPos, const nsCString& entityID,
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -10220,23 +10220,25 @@ nsresult nsHttpChannel::RedirectToInterc
   }
 
   return rv;
 }
 
 void nsHttpChannel::ReEvaluateReferrerAfterTrackingStatusIsKnown() {
   if (StaticPrefs::network_cookie_cookieBehavior() ==
       nsICookieService::BEHAVIOR_REJECT_TRACKER) {
-    // If our referrer has been set before, and our referrer policy is equal to
-    // the default policy if we thought the channel wasn't a third-party
-    // tracking channel, we may need to set our referrer with referrer policy
-    // once again to ensure our defaults properly take effect now.
+    // If our referrer has been set before, the JS/DOM caller did not
+    // previously provide us with an explicit referrer policy value and our
+    // current referrer policy is equal to the default policy if we thought the
+    // channel wasn't a third-party tracking channel, we may need to set our
+    // referrer with referrer policy once again to ensure our defaults properly
+    // take effect now.
     bool isPrivate =
         mLoadInfo && mLoadInfo->GetOriginAttributes().mPrivateBrowsingId > 0;
-    if (mOriginalReferrer &&
+    if (mOriginalReferrer && mOriginalReferrerPolicy == REFERRER_POLICY_UNSET &&
         mReferrerPolicy ==
             NS_GetDefaultReferrerPolicy(nullptr, nullptr, isPrivate)) {
       SetReferrer(mOriginalReferrer);
     }
   }
 }
 
 namespace {
--- a/netwerk/sctp/datachannel/DataChannel.cpp
+++ b/netwerk/sctp/datachannel/DataChannel.cpp
@@ -2337,17 +2337,20 @@ int DataChannelConnection::ReceiveCallba
   // usrsctp defines the callback as returning an int, but doesn't use it
   return 1;
 }
 
 already_AddRefed<DataChannel> DataChannelConnection::Open(
     const nsACString &label, const nsACString &protocol, Type type,
     bool inOrder, uint32_t prValue, DataChannelListener *aListener,
     nsISupports *aContext, bool aExternalNegotiated, uint16_t aStream) {
-  // aStream == INVALID_STREAM to have the protocol allocate
+  if (!aExternalNegotiated) {
+    // aStream == INVALID_STREAM to have the protocol allocate
+    aStream = INVALID_STREAM;
+  }
   uint16_t prPolicy = SCTP_PR_SCTP_NONE;
   uint32_t flags;
 
   LOG(
       ("DC Open: label %s/%s, type %u, inorder %d, prValue %u, listener %p, "
        "context %p, external: %s, stream %u",
        PromiseFlatCString(label).get(), PromiseFlatCString(protocol).get(),
        type, inOrder, prValue, aListener, aContext,
--- a/servo/components/selectors/parser.rs
+++ b/servo/components/selectors/parser.rs
@@ -25,62 +25,110 @@ use thin_slice::ThinBoxedSlice;
 
 /// A trait that represents a pseudo-element.
 pub trait PseudoElement: Sized + ToCss {
     /// The `SelectorImpl` this pseudo-element is used for.
     type Impl: SelectorImpl;
 
     /// Whether the pseudo-element supports a given state selector to the right
     /// of it.
-    fn supports_pseudo_class(
-        &self,
-        _pseudo_class: &<Self::Impl as SelectorImpl>::NonTSPseudoClass,
-    ) -> bool {
-        false
-    }
+    fn accepts_state_pseudo_classes(&self) -> bool { false }
 
     /// Whether this pseudo-element is valid after a ::slotted(..) pseudo.
-    fn valid_after_slotted(&self) -> bool {
-        false
-    }
+    fn valid_after_slotted(&self) -> bool { false }
 }
 
 /// A trait that represents a pseudo-class.
 pub trait NonTSPseudoClass: Sized + ToCss {
     /// The `SelectorImpl` this pseudo-element is used for.
     type Impl: SelectorImpl;
 
     /// Whether this pseudo-class is :active or :hover.
     fn is_active_or_hover(&self) -> bool;
+
+    /// Whether this pseudo-class belongs to:
+    ///
+    /// https://drafts.csswg.org/selectors-4/#useraction-pseudos
+    fn is_user_action_state(&self) -> bool;
 }
 
 /// Returns a Cow::Borrowed if `s` is already ASCII lowercase, and a
 /// Cow::Owned if `s` had to be converted into ASCII lowercase.
 fn to_ascii_lowercase(s: &str) -> Cow<str> {
     if let Some(first_uppercase) = s.bytes().position(|byte| byte >= b'A' && byte <= b'Z') {
         let mut string = s.to_owned();
         string[first_uppercase..].make_ascii_lowercase();
         string.into()
     } else {
         s.into()
     }
 }
 
+bitflags! {
+    /// Flags that indicate at which point of parsing a selector are we.
+    struct SelectorParsingState: u8 {
+        /// Whether we're inside a negation. If we're inside a negation, we're
+        /// not allowed to add another negation or such, for example.
+        const INSIDE_NEGATION = 1 << 0;
+        /// Whether we've parsed an ::slotted() pseudo-element already.
+        ///
+        /// If so, then we can only parse a subset of pseudo-elements, and
+        /// whatever comes after them if so.
+        const AFTER_SLOTTED = 1 << 1;
+        /// Whether we've parsed a pseudo-element (as in, an
+        /// `Impl::PseudoElement` thus not accounting for `::slotted`) already.
+        ///
+        /// If so, then other pseudo-elements and most other selectors are
+        /// disallowed.
+        const AFTER_PSEUDO_ELEMENT = 1 << 2;
+        /// Whether we've parsed a non-stateful pseudo-element (again, as-in
+        /// `Impl::PseudoElement`) already. If so, then other pseudo-classes are
+        /// disallowed. If this flag is set, `AFTER_PSEUDO_ELEMENT` must be set
+        /// as well.
+        const AFTER_NON_STATEFUL_PSEUDO_ELEMENT = 1 << 3;
+        /// Whether we are after any of the pseudo-like things.
+        const AFTER_PSEUDO = Self::AFTER_SLOTTED.bits | Self::AFTER_PSEUDO_ELEMENT.bits;
+    }
+}
+
+impl SelectorParsingState {
+    #[inline]
+    fn allows_functional_pseudo_classes(self) -> bool {
+        !self.intersects(SelectorParsingState::AFTER_PSEUDO)
+    }
+
+    #[inline]
+    fn allows_slotted(self) -> bool {
+        !self.intersects(SelectorParsingState::AFTER_PSEUDO)
+    }
+
+    #[inline]
+    fn allows_non_functional_pseudo_classes(self) -> bool {
+        !self.intersects(SelectorParsingState::AFTER_SLOTTED | SelectorParsingState::AFTER_NON_STATEFUL_PSEUDO_ELEMENT)
+    }
+
+    #[inline]
+    fn allows_tree_structural_pseudo_classes(self) -> bool {
+        !self.intersects(SelectorParsingState::AFTER_PSEUDO)
+    }
+}
+
 pub type SelectorParseError<'i> = ParseError<'i, SelectorParseErrorKind<'i>>;
 
 #[derive(Clone, Debug, PartialEq)]
 pub enum SelectorParseErrorKind<'i> {
     PseudoElementInComplexSelector,
     NoQualifiedNameInAttributeSelector(Token<'i>),
     EmptySelector,
     DanglingCombinator,
     NonSimpleSelectorInNegation,
     NonCompoundSelector,
     NonPseudoElementAfterSlotted,
     InvalidPseudoElementAfterSlotted,
+    InvalidState,
     UnexpectedTokenInAttributeSelector(Token<'i>),
     PseudoElementExpectedColon(Token<'i>),
     PseudoElementExpectedIdent(Token<'i>),
     NoIdentForPseudo(Token<'i>),
     UnsupportedPseudoClassOrElement(CowRcStr<'i>),
     UnexpectedIdent(CowRcStr<'i>),
     ExpectedNamespace(CowRcStr<'i>),
     ExpectedBarInAttr(Token<'i>),
@@ -1364,19 +1412,19 @@ where
 {
     let mut builder = SelectorBuilder::default();
 
     let mut has_pseudo_element;
     let mut slotted;
     'outer_loop: loop {
         // Parse a sequence of simple selectors.
         match parse_compound_selector(parser, input, &mut builder)? {
-            Some((has_pseudo, slot)) => {
-                has_pseudo_element = has_pseudo;
-                slotted = slot;
+            Some(state) => {
+                has_pseudo_element = state.intersects(SelectorParsingState::AFTER_PSEUDO_ELEMENT);
+                slotted = state.intersects(SelectorParsingState::AFTER_SLOTTED);
             },
             None => {
                 return Err(input.new_custom_error(if builder.has_combinators() {
                     SelectorParseErrorKind::DanglingCombinator
                 } else {
                     SelectorParseErrorKind::EmptySelector
                 }));
             },
@@ -1843,17 +1891,17 @@ where
         Ok(result) => result,
         Err(ParseError {
             kind: ParseErrorKind::Basic(BasicParseErrorKind::EndOfInput),
             ..
         }) => return Err(input.new_custom_error(SelectorParseErrorKind::EmptyNegation)),
         Err(e) => return Err(e.into()),
     };
     if !is_type_sel {
-        match parse_one_simple_selector(parser, input, /* inside_negation = */ true)? {
+        match parse_one_simple_selector(parser, input, SelectorParsingState::INSIDE_NEGATION)? {
             Some(SimpleSelectorParseResult::SimpleSelector(s)) => {
                 sequence.push(s);
             },
             None => {
                 return Err(input.new_custom_error(SelectorParseErrorKind::EmptyNegation));
             },
             Some(SimpleSelectorParseResult::PseudoElement(_)) |
             Some(SimpleSelectorParseResult::SlottedPseudo(_)) => {
@@ -1870,24 +1918,21 @@ where
 }
 
 /// simple_selector_sequence
 /// : [ type_selector | universal ] [ HASH | class | attrib | pseudo | negation ]*
 /// | [ HASH | class | attrib | pseudo | negation ]+
 ///
 /// `Err(())` means invalid selector.
 /// `Ok(None)` is an empty selector
-///
-/// The booleans represent whether a pseudo-element has been parsed, and whether
-/// ::slotted() has been parsed, respectively.
 fn parse_compound_selector<'i, 't, P, Impl>(
     parser: &P,
     input: &mut CssParser<'i, 't>,
     builder: &mut SelectorBuilder<Impl>,
-) -> Result<Option<(bool, bool)>, ParseError<'i, P::Error>>
+) -> Result<Option<SelectorParsingState>, ParseError<'i, P::Error>>
 where
     P: Parser<'i, Impl = Impl>,
     Impl: SelectorImpl,
 {
     input.skip_whitespace();
 
     let mut empty = true;
     if !parse_type_selector(parser, input, builder)? {
@@ -1896,157 +1941,86 @@ where
             // default namespace, there is an implicit "<defaultns>|*" type
             // selector.
             builder.push_simple_selector(Component::DefaultNamespace(url))
         }
     } else {
         empty = false;
     }
 
-    let mut pseudo = false;
-    let mut slot = false;
+    let mut state = SelectorParsingState::empty();
     loop {
         let parse_result =
-            match parse_one_simple_selector(parser, input, /* inside_negation = */ false)? {
+            match parse_one_simple_selector(parser, input, state)? {
                 None => break,
                 Some(result) => result,
             };
 
         empty = false;
 
-        let slotted_selector;
-        let pseudo_element;
-
         match parse_result {
             SimpleSelectorParseResult::SimpleSelector(s) => {
                 builder.push_simple_selector(s);
-                continue;
-            },
-            SimpleSelectorParseResult::PseudoElement(p) => {
-                slotted_selector = None;
-                pseudo_element = Some(p);
             },
             SimpleSelectorParseResult::SlottedPseudo(selector) => {
-                slotted_selector = Some(selector);
-                let maybe_pseudo =
-                    parse_one_simple_selector(parser, input, /* inside_negation = */ false)?;
-
-                pseudo_element = match maybe_pseudo {
-                    None => None,
-                    Some(SimpleSelectorParseResult::PseudoElement(pseudo)) => {
-                        if !pseudo.valid_after_slotted() {
-                            return Err(input.new_custom_error(
-                                SelectorParseErrorKind::InvalidPseudoElementAfterSlotted,
-                            ));
-                        }
-                        Some(pseudo)
-                    },
-                    Some(SimpleSelectorParseResult::SimpleSelector(..)) |
-                    Some(SimpleSelectorParseResult::SlottedPseudo(..)) => {
-                        return Err(input.new_custom_error(
-                            SelectorParseErrorKind::NonPseudoElementAfterSlotted,
-                        ));
-                    },
-                };
+                state.insert(SelectorParsingState::AFTER_SLOTTED);
+                if !builder.is_empty() {
+                    builder.push_combinator(Combinator::SlotAssignment);
+                }
+                builder.push_simple_selector(Component::Slotted(selector));
+            },
+            SimpleSelectorParseResult::PseudoElement(p) => {
+                state.insert(SelectorParsingState::AFTER_PSEUDO_ELEMENT);
+                if !p.accepts_state_pseudo_classes() {
+                    state.insert(SelectorParsingState::AFTER_NON_STATEFUL_PSEUDO_ELEMENT);
+                }
+                if !builder.is_empty() {
+                    builder.push_combinator(Combinator::PseudoElement);
+                }
+                builder.push_simple_selector(Component::PseudoElement(p));
             },
         }
-
-        debug_assert!(slotted_selector.is_some() || pseudo_element.is_some());
-        // Try to parse state to the right of the pseudo-element.
-        //
-        // There are only 3 allowable state selectors that can go on
-        // pseudo-elements as of right now.
-        let mut state_selectors = SmallVec::<[Component<Impl>; 3]>::new();
-        if let Some(ref p) = pseudo_element {
-            loop {
-                let location = input.current_source_location();
-                match input.next_including_whitespace() {
-                    Ok(&Token::Colon) => {},
-                    Ok(&Token::WhiteSpace(_)) | Err(_) => break,
-                    Ok(t) => {
-                        let e = SelectorParseErrorKind::PseudoElementExpectedColon(t.clone());
-                        return Err(location.new_custom_error(e));
-                    },
-                }
-
-                let location = input.current_source_location();
-                // TODO(emilio): Functional pseudo-classes too?
-                // We don't need it for now.
-                let name = match input.next_including_whitespace()? {
-                    &Token::Ident(ref name) => name.clone(),
-                    t => {
-                        return Err(location.new_custom_error(
-                            SelectorParseErrorKind::NoIdentForPseudo(t.clone()),
-                        ));
-                    },
-                };
-
-                let pseudo_class = P::parse_non_ts_pseudo_class(parser, location, name.clone())?;
-                if !p.supports_pseudo_class(&pseudo_class) {
-                    return Err(input.new_custom_error(
-                        SelectorParseErrorKind::UnsupportedPseudoClassOrElement(name),
-                    ));
-                }
-                state_selectors.push(Component::NonTSPseudoClass(pseudo_class));
-            }
-        }
-
-        if let Some(slotted) = slotted_selector {
-            slot = true;
-            if !builder.is_empty() {
-                builder.push_combinator(Combinator::SlotAssignment);
-            }
-            builder.push_simple_selector(Component::Slotted(slotted));
-        }
-
-        if let Some(p) = pseudo_element {
-            pseudo = true;
-            if !builder.is_empty() {
-                builder.push_combinator(Combinator::PseudoElement);
-            }
-
-            builder.push_simple_selector(Component::PseudoElement(p));
-
-            for state_selector in state_selectors.drain() {
-                builder.push_simple_selector(state_selector);
-            }
-        }
-
-        break;
     }
     if empty {
         // An empty selector is invalid.
         Ok(None)
     } else {
-        Ok(Some((pseudo, slot)))
+        Ok(Some(state))
     }
 }
 
 fn parse_functional_pseudo_class<'i, 't, P, Impl>(
     parser: &P,
     input: &mut CssParser<'i, 't>,
     name: CowRcStr<'i>,
-    inside_negation: bool,
+    state: SelectorParsingState,
 ) -> Result<Component<Impl>, ParseError<'i, P::Error>>
 where
     P: Parser<'i, Impl = Impl>,
     Impl: SelectorImpl,
 {
+    if !state.allows_functional_pseudo_classes() {
+        return Err(input.new_custom_error(
+            SelectorParseErrorKind::InvalidState
+        ));
+    }
+    debug_assert!(state.allows_tree_structural_pseudo_classes());
     match_ignore_ascii_case! { &name,
         "nth-child" => return Ok(parse_nth_pseudo_class(input, Component::NthChild)?),
         "nth-of-type" => return Ok(parse_nth_pseudo_class(input, Component::NthOfType)?),
         "nth-last-child" => return Ok(parse_nth_pseudo_class(input, Component::NthLastChild)?),
         "nth-last-of-type" => return Ok(parse_nth_pseudo_class(input, Component::NthLastOfType)?),
         "host" => return Ok(Component::Host(Some(parse_inner_compound_selector(parser, input)?))),
         "not" => {
-            if inside_negation {
+            if state.intersects(SelectorParsingState::INSIDE_NEGATION) {
                 return Err(input.new_custom_error(
                     SelectorParseErrorKind::UnexpectedIdent("not".into())
                 ));
             }
+            debug_assert!(state.is_empty());
             return parse_negation(parser, input)
         },
         _ => {}
     }
     P::parse_non_ts_functional_pseudo_class(parser, name, input).map(Component::NonTSPseudoClass)
 }
 
 fn parse_nth_pseudo_class<'i, 't, Impl, F>(
@@ -2075,126 +2049,155 @@ pub fn is_css2_pseudo_element(name: &str
 /// Parse a simple selector other than a type selector.
 ///
 /// * `Err(())`: Invalid selector, abort
 /// * `Ok(None)`: Not a simple selector, could be something else. `input` was not consumed.
 /// * `Ok(Some(_))`: Parsed a simple selector or pseudo-element
 fn parse_one_simple_selector<'i, 't, P, Impl>(
     parser: &P,
     input: &mut CssParser<'i, 't>,
-    inside_negation: bool,
+    state: SelectorParsingState,
 ) -> Result<Option<SimpleSelectorParseResult<Impl>>, ParseError<'i, P::Error>>
 where
     P: Parser<'i, Impl = Impl>,
     Impl: SelectorImpl,
 {
     let start = input.state();
-    // FIXME: remove clone() when lifetimes are non-lexical
-    match input.next_including_whitespace().map(|t| t.clone()) {
-        Ok(Token::IDHash(id)) => {
+    let token = match input.next_including_whitespace().map(|t| t.clone()) {
+        Ok(t) => t,
+        Err(..) => {
+            input.reset(&start);
+            return Ok(None);
+        }
+    };
+
+    Ok(Some(match token {
+        Token::IDHash(id) => {
+            if state.intersects(SelectorParsingState::AFTER_PSEUDO) {
+                return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
+            }
             let id = Component::ID(id.as_ref().into());
-            Ok(Some(SimpleSelectorParseResult::SimpleSelector(id)))
+            SimpleSelectorParseResult::SimpleSelector(id)
         },
-        Ok(Token::Delim('.')) => {
+        Token::Delim('.') => {
+            if state.intersects(SelectorParsingState::AFTER_PSEUDO) {
+                return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
+            }
             let location = input.current_source_location();
-            match *input.next_including_whitespace()? {
-                Token::Ident(ref class) => {
-                    let class = Component::Class(class.as_ref().into());
-                    Ok(Some(SimpleSelectorParseResult::SimpleSelector(class)))
-                },
+            let class = match *input.next_including_whitespace()? {
+                Token::Ident(ref class) => class,
                 ref t => {
                     let e = SelectorParseErrorKind::ClassNeedsIdent(t.clone());
-                    Err(location.new_custom_error(e))
+                    return Err(location.new_custom_error(e))
                 },
-            }
+            };
+            let class = Component::Class(class.as_ref().into());
+            SimpleSelectorParseResult::SimpleSelector(class)
         },
-        Ok(Token::SquareBracketBlock) => {
+        Token::SquareBracketBlock => {
+            if state.intersects(SelectorParsingState::AFTER_PSEUDO) {
+                return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
+            }
             let attr = input.parse_nested_block(|input| parse_attribute_selector(parser, input))?;
-            Ok(Some(SimpleSelectorParseResult::SimpleSelector(attr)))
+            SimpleSelectorParseResult::SimpleSelector(attr)
         },
-        Ok(Token::Colon) => {
+        Token::Colon => {
             let location = input.current_source_location();
             let (is_single_colon, next_token) = match input.next_including_whitespace()?.clone() {
                 Token::Colon => (false, input.next_including_whitespace()?.clone()),
                 t => (true, t),
             };
             let (name, is_functional) = match next_token {
                 Token::Ident(name) => (name, false),
                 Token::Function(name) => (name, true),
                 t => {
                     let e = SelectorParseErrorKind::PseudoElementExpectedIdent(t);
                     return Err(input.new_custom_error(e));
                 },
             };
             let is_pseudo_element =
                 !is_single_colon || P::pseudo_element_allows_single_colon(&name);
             if is_pseudo_element {
-                let parse_result = if is_functional {
+                if state.intersects(SelectorParsingState::AFTER_PSEUDO_ELEMENT) {
+                    return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
+                }
+                let pseudo_element = if is_functional {
                     if P::parse_slotted(parser) && name.eq_ignore_ascii_case("slotted") {
+                        if !state.allows_slotted() {
+                            return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
+                        }
                         let selector = input.parse_nested_block(|input| {
                             parse_inner_compound_selector(parser, input)
                         })?;
-                        SimpleSelectorParseResult::SlottedPseudo(selector)
-                    } else {
-                        let selector = input.parse_nested_block(|input| {
-                            P::parse_functional_pseudo_element(parser, name, input)
-                        })?;
-                        SimpleSelectorParseResult::PseudoElement(selector)
+                        return Ok(Some(SimpleSelectorParseResult::SlottedPseudo(selector)));
                     }
+                    input.parse_nested_block(|input| {
+                        P::parse_functional_pseudo_element(parser, name, input)
+                    })?
                 } else {
-                    SimpleSelectorParseResult::PseudoElement(P::parse_pseudo_element(
-                        parser, location, name,
-                    )?)
+                    P::parse_pseudo_element(parser, location, name)?
                 };
-                Ok(Some(parse_result))
+
+                if state.intersects(SelectorParsingState::AFTER_SLOTTED) && !pseudo_element.valid_after_slotted() {
+                    return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
+                }
+                SimpleSelectorParseResult::PseudoElement(pseudo_element)
             } else {
                 let pseudo_class = if is_functional {
                     input.parse_nested_block(|input| {
-                        parse_functional_pseudo_class(parser, input, name, inside_negation)
+                        parse_functional_pseudo_class(parser, input, name, state)
                     })?
                 } else {
-                    parse_simple_pseudo_class(parser, location, name)?
+                    parse_simple_pseudo_class(parser, location, name, state)?
                 };
-                Ok(Some(SimpleSelectorParseResult::SimpleSelector(
-                    pseudo_class,
-                )))
+                SimpleSelectorParseResult::SimpleSelector(pseudo_class)
             }
         },
         _ => {
             input.reset(&start);
-            Ok(None)
+            return Ok(None)
         },
-    }
+    }))
 }
 
 fn parse_simple_pseudo_class<'i, P, Impl>(
     parser: &P,
     location: SourceLocation,
     name: CowRcStr<'i>,
+    state: SelectorParsingState,
 ) -> Result<Component<Impl>, ParseError<'i, P::Error>>
 where
     P: Parser<'i, Impl = Impl>,
     Impl: SelectorImpl,
 {
-    (match_ignore_ascii_case! { &name,
-        "first-child" => Ok(Component::FirstChild),
-        "last-child"  => Ok(Component::LastChild),
-        "only-child"  => Ok(Component::OnlyChild),
-        "root" => Ok(Component::Root),
-        "empty" => Ok(Component::Empty),
-        "scope" => Ok(Component::Scope),
-        "host" if P::parse_host(parser) => Ok(Component::Host(None)),
-        "first-of-type" => Ok(Component::FirstOfType),
-        "last-of-type" => Ok(Component::LastOfType),
-        "only-of-type" => Ok(Component::OnlyOfType),
-        _ => Err(())
-    })
-    .or_else(|()| {
-        P::parse_non_ts_pseudo_class(parser, location, name).map(Component::NonTSPseudoClass)
-    })
+    if !state.allows_non_functional_pseudo_classes() {
+        return Err(location.new_custom_error(SelectorParseErrorKind::InvalidState));
+    }
+
+    if state.allows_tree_structural_pseudo_classes() {
+        match_ignore_ascii_case! { &name,
+            "first-child" => return Ok(Component::FirstChild),
+            "last-child" => return Ok(Component::LastChild),
+            "only-child" => return Ok(Component::OnlyChild),
+            "root" => return Ok(Component::Root),
+            "empty" => return Ok(Component::Empty),
+            "scope" => return Ok(Component::Scope),
+            "host" if P::parse_host(parser) => return Ok(Component::Host(None)),
+            "first-of-type" => return Ok(Component::FirstOfType),
+            "last-of-type" => return Ok(Component::LastOfType),
+            "only-of-type" => return Ok(Component::OnlyOfType),
+            _ => {},
+        }
+    }
+
+    let pseudo_class = P::parse_non_ts_pseudo_class(parser, location, name)?;
+    if state.intersects(SelectorParsingState::AFTER_PSEUDO_ELEMENT) && !pseudo_class.is_user_action_state() {
+        return Err(location.new_custom_error(SelectorParseErrorKind::InvalidState));
+    }
+    Ok(Component::NonTSPseudoClass(pseudo_class))
 }
 
 // NB: pub module in order to access the DummyParser
 #[cfg(test)]
 pub mod tests {
     use super::*;
     use crate::builder::HAS_PSEUDO_BIT;
     use crate::parser;
@@ -2213,35 +2216,33 @@ pub mod tests {
     pub enum PseudoElement {
         Before,
         After,
     }
 
     impl parser::PseudoElement for PseudoElement {
         type Impl = DummySelectorImpl;
 
-        fn supports_pseudo_class(&self, pc: &PseudoClass) -> bool {
-            match *pc {
-                PseudoClass::Hover => true,
-                PseudoClass::Active | PseudoClass::Lang(..) => false,
-            }
-        }
+        fn accepts_state_pseudo_classes(&self) -> bool { true }
 
-        fn valid_after_slotted(&self) -> bool {
-            true
-        }
+        fn valid_after_slotted(&self) -> bool { true }
     }
 
     impl parser::NonTSPseudoClass for PseudoClass {
         type Impl = DummySelectorImpl;
 
         #[inline]
         fn is_active_or_hover(&self) -> bool {
             matches!(*self, PseudoClass::Active | PseudoClass::Hover)
         }
+
+        #[inline]
+        fn is_user_action_state(&self) -> bool {
+            self.is_active_or_hover()
+        }
     }
 
     impl ToCss for PseudoClass {
         fn to_css<W>(&self, dest: &mut W) -> fmt::Result
         where
             W: fmt::Write,
         {
             match *self {
@@ -2784,21 +2785,21 @@ pub mod tests {
                 vec![
                     Component::PseudoElement(PseudoElement::Before),
                     Component::NonTSPseudoClass(PseudoClass::Hover),
                     Component::NonTSPseudoClass(PseudoClass::Hover),
                 ],
                 specificity(0, 2, 1) | HAS_PSEUDO_BIT,
             )]))
         );
-        assert!(parse("::before:hover:active").is_err());
+        assert!(parse("::before:hover:lang(foo)").is_err());
         assert!(parse("::before:hover .foo").is_err());
         assert!(parse("::before .foo").is_err());
         assert!(parse("::before ~ bar").is_err());
-        assert!(parse("::before:active").is_err());
+        assert!(parse("::before:active").is_ok());
 
         // https://github.com/servo/servo/issues/15335
         assert!(parse(":: before").is_err());
         assert_eq!(
             parse("div ::after"),
             Ok(SelectorList::from_vec(vec![Selector::from_vec(
                 vec![
                     Component::LocalName(LocalName {
--- a/servo/components/style/gecko/pseudo_element.rs
+++ b/servo/components/style/gecko/pseudo_element.rs
@@ -6,17 +6,17 @@
 //!
 //! Note that a few autogenerated bits of this live in
 //! `pseudo_element_definition.mako.rs`. If you touch that file, you probably
 //! need to update the checked-in files for Servo.
 
 use crate::gecko_bindings::structs::{self, PseudoStyleType};
 use crate::properties::longhands::display::computed_value::T as Display;
 use crate::properties::{ComputedValues, PropertyFlags};
-use crate::selector_parser::{NonTSPseudoClass, PseudoElementCascadeType, SelectorImpl};
+use crate::selector_parser::{PseudoElementCascadeType, SelectorImpl};
 use crate::str::{starts_with_ignore_ascii_case, string_as_ascii_lowercase};
 use crate::string_cache::Atom;
 use crate::values::serialize_atom_identifier;
 use cssparser::ToCss;
 use std::fmt;
 use thin_slice::ThinBoxedSlice;
 
 include!(concat!(
@@ -25,32 +25,30 @@ include!(concat!(
 ));
 
 impl ::selectors::parser::PseudoElement for PseudoElement {
     type Impl = SelectorImpl;
 
     // ::slotted() should support all tree-abiding pseudo-elements, see
     // https://drafts.csswg.org/css-scoping/#slotted-pseudo
     // https://drafts.csswg.org/css-pseudo-4/#treelike
+    #[inline]
     fn valid_after_slotted(&self) -> bool {
         matches!(
             *self,
             PseudoElement::Before |
                 PseudoElement::After |
                 PseudoElement::Marker |
                 PseudoElement::Placeholder
         )
     }
 
-    fn supports_pseudo_class(&self, pseudo_class: &NonTSPseudoClass) -> bool {
-        if !self.supports_user_action_state() {
-            return false;
-        }
-
-        return pseudo_class.is_safe_user_action_state();
+    #[inline]
+    fn accepts_state_pseudo_classes(&self) -> bool {
+        self.supports_user_action_state()
     }
 }
 
 impl PseudoElement {
     /// Returns the kind of cascade type that a given pseudo is going to use.
     ///
     /// In Gecko we only compute ::before and ::after eagerly. We save the rules
     /// for anonymous boxes separately, so we resolve them as precomputed
--- a/servo/components/style/gecko/selector_parser.rs
+++ b/servo/components/style/gecko/selector_parser.rs
@@ -179,26 +179,16 @@ impl NonTSPseudoClass {
             },
             // Otherwise, a pseudo-class is enabled in content when it
             // doesn't have any enabled flag.
             _ => !self
                 .has_any_flag(NonTSPseudoClassFlag::PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
         }
     }
 
-    /// <https://drafts.csswg.org/selectors-4/#useraction-pseudos>
-    ///
-    /// We intentionally skip the link-related ones.
-    pub fn is_safe_user_action_state(&self) -> bool {
-        matches!(
-            *self,
-            NonTSPseudoClass::Hover | NonTSPseudoClass::Active | NonTSPseudoClass::Focus
-        )
-    }
-
     /// Get the state flag associated with a pseudo-class, if any.
     pub fn state_flag(&self) -> ElementState {
         macro_rules! flag {
             (_) => {
                 ElementState::empty()
             };
             ($state:ident) => {
                 ElementState::$state
@@ -274,16 +264,25 @@ impl NonTSPseudoClass {
 
 impl ::selectors::parser::NonTSPseudoClass for NonTSPseudoClass {
     type Impl = SelectorImpl;
 
     #[inline]
     fn is_active_or_hover(&self) -> bool {
         matches!(*self, NonTSPseudoClass::Active | NonTSPseudoClass::Hover)
     }
+
+    /// We intentionally skip the link-related ones.
+    #[inline]
+    fn is_user_action_state(&self) -> bool {
+        matches!(
+            *self,
+            NonTSPseudoClass::Hover | NonTSPseudoClass::Active | NonTSPseudoClass::Focus
+        )
+    }
 }
 
 /// The dummy struct we use to implement our selector parsing.
 #[derive(Clone, Debug, Eq, PartialEq)]
 pub struct SelectorImpl;
 
 impl ::selectors::SelectorImpl for SelectorImpl {
     type ExtraMatchingData = InvalidationMatchingData;
--- a/taskcluster/ci/toolchain/sccache.yml
+++ b/taskcluster/ci/toolchain/sccache.yml
@@ -16,23 +16,23 @@ linux64-sccache:
     treeherder:
         symbol: TL(sccache)
     worker-type: aws-provisioner-v1/gecko-{level}-b-linux
     worker:
         max-run-time: 1800
     run:
         toolchain-artifact: public/build/sccache2.tar.xz
     toolchains:
-        - linux64-rust-1.28
+        - linux64-rust-1.34
         - linux64-binutils
 
 win64-sccache:
     treeherder:
         symbol: TW64(sccache)
     worker-type: aws-provisioner-v1/gecko-{level}-b-win2012
     worker:
         max-run-time: 3600
         env:
             TOOLTOOL_MANIFEST: "browser/config/tooltool-manifests/win64/sccache-build.manifest"
     run:
         toolchain-artifact: public/build/sccache2.tar.bz2
     toolchains:
-        - win64-rust-1.28
+        - win64-rust-1.34
deleted file mode 100644
--- a/taskcluster/docker/desktop1604-test/tester.env
+++ /dev/null
@@ -1,4 +0,0 @@
-GAIA_REV=tip
-GAIA_REF=tip
-GAIA_BASE_REPOSITORY=https://hg.mozilla.org/integration/gaia-central
-GAIA_HEAD_REPOSITORY=https://hg.mozilla.org/integration/gaia-central
--- a/taskcluster/scripts/misc/build-sccache.sh
+++ b/taskcluster/scripts/misc/build-sccache.sh
@@ -1,13 +1,13 @@
 #!/bin/bash
 set -x -e -v
 
-# 0.2.7 + a number of changes
-SCCACHE_REVISION=8f295c09cfdd4cff4f4a0c6f0e057979eeb8842d
+# 0.2.8 + a number of changes
+SCCACHE_REVISION=5cbd30684e03cab9c2d1272fdc530fd83b8c903b
 
 # This script is for building sccache
 
 case "$(uname -s)" in
 Linux)
     WORKSPACE=$HOME/workspace
     UPLOAD_DIR=$HOME/artifacts
     COMPRESS_EXT=xz
@@ -61,17 +61,17 @@ EOF
     make -j `nproc --all`
     # `make install` installs a *ton* of docs that we don't care about.
     # Just the software, please.
     make install_sw
     popd
 
     # We don't need to set OPENSSL_STATIC here, because we only have static
     # libraries in the directory we are passing.
-    env "OPENSSL_DIR=$OPENSSL_BUILD_DIRECTORY" cargo build --verbose --release
+    env "OPENSSL_DIR=$OPENSSL_BUILD_DIRECTORY" cargo build --features "all dist-server" --verbose --release
     ;;
 MINGW*)
     cargo build --verbose --release
     ;;
 esac
 
 mkdir sccache2
 cp target/release/sccache* sccache2/
--- a/testing/mozharness/configs/raptor/mac_config.py
+++ b/testing/mozharness/configs/raptor/mac_config.py
@@ -1,18 +1,8 @@
-ENABLE_SCREEN_RESOLUTION_CHECK = True
-
-SCREEN_RESOLUTION_CHECK = {
-    "name": "check_screen_resolution",
-    "cmd": ["bash", "-c", "screenresolution get && screenresolution list && system_profiler SPDisplaysDataType"],
-    "architectures": ["32bit", "64bit"],
-    "halt_on_failure": False,
-    "enabled": ENABLE_SCREEN_RESOLUTION_CHECK
-}
-
 import os
 
 VENV_PATH = '%s/build/venv' % os.getcwd()
 
 config = {
     "log_name": "raptor",
     "installer_path": "installer.exe",
     "virtualenv_path": VENV_PATH,
@@ -22,18 +12,14 @@ config = {
         "download-and-extract",
         "populate-webroot",
         "install-chromium-distribution",
         "create-virtualenv",
         "install",
         "run-tests",
     ],
     "run_cmd_checks_enabled": True,
-    "preflight_run_cmd_suites": [
-        SCREEN_RESOLUTION_CHECK,
-    ],
-    "postflight_run_cmd_suites": [
-        SCREEN_RESOLUTION_CHECK,
-    ],
+    "preflight_run_cmd_suites": [],
+    "postflight_run_cmd_suites": [],
     "minidump_stackwalk_path": "macosx64-minidump_stackwalk",
     "minidump_tooltool_manifest_path": "config/tooltool-manifests/macosx64/releng.manifest",
     "tooltool_cache": "/builds/tooltool_cache",
 }
--- a/testing/mozharness/configs/talos/mac_config.py
+++ b/testing/mozharness/configs/talos/mac_config.py
@@ -1,18 +1,8 @@
-ENABLE_SCREEN_RESOLUTION_CHECK = True
-
-SCREEN_RESOLUTION_CHECK = {
-    "name": "check_screen_resolution",
-    "cmd": ["bash", "-c", "screenresolution get && screenresolution list && system_profiler SPDisplaysDataType"],
-    "architectures": ["32bit", "64bit"],
-    "halt_on_failure": False,
-    "enabled": ENABLE_SCREEN_RESOLUTION_CHECK
-}
-
 import os
 
 VENV_PATH = '%s/build/venv' % os.getcwd()
 
 config = {
     "log_name": "talos",
     "installer_path": "installer.exe",
     "virtualenv_path": VENV_PATH,
@@ -22,18 +12,14 @@ config = {
         "download-and-extract",
         "populate-webroot",
         "create-virtualenv",
         "install",
         "setup-mitmproxy",
         "run-tests",
     ],
     "run_cmd_checks_enabled": True,
-    "preflight_run_cmd_suites": [
-        SCREEN_RESOLUTION_CHECK,
-    ],
-    "postflight_run_cmd_suites": [
-        SCREEN_RESOLUTION_CHECK,
-    ],
+    "preflight_run_cmd_suites": [],
+    "postflight_run_cmd_suites": [],
     "minidump_stackwalk_path": "macosx64-minidump_stackwalk",
     "minidump_tooltool_manifest_path": "config/tooltool-manifests/macosx64/releng.manifest",
     "tooltool_cache": "/builds/tooltool_cache",
 }
--- a/testing/web-platform/meta/webrtc/RTCPeerConnection-createDataChannel.html.ini
+++ b/testing/web-platform/meta/webrtc/RTCPeerConnection-createDataChannel.html.ini
@@ -10,28 +10,16 @@
   [createDataChannel with both maxPacketLifeTime and maxRetransmits should throw SyntaxError]
     expected: FAIL
     bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1531894
 
   [createDataChannel with negotiated true should succeed]
     expected: FAIL
     bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1529695
 
-  [createDataChannel with id -1 should throw TypeError]
-    expected: FAIL
-    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1531904
-
-  [createDataChannel with id 65535 should throw TypeError]
-    expected: FAIL
-    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1531904
-
-  [createDataChannel with id 65536 should throw TypeError]
-    expected: FAIL
-    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1531904
-
   [createDataChannel with priority "high" should succeed]
     expected: FAIL
     bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1531100
 
   [createDataChannel with invalid priority should throw TypeError]
     expected: FAIL
     bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1531100
 
--- a/testing/web-platform/tests/webrtc/RTCPeerConnection-createDataChannel.html
+++ b/testing/web-platform/tests/webrtc/RTCPeerConnection-createDataChannel.html
@@ -253,40 +253,56 @@ for (const [description, protocol, expec
 /*
   6.2.  RTCDataChannel
     createDataChannel
       11. Let channel have an [[Negotiated]] internal slot initialized to option's
           negotiated member.
  */
 test(() => {
   const pc = new RTCPeerConnection;
-  const channel = pc.createDataChannel('', { negotiated: true });
+  const channel = pc.createDataChannel('', { negotiated: true, id: 1 });
   assert_equals(channel.negotiated, true);
 }, 'createDataChannel with negotiated true should succeed');
 
 
 /*
   6.2.  RTCDataChannel
     createDataChannel
       10. If id is equal to 65535, which is greater than the maximum allowed ID
           of 65534 but still qualifies as an unsigned short, throw a TypeError.
  */
+for (const id of [0, 1, 65534, 65535]) {
+  test(() => {
+    const pc = new RTCPeerConnection();
+    const channel = pc.createDataChannel('', { id });
+    assert_equals(channel.id, null);
+  }, `createDataChannel with id ${id} and negotiated not set should succeed, but not set the channel's id`);
+}
+
 for (const id of [0, 1, 65534]) {
   test(() => {
     const pc = new RTCPeerConnection();
-    const channel = pc.createDataChannel('', { id });
+    const channel = pc.createDataChannel('', { id, negotiated: true });
     assert_equals(channel.id, id);
-  }, `createDataChannel with id ${id} should succeed`);
+  }, `createDataChannel with id ${id} and negotiated true should succeed, and set the channel's id`);
+}
+
+for (const id of [-1, 65536]) {
+  test(() => {
+    const pc = new RTCPeerConnection();
+    assert_throws(new TypeError(), () => pc.createDataChannel('', { id }));
+  }, `createDataChannel with id ${id} and negotiated not set should throw TypeError`);
 }
 
 for (const id of [-1, 65535, 65536]) {
   test(() => {
     const pc = new RTCPeerConnection();
-    assert_throws(new TypeError(), () => pc.createDataChannel('', { id }));
-  }, `createDataChannel with id ${id} should throw TypeError`);
+    assert_throws(new TypeError(), () => pc.createDataChannel('',
+      { id, negotiated: true }));
+  }, `createDataChannel with id ${id} and negotiated true should throw TypeError`);
 }
 
 /*
   6.2.  RTCDataChannel
     createDataChannel
       12. Let channel have an [[DataChannelPriority]] internal slot initialized
           to option's priority member.
 
@@ -327,19 +343,28 @@ test(() => {
     pc.createDataChannel('', {
       protocol: ' '.repeat(65536),
       negotiated: false
     }));
 }, 'createDataChannel with negotiated false and long protocol should throw TypeError');
 
 test(() => {
   const pc = new RTCPeerConnection();
+  assert_throws(new TypeError(), () =>
+    pc.createDataChannel('', {
+      negotiated: true
+    }));
+}, 'createDataChannel with negotiated true and no id should fail with TypeError');
+
+test(() => {
+  const pc = new RTCPeerConnection();
   const label = ' '.repeat(65536)
 
   const channel = pc.createDataChannel('', {
+    id: 1,
     label,
     protocol: ' '.repeat(65536),
     negotiated: true
   });
 
   assert_equals(channel.label, label);
 }, 'createDataChannel with negotiated true and long label and long protocol should succeed');
 
--- a/testing/web-platform/tests/webrtc/RTCPeerConnection-ondatachannel.html
+++ b/testing/web-platform/tests/webrtc/RTCPeerConnection-ondatachannel.html
@@ -153,16 +153,17 @@
     t.add_cleanup(() => localPc.close());
     const remotePc = new RTCPeerConnection();
 
     t.add_cleanup(() => remotePc.close());
 
     const onDataChannel = t.unreached_func('datachannel event should not be fired');
 
     localPc.createDataChannel('test', {
+      id: 1,
       negotiated: true
     });
 
     remotePc.addEventListener('datachannel', onDataChannel);
     exchangeIceCandidates(localPc, remotePc);
     doSignalingHandshake(localPc, remotePc);
 
     t.step_timeout(t.step_func_done(), 200);
--- a/testing/web-platform/tests/webrtc/RTCRtpTransceiver.https.html
+++ b/testing/web-platform/tests/webrtc/RTCRtpTransceiver.https.html
@@ -2213,16 +2213,48 @@
     await pc1.setLocalDescription(offer);
     await pc2.setRemoteDescription(offer);
 
     const answer = await pc2.createAnswer();
     await pc2.setLocalDescription(answer);
     await pc1.setRemoteDescription(answer);
   };
 
+  const checkAddIceCandidateToStoppedTransceiver = async t => {
+    const pc1 = new RTCPeerConnection();
+    t.add_cleanup(() => pc1.close());
+    const pc2 = new RTCPeerConnection();
+    t.add_cleanup(() => pc2.close());
+
+    const stream = await getNoiseStream({audio: true, video: true});
+    t.add_cleanup(() => stopTracks(stream));
+    const audio = stream.getAudioTracks()[0];
+    const video = stream.getVideoTracks()[0];
+
+    pc1.addTrack(audio, stream);
+    pc1.addTrack(video, stream);
+
+    pc2.addTrack(audio, stream);
+    pc2.addTrack(video, stream);
+
+    await pc1.setLocalDescription(await pc1.createOffer());
+    pc1.getTransceivers()[1].stop();
+    pc1.setLocalDescription({type: "rollback"});
+
+    const offer = await pc2.createOffer();
+    await pc2.setLocalDescription(offer);
+    await pc1.setRemoteDescription(offer);
+
+    await pc1.addIceCandidate(
+      {
+        candidate: "candidate:0 1 UDP 2122252543 192.168.1.112 64261 typ host",
+        sdpMid: pc2.getTransceivers()[1].mid
+      });
+  };
+
 const tests = [
   checkAddTransceiverNoTrack,
   checkAddTransceiverWithTrack,
   checkAddTransceiverWithAddTrack,
   checkAddTransceiverWithDirection,
   checkAddTransceiverWithSetRemoteOfferSending,
   checkAddTransceiverWithSetRemoteOfferNoSend,
   checkAddTransceiverBadKind,
@@ -2248,12 +2280,13 @@ const tests = [
   checkStopAfterSetRemoteOffer,
   checkStopAfterCreateAnswer,
   checkStopAfterSetLocalAnswer,
   checkStopAfterClose,
   checkLocalRollback,
   checkRollbackAndSetRemoteOfferWithDifferentType,
   checkRemoteRollback,
   checkMsectionReuse,
-  checkStopAfterCreateOfferWithReusedMsection
+  checkStopAfterCreateOfferWithReusedMsection,
+  checkAddIceCandidateToStoppedTransceiver
 ].forEach(test => promise_test(test, test.name));
 
 </script>
--- a/third_party/rust/lmdb-rkv-sys/.cargo-checksum.json
+++ b/third_party/rust/lmdb-rkv-sys/.cargo-checksum.json
@@ -1,1 +1,1 @@
-{"files":{"Cargo.toml":"89945728564108bb284dc515a9048ffaae5016b58ea8781d9f6af7e6d4ce9000","build.rs":"4204dd0891e2eca08cb7cbcccfb36d1bdac137651c383f4a36368478fd836402","lmdb/libraries/liblmdb/CHANGES":"12f41155ee12375b7ddeabd2f793399d3216e92bc44f1122d6f305b7637c2fec","lmdb/libraries/liblmdb/COPYRIGHT":"7cf04234accacc7b41b73fe6b3b19759fa49dee1ce705a9fa6533900400e4cca","lmdb/libraries/liblmdb/Doxyfile":"5545f6b049040ce58e6d1a603eaea6b7fb8ae92459f2ab8d3bcbacabcce1014d","lmdb/libraries/liblmdb/LICENSE":"310fe25c858a9515fc8c8d7d1f24a67c9496f84a91e0a0e41ea9975b1371e569","lmdb/libraries/liblmdb/Makefile":"60b5f574e6642602f692a95956da61c588a265ad50b8059960c230b9e6aaf4fd","lmdb/libraries/liblmdb/intro.doc":"9442e0db4fc9c70f058c43545e710476d8d5a80b959d20f4381240fd50c6b843","lmdb/libraries/liblmdb/lmdb.h":"a8194f7a061f4168d99de3c80464de5d279b2c851c24fdd102f1f14dd5828a77","lmdb/libraries/liblmdb/mdb.c":"87d29ea006d0c4dc31a1bc960e37db33c971abe8b3e73de5041813e0333ad8b5","lmdb/libraries/liblmdb/mdb_copy.1":"3a6a8a7a91e1bd42dc4d2a0188ff62d699ff2b3b097a670f30681decf63f22f3","lmdb/libraries/liblmdb/mdb_copy.c":"d3d412a770a5c3afeb88c44b4acdde0f0b985cde22497198e8f38296281cdddd","lmdb/libraries/liblmdb/mdb_dump.1":"9257be883c7fcfcbd61003cc730f7c0900fa8f6feba074c8c1e46634a257b13a","lmdb/libraries/liblmdb/mdb_dump.c":"b046cffcd997254e6daea47a2d7fb74f9d23282174cbb1e3bf9f5fb51a90fe64","lmdb/libraries/liblmdb/mdb_load.1":"ea927473245a4a7777ba687aa26baf7f0951fb620daf82b8d730a090185b2bbc","lmdb/libraries/liblmdb/mdb_load.c":"4f722613c65350315db23060be98584fb572978108885dab271101ba7187dca4","lmdb/libraries/liblmdb/mdb_stat.1":"c0a70d96b4b2d32e73301383d9d5620bc0bbbefb019bfd54f32088dfd4bc921a","lmdb/libraries/liblmdb/mdb_stat.c":"e6405fa191d784ecfa8eb8d1f153a58facc49a8f5a2c891a93802e67acc4861e","lmdb/libraries/liblmdb/midl.c":"92b0933c7736443448018a93f9588e9e26ae2e242f91b19211dea9ed3ab91141","lmdb/libraries/liblmdb/midl.h":"8e01e6c3b1cbd2e93a5a95686a6dd0ca43878b2b8cf0ba53962ccfe355605354","lmdb/libraries/liblmdb/mtest.c":"89ab9ac8bf1e14a9f32a33757c4b3254e4984e0f24e5a302e2d126eb2c86f6db","lmdb/libraries/liblmdb/mtest2.c":"076b00395fe1461dd9577f7bb5567908ce50cf470efbf652787e6fe1dc2fb68c","lmdb/libraries/liblmdb/mtest3.c":"51b9a055e123bd0757ee3082cc6864c836969cf630e646a9cc34e01398c20634","lmdb/libraries/liblmdb/mtest4.c":"b0a725405d80bda6ab95b3ecf410ae330ab8df7a081ca81dd6ea1f8db87642e9","lmdb/libraries/liblmdb/mtest5.c":"7f3b06ca3833315ea4c70d5e91feb1b677f6949f105f4f89d96c3ac35e104f2f","lmdb/libraries/liblmdb/mtest6.c":"e4d7880c36547ebf33bc020046730bf2c075c53aaacd5c876152cc5ae7ab5e6c","lmdb/libraries/liblmdb/sample-bdb.txt":"153d84f8fc49a3abba53ed52d5a41c8d6d4698753a10bbe0689a9e65d3513513","lmdb/libraries/liblmdb/sample-mdb.txt":"1f77385786cffdf72b33da06a91a444fe2827673c3627f89110903a8fe012795","lmdb/libraries/liblmdb/tooltag":"4734c6dc1fa7aec8c2e9646bd04bc5218ef6a03ad83a3b18de2ac4069eb94120","src/constants.rs":"af67740b5acccdc71b2267ec051bb60e5433c4f0313fe16dc0627376a52dcdff","src/ffi.rs":"caa9bbfb3868a7a9e9ad822d775e60ffa8c8c2f2450ac4ed403a93ddb7547899","src/lib.rs":"1d3222feec7b2b4e0902c3b9d7d37d51f5ae5cce8a3ff3dcf0256b7d78f0fecb"},"package":"96846a2e6785ec0fce6577479d18273c8e5b287e6df8a1b398b7f0f7a41cdcbb"}
\ No newline at end of file
+{"files":{"Cargo.toml":"60155c7f910da559c7ab932a66d0bede7c3734dbf9c4d2abeb4e23d5e4d0546a","build.rs":"d001be8ded3d94c0ae26dd355b5481dc6c0e20e05ea55cbfbb9df572e0678fbc","lmdb/libraries/liblmdb/CHANGES":"12f41155ee12375b7ddeabd2f793399d3216e92bc44f1122d6f305b7637c2fec","lmdb/libraries/liblmdb/COPYRIGHT":"7cf04234accacc7b41b73fe6b3b19759fa49dee1ce705a9fa6533900400e4cca","lmdb/libraries/liblmdb/Doxyfile":"5545f6b049040ce58e6d1a603eaea6b7fb8ae92459f2ab8d3bcbacabcce1014d","lmdb/libraries/liblmdb/LICENSE":"310fe25c858a9515fc8c8d7d1f24a67c9496f84a91e0a0e41ea9975b1371e569","lmdb/libraries/liblmdb/Makefile":"60b5f574e6642602f692a95956da61c588a265ad50b8059960c230b9e6aaf4fd","lmdb/libraries/liblmdb/intro.doc":"9442e0db4fc9c70f058c43545e710476d8d5a80b959d20f4381240fd50c6b843","lmdb/libraries/liblmdb/lmdb.h":"a8194f7a061f4168d99de3c80464de5d279b2c851c24fdd102f1f14dd5828a77","lmdb/libraries/liblmdb/mdb.c":"87d29ea006d0c4dc31a1bc960e37db33c971abe8b3e73de5041813e0333ad8b5","lmdb/libraries/liblmdb/mdb_copy.1":"3a6a8a7a91e1bd42dc4d2a0188ff62d699ff2b3b097a670f30681decf63f22f3","lmdb/libraries/liblmdb/mdb_copy.c":"d3d412a770a5c3afeb88c44b4acdde0f0b985cde22497198e8f38296281cdddd","lmdb/libraries/liblmdb/mdb_dump.1":"9257be883c7fcfcbd61003cc730f7c0900fa8f6feba074c8c1e46634a257b13a","lmdb/libraries/liblmdb/mdb_dump.c":"b046cffcd997254e6daea47a2d7fb74f9d23282174cbb1e3bf9f5fb51a90fe64","lmdb/libraries/liblmdb/mdb_load.1":"ea927473245a4a7777ba687aa26baf7f0951fb620daf82b8d730a090185b2bbc","lmdb/libraries/liblmdb/mdb_load.c":"4f722613c65350315db23060be98584fb572978108885dab271101ba7187dca4","lmdb/libraries/liblmdb/mdb_stat.1":"c0a70d96b4b2d32e73301383d9d5620bc0bbbefb019bfd54f32088dfd4bc921a","lmdb/libraries/liblmdb/mdb_stat.c":"e6405fa191d784ecfa8eb8d1f153a58facc49a8f5a2c891a93802e67acc4861e","lmdb/libraries/liblmdb/midl.c":"92b0933c7736443448018a93f9588e9e26ae2e242f91b19211dea9ed3ab91141","lmdb/libraries/liblmdb/midl.h":"b8fa0d35b5505001d9f722f8f23993b8602c3599eb71ec869bb556f5e4ef2fde","lmdb/libraries/liblmdb/mtest.c":"89ab9ac8bf1e14a9f32a33757c4b3254e4984e0f24e5a302e2d126eb2c86f6db","lmdb/libraries/liblmdb/mtest2.c":"076b00395fe1461dd9577f7bb5567908ce50cf470efbf652787e6fe1dc2fb68c","lmdb/libraries/liblmdb/mtest3.c":"51b9a055e123bd0757ee3082cc6864c836969cf630e646a9cc34e01398c20634","lmdb/libraries/liblmdb/mtest4.c":"b0a725405d80bda6ab95b3ecf410ae330ab8df7a081ca81dd6ea1f8db87642e9","lmdb/libraries/liblmdb/mtest5.c":"7f3b06ca3833315ea4c70d5e91feb1b677f6949f105f4f89d96c3ac35e104f2f","lmdb/libraries/liblmdb/mtest6.c":"e4d7880c36547ebf33bc020046730bf2c075c53aaacd5c876152cc5ae7ab5e6c","lmdb/libraries/liblmdb/sample-bdb.txt":"153d84f8fc49a3abba53ed52d5a41c8d6d4698753a10bbe0689a9e65d3513513","lmdb/libraries/liblmdb/sample-mdb.txt":"1f77385786cffdf72b33da06a91a444fe2827673c3627f89110903a8fe012795","lmdb/libraries/liblmdb/tooltag":"4734c6dc1fa7aec8c2e9646bd04bc5218ef6a03ad83a3b18de2ac4069eb94120","src/constants.rs":"af67740b5acccdc71b2267ec051bb60e5433c4f0313fe16dc0627376a52dcdff","src/ffi.rs":"caa9bbfb3868a7a9e9ad822d775e60ffa8c8c2f2450ac4ed403a93ddb7547899","src/lib.rs":"babc1389cd28645a92cf457096b70be8bf549ed1a0e404cfa32f328f854223ae"},"package":"1470e0168f1832e35afd6d0931ae60db625685332837b97aa156773ec9c5e393"}
\ No newline at end of file
--- a/third_party/rust/lmdb-rkv-sys/Cargo.toml
+++ b/third_party/rust/lmdb-rkv-sys/Cargo.toml
@@ -1,31 +1,42 @@
 # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
 #
 # When uploading crates to the registry Cargo will automatically
 # "normalize" Cargo.toml files for maximal compatibility
 # with all versions of Cargo and also rewrite `path` dependencies
-# to registry (e.g. crates.io) dependencies
+# to registry (e.g., crates.io) dependencies
 #
 # If you believe there's an error in this file please file an
 # issue against the rust-lang/cargo repository. If you're
 # editing this file be aware that the upstream Cargo.toml
 # will likely look very different (and much more reasonable)
 
 [package]
 name = "lmdb-rkv-sys"
-version = "0.8.2"
+version = "0.8.3"
 authors = ["Dan Burkert <dan@danburkert.com>"]
 build = "build.rs"
 description = "Rust bindings for liblmdb."
 documentation = "https://docs.rs/lmdb-rkv-sys"
 readme = "../README.md"
 keywords = ["LMDB", "database", "storage-engine", "bindings", "library"]
 categories = ["database", "external-ffi-bindings"]
 license = "Apache-2.0"
 repository = "https://github.com/mozilla/lmdb-rs.git"
 [dependencies.libc]
 version = "0.2"
 [build-dependencies.cc]
 version = "1"
 
 [build-dependencies.pkg-config]
 version = "0.3.2"
+
+[features]
+default = []
+mdb_idl_logn_10 = []
+mdb_idl_logn_11 = []
+mdb_idl_logn_12 = []
+mdb_idl_logn_13 = []
+mdb_idl_logn_14 = []
+mdb_idl_logn_15 = []
+mdb_idl_logn_8 = []
+mdb_idl_logn_9 = []
--- a/third_party/rust/lmdb-rkv-sys/build.rs
+++ b/third_party/rust/lmdb-rkv-sys/build.rs
@@ -1,21 +1,50 @@
 extern crate pkg_config;
 extern crate cc;
 
 use std::env;
 use std::path::PathBuf;
 
+#[cfg(feature = "mdb_idl_logn_8")]
+const MDB_IDL_LOGN: u8 = 8;
+#[cfg(feature = "mdb_idl_logn_9")]
+const MDB_IDL_LOGN: u8 = 9;
+#[cfg(feature = "mdb_idl_logn_10")]
+const MDB_IDL_LOGN: u8 = 10;
+#[cfg(feature = "mdb_idl_logn_11")]
+const MDB_IDL_LOGN: u8 = 11;
+#[cfg(feature = "mdb_idl_logn_12")]
+const MDB_IDL_LOGN: u8 = 12;
+#[cfg(feature = "mdb_idl_logn_13")]
+const MDB_IDL_LOGN: u8 = 13;
+#[cfg(feature = "mdb_idl_logn_14")]
+const MDB_IDL_LOGN: u8 = 14;
+#[cfg(feature = "mdb_idl_logn_15")]
+const MDB_IDL_LOGN: u8 = 15;
+#[cfg(not(any(
+    feature = "mdb_idl_logn_8",
+    feature = "mdb_idl_logn_9",
+    feature = "mdb_idl_logn_10",
+    feature = "mdb_idl_logn_11",
+    feature = "mdb_idl_logn_12",
+    feature = "mdb_idl_logn_13",
+    feature = "mdb_idl_logn_14",
+    feature = "mdb_idl_logn_15",
+)))]
+const MDB_IDL_LOGN: u8 = 16;
+
 fn main() {
     let mut lmdb: PathBuf = PathBuf::from(&env::var("CARGO_MANIFEST_DIR").unwrap());
     lmdb.push("lmdb");
     lmdb.push("libraries");
     lmdb.push("liblmdb");
 
     if !pkg_config::find_library("liblmdb").is_ok() {
         cc::Build::new()
+                    .define("MDB_IDL_LOGN", Some(MDB_IDL_LOGN.to_string().as_str()))
                     .file(lmdb.join("mdb.c"))
                     .file(lmdb.join("midl.c"))
                     // https://github.com/LMDB/lmdb/blob/LMDB_0.9.21/libraries/liblmdb/Makefile#L25
                     .opt_level(2)
                     .compile("liblmdb.a")
     }
 }
--- a/third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/midl.h
+++ b/third_party/rust/lmdb-rkv-sys/lmdb/libraries/liblmdb/midl.h
@@ -51,17 +51,19 @@ typedef size_t MDB_ID;
 	 * sorted in ascending order. For libmdb IDLs are sorted in
 	 * descending order.
 	 */
 typedef MDB_ID *MDB_IDL;
 
 /* IDL sizes - likely should be even bigger
  *   limiting factors: sizeof(ID), thread stack size
  */
+#ifndef MDB_IDL_LOGN
 #define	MDB_IDL_LOGN	16	/* DB_SIZE is 2^16, UM_SIZE is 2^17 */
+#endif
 #define MDB_IDL_DB_SIZE		(1<<MDB_IDL_LOGN)
 #define MDB_IDL_UM_SIZE		(1<<(MDB_IDL_LOGN+1))
 
 #define MDB_IDL_DB_MAX		(MDB_IDL_DB_SIZE-1)
 #define MDB_IDL_UM_MAX		(MDB_IDL_UM_SIZE-1)
 
 #define MDB_IDL_SIZEOF(ids)		(((ids)[0]+1) * sizeof(MDB_ID))
 #define MDB_IDL_IS_ZERO(ids) ( (ids)[0] == 0 )
--- a/third_party/rust/lmdb-rkv-sys/src/lib.rs
+++ b/third_party/rust/lmdb-rkv-sys/src/lib.rs
@@ -1,11 +1,11 @@
 #![allow(non_camel_case_types)]
 #![deny(warnings)]
-#![doc(html_root_url = "https://docs.rs/lmdb-rkv-sys/0.8.2")]
+#![doc(html_root_url = "https://docs.rs/lmdb-rkv-sys/0.8.3")]
 
 extern crate libc;
 
 #[cfg(unix)]
 #[allow(non_camel_case_types)]
 pub type mode_t = ::libc::mode_t;
 #[cfg(windows)]
 #[allow(non_camel_case_types)]
--- a/toolkit/components/antitracking/test/browser/browser_referrerDefaultPolicy.js
+++ b/toolkit/components/antitracking/test/browser/browser_referrerDefaultPolicy.js
@@ -1,52 +1,223 @@
+requestLongerTimeout(4);
+
 const CHROME_BASE = "chrome://mochitests/content/browser/browser/base/content/test/general/";
 Services.scriptloader.loadSubScript(CHROME_BASE + "head.js", this);
 /* import-globals-from ../../../../../browser/base/content/test/general/head.js */
 
-async function testOnWindow(private, expectedReferrer) {
+async function testOnWindow(private, expectedReferrer, rp) {
   info("Creating a new " + (private ? "private" : "normal") + " window");
   let win = await BrowserTestUtils.openNewBrowserWindow({private});
   let browser = win.gBrowser;
   let tab = browser.selectedTab;
   let b = browser.getBrowserForTab(tab);
   await promiseTabLoadEvent(tab, TEST_TOP_PAGE);
 
   info("Loading tracking scripts and tracking images");
-  await ContentTask.spawn(b, null, async function() {
-    {
-      let src = content.document.createElement("script");
-      let p = new content.Promise(resolve => { src.onload = resolve; });
-      content.document.body.appendChild(src);
-      src.src = "https://tracking.example.org/browser/toolkit/components/antitracking/test/browser/referrer.sjs?what=script";
-      await p;
-    }
-
+  await ContentTask.spawn(b, {rp}, async function({rp}) {
     {
       let img = content.document.createElement("img");
       let p = new content.Promise(resolve => { img.onload = resolve; });
       content.document.body.appendChild(img);
+      if (rp) {
+        img.referrerPolicy = rp;
+      }
       img.src = "https://tracking.example.org/browser/toolkit/components/antitracking/test/browser/referrer.sjs?what=image";
       await p;
     }
   });
 
   await fetch("https://tracking.example.org/browser/toolkit/components/antitracking/test/browser/referrer.sjs?result&what=image")
     .then(r => r.text())
     .then(text => {
       is(text, expectedReferrer, "We sent the correct Referer header");
     });
 
-  await fetch("https://tracking.example.org/browser/toolkit/components/antitracking/test/browser/referrer.sjs?result&what=script")
-    .then(r => r.text())
-    .then(text => {
-      is(text, expectedReferrer, "We sent the correct Referer header");
-    });
+  await BrowserTestUtils.closeWindow(win);
+}
+
+function pn(name, private) {
+  return private ? (name + ".pbmode") : name;
+}
+
+async function testOnNoReferrer(private) {
+  // no-referrer pref when no-referrer is forced
+  await SpecialPowers.pushPrefEnv({"set":
+    [[pn("network.http.referer.defaultPolicy.trackers", private), 3]]});
+  await testOnWindow(private, "", "no-referrer");
+
+  // strict-origin-when-cross-origin pref when no-referrer is forced
+  await SpecialPowers.pushPrefEnv({"set":
+    [[pn("network.http.referer.defaultPolicy.trackers", private), 2]]});
+  await testOnWindow(private, "", "no-referrer");
+
+  // same-origin pref when no-referrer is forced
+  await SpecialPowers.pushPrefEnv({"set":
+    [[pn("network.http.referer.defaultPolicy.trackers", private), 1]]});
+  await testOnWindow(private, "", "no-referrer");
+
+  // no-referrer pref when no-referrer is forced
+  await SpecialPowers.pushPrefEnv({"set":
+    [[pn("network.http.referer.defaultPolicy.trackers", private), 0]]});
+  await testOnWindow(private, "", "no-referrer");
+}
+
+async function testOnSameOrigin(private) {
+  // same-origin pref when same-origin is forced
+  await SpecialPowers.pushPrefEnv({"set":
+    [[pn("network.http.referer.defaultPolicy.trackers", private), 3]]});
+  await testOnWindow(private, "", "same-origin");
+
+  // strict-origin-when-cross-origin pref when same-origin is forced
+  await SpecialPowers.pushPrefEnv({"set":
+    [[pn("network.http.referer.defaultPolicy.trackers", private), 2]]});
+  await testOnWindow(private, "", "same-origin");
+
+  // same-origin pref when same-origin is forced
+  await SpecialPowers.pushPrefEnv({"set":
+    [[pn("network.http.referer.defaultPolicy.trackers", private), 1]]});
+  await testOnWindow(private, "", "same-origin");
+
+  // same-origin pref when same-origin is forced
+  await SpecialPowers.pushPrefEnv({"set":
+    [[pn("network.http.referer.defaultPolicy.trackers", private), 0]]});
+  await testOnWindow(private, "", "same-origin");
+}
+
+async function testOnNoReferrerWhenDowngrade(private) {
+  // no-referrer-when-downgrade pref when no-referrer-when-downgrade is forced
+  await SpecialPowers.pushPrefEnv({"set":
+    [[pn("network.http.referer.defaultPolicy.trackers", private), 3]]});
+  await testOnWindow(private, TEST_TOP_PAGE, "no-referrer-when-downgrade");
+
+  // strict-origin-when-cross-origin pref when no-referrer-when-downgrade is forced
+  await SpecialPowers.pushPrefEnv({"set":
+    [[pn("network.http.referer.defaultPolicy.trackers", private), 2]]});
+  await testOnWindow(private, TEST_TOP_PAGE, "no-referrer-when-downgrade");
+
+  // same-origin pref when no-referrer-when-downgrade is forced
+  await SpecialPowers.pushPrefEnv({"set":
+    [[pn("network.http.referer.defaultPolicy.trackers", private), 1]]});
+  await testOnWindow(private, TEST_TOP_PAGE, "no-referrer-when-downgrade");
+
+  // no-referrer pref when no-referrer-when-downgrade is forced
+  await SpecialPowers.pushPrefEnv({"set":
+    [[pn("network.http.referer.defaultPolicy.trackers", private), 0]]});
+  await testOnWindow(private, TEST_TOP_PAGE, "no-referrer-when-downgrade");
+}
+
+async function testOnOrigin(private) {
+  // origin pref when origin is forced
+  await SpecialPowers.pushPrefEnv({"set":
+    [[pn("network.http.referer.defaultPolicy.trackers", private), 3]]});
+  await testOnWindow(private, TEST_DOMAIN, "origin");
+
+  // strict-origin pref when origin is forced
+  await SpecialPowers.pushPrefEnv({"set":
+    [[pn("network.http.referer.defaultPolicy.trackers", private), 2]]});
+  await testOnWindow(private, TEST_DOMAIN, "origin");
+
+  // same-origin pref when origin is forced
+  await SpecialPowers.pushPrefEnv({"set":
+    [[pn("network.http.referer.defaultPolicy.trackers", private), 1]]});
+  await testOnWindow(private, TEST_DOMAIN, "origin");
 
-  await BrowserTestUtils.closeWindow(win);
+  // no-referrer pref when origin is forced
+  await SpecialPowers.pushPrefEnv({"set":
+    [[pn("network.http.referer.defaultPolicy.trackers", private), 0]]});
+  await testOnWindow(private, TEST_DOMAIN, "origin");
+}
+
+async function testOnStrictOrigin(private) {
+  // strict-origin pref when strict-origin is forced
+  await SpecialPowers.pushPrefEnv({"set":
+    [[pn("network.http.referer.defaultPolicy.trackers", private), 3]]});
+  await testOnWindow(private, TEST_DOMAIN, "strict-origin");
+
+  // strict-origin pref when strict-origin is forced
+  await SpecialPowers.pushPrefEnv({"set":
+    [[pn("network.http.referer.defaultPolicy.trackers", private), 2]]});
+  await testOnWindow(private, TEST_DOMAIN, "strict-origin");
+
+  // same-origin pref when strict-origin is forced
+  await SpecialPowers.pushPrefEnv({"set":
+    [[pn("network.http.referer.defaultPolicy.trackers", private), 1]]});
+  await testOnWindow(private, TEST_DOMAIN, "strict-origin");
+
+  // no-referrer pref when strict-origin is forced
+  await SpecialPowers.pushPrefEnv({"set":
+    [[pn("network.http.referer.defaultPolicy.trackers", private), 0]]});
+  await testOnWindow(private, TEST_DOMAIN, "strict-origin");
+}
+
+async function testOnOriginWhenCrossOrigin(private) {
+  // origin-when-cross-origin pref when origin-when-cross-origin is forced
+  await SpecialPowers.pushPrefEnv({"set":
+    [[pn("network.http.referer.defaultPolicy.trackers", private), 3]]});
+  await testOnWindow(private, TEST_DOMAIN, "origin-when-cross-origin");
+
+  // strict-origin-when-cross-origin pref when origin-when-cross-origin is forced
+  await SpecialPowers.pushPrefEnv({"set":
+    [[pn("network.http.referer.defaultPolicy.trackers", private), 2]]});
+  await testOnWindow(private, TEST_DOMAIN, "origin-when-cross-origin");
+
+  // same-origin pref when origin-when-cross-origin is forced
+  await SpecialPowers.pushPrefEnv({"set":
+    [[pn("network.http.referer.defaultPolicy.trackers", private), 1]]});
+  await testOnWindow(private, TEST_DOMAIN, "origin-when-cross-origin");
+
+  // no-referrer pref when origin-when-cross-origin is forced
+  await SpecialPowers.pushPrefEnv({"set":
+    [[pn("network.http.referer.defaultPolicy.trackers", private), 0]]});
+  await testOnWindow(private, TEST_DOMAIN, "origin-when-cross-origin");
+}
+
+async function testOnStrictOriginWhenCrossOrigin(private) {
+  // origin-when-cross-origin pref when strict-origin-when-cross-origin is forced
+  await SpecialPowers.pushPrefEnv({"set":
+    [[pn("network.http.referer.defaultPolicy.trackers", private), 3]]});
+  await testOnWindow(private, TEST_DOMAIN, "strict-origin-when-cross-origin");
+
+  // strict-origin-when-cross-origin pref when strict-origin-when-cross-origin is forced
+  await SpecialPowers.pushPrefEnv({"set":
+    [[pn("network.http.referer.defaultPolicy.trackers", private), 2]]});
+  await testOnWindow(private, TEST_DOMAIN, "strict-origin-when-cross-origin");
+
+  // same-origin pref when strict-origin-when-cross-origin is forced
+  await SpecialPowers.pushPrefEnv({"set":
+    [[pn("network.http.referer.defaultPolicy.trackers", private), 1]]});
+  await testOnWindow(private, TEST_DOMAIN, "strict-origin-when-cross-origin");
+
+  // no-referrer pref when strict-origin-when-cross-origin is forced
+  await SpecialPowers.pushPrefEnv({"set":
+    [[pn("network.http.referer.defaultPolicy.trackers", private), 0]]});
+  await testOnWindow(private, TEST_DOMAIN, "strict-origin-when-cross-origin");
+}
+
+async function testOnUnsafeUrl(private) {
+  // no-referrer-when-downgrade pref when unsafe-url is forced
+  await SpecialPowers.pushPrefEnv({"set":
+    [[pn("network.http.referer.defaultPolicy.trackers", private), 3]]});
+  await testOnWindow(private, TEST_TOP_PAGE, "unsafe-url");
+
+  // strict-origin-when-cross-origin pref when unsafe-url is forced
+  await SpecialPowers.pushPrefEnv({"set":
+    [[pn("network.http.referer.defaultPolicy.trackers", private), 2]]});
+  await testOnWindow(private, TEST_TOP_PAGE, "unsafe-url");
+
+  // same-origin pref when unsafe-url is forced
+  await SpecialPowers.pushPrefEnv({"set":
+    [[pn("network.http.referer.defaultPolicy.trackers", private), 1]]});
+  await testOnWindow(private, TEST_TOP_PAGE, "unsafe-url");
+
+  // no-referrer pref when unsafe-url is forced
+  await SpecialPowers.pushPrefEnv({"set":
+    [[pn("network.http.referer.defaultPolicy.trackers", private), 0]]});
+  await testOnWindow(private, TEST_TOP_PAGE, "unsafe-url");
 }
 
 add_task(async function() {
   info("Starting referrer default policy test");
 
   await SpecialPowers.flushPrefEnv();
   await SpecialPowers.pushPrefEnv({"set": [
     ["browser.contentblocking.allowlist.annotations.enabled", true],
@@ -57,59 +228,107 @@ add_task(async function() {
     ["privacy.trackingprotection.annotate_channels", true],
   ]});
 
   await UrlClassifierTestUtils.addTestTrackers();
 
   // no-referrer-when-downgrade
   await SpecialPowers.pushPrefEnv({"set":
     [["network.http.referer.defaultPolicy.trackers", 3]]});
-  await testOnWindow(false, TEST_TOP_PAGE);
+  await testOnWindow(false, TEST_TOP_PAGE, null);
 
   // strict-origin-when-cross-origin
   await SpecialPowers.pushPrefEnv({"set":
     [["network.http.referer.defaultPolicy.trackers", 2]]});
-  await testOnWindow(false, TEST_DOMAIN);
+  await testOnWindow(false, TEST_DOMAIN, null);
 
   // same-origin
   await SpecialPowers.pushPrefEnv({"set":
     [["network.http.referer.defaultPolicy.trackers", 1]]});
-  await testOnWindow(false, "");
+  await testOnWindow(false, "", null);
 
   // no-referrer
   await SpecialPowers.pushPrefEnv({"set":
     [["network.http.referer.defaultPolicy.trackers", 0]]});
-  await testOnWindow(false, "");
+  await testOnWindow(false, "", null);
+
+  // override with no-referrer
+  await testOnNoReferrer(false);
+
+  // override with same-origin
+  await testOnSameOrigin(false);
+
+  // override with no-referrer-when-downgrade
+  await testOnNoReferrerWhenDowngrade(false);
+
+  // override with origin
+  await testOnOrigin(false);
+
+  // override with strict-origin
+  await testOnStrictOrigin(false);
+
+  // override with origin-when-cross-origin
+  await testOnOriginWhenCrossOrigin(false);
+
+  // override with strict-origin-when-cross-origin
+  await testOnStrictOriginWhenCrossOrigin(false);
+
+  // override with unsafe-url
+  await testOnUnsafeUrl(false);
 
   // Reset the pref.
   Services.prefs.clearUserPref("network.http.referer.defaultPolicy.trackers");
 
   // no-referrer-when-downgrade
   await SpecialPowers.pushPrefEnv({"set": [
     // Set both prefs, because if we only set the trackers pref, then the PB
     // mode default policy pref (2) would apply!
     ["network.http.referer.defaultPolicy.pbmode", 3],
     ["network.http.referer.defaultPolicy.trackers.pbmode", 3],
   ]});
-  await testOnWindow(true, TEST_TOP_PAGE);
+  await testOnWindow(true, TEST_TOP_PAGE, null);
 
   // strict-origin-when-cross-origin
   await SpecialPowers.pushPrefEnv({"set":
     [["network.http.referer.defaultPolicy.trackers.pbmode", 2]]});
-  await testOnWindow(true, TEST_DOMAIN);
+  await testOnWindow(true, TEST_DOMAIN, null);
 
   // same-origin
   await SpecialPowers.pushPrefEnv({"set":
     [["network.http.referer.defaultPolicy.trackers.pbmode", 1]]});
-  await testOnWindow(true, "");
+  await testOnWindow(true, "", null);
 
   // no-referrer
   await SpecialPowers.pushPrefEnv({"set":
     [["network.http.referer.defaultPolicy.trackers.pbmode", 0]]});
-  await testOnWindow(true, "");
+  await testOnWindow(true, "", null);
+
+  // override with no-referrer
+  await testOnNoReferrer(true);
+
+  // override with same-origin
+  await testOnSameOrigin(true);
+
+  // override with no-referrer-when-downgrade
+  await testOnNoReferrerWhenDowngrade(true);
+
+  // override with origin
+  await testOnOrigin(true);
+
+  // override with strict-origin
+  await testOnStrictOrigin(true);
+
+  // override with origin-when-cross-origin
+  await testOnOriginWhenCrossOrigin(true);
+
+  // override with strict-origin-when-cross-origin
+  await testOnStrictOriginWhenCrossOrigin(true);
+
+  // override with unsafe-url
+  await testOnUnsafeUrl(true);
 
   // Reset the pref.
   Services.prefs.clearUserPref("network.http.referer.defaultPolicy.trackers.pbmode");
 });
 
 add_task(async function() {
   info("Cleaning up.");
   await new Promise(resolve => {
--- a/toolkit/components/extensions/test/xpcshell/test_ext_incognito.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_incognito.js
@@ -7,17 +7,17 @@ const {AddonManager} = ChromeUtils.impor
 AddonTestUtils.init(this);
 AddonTestUtils.overrideCertDB();
 AddonTestUtils.createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "42");
 AddonTestUtils.usePrivilegedSignatures = false;
 
 // Assert on the expected "addonsManager.action" telemetry events (and optional filter events to verify
 // by using a given actionType).
 function assertActionAMTelemetryEvent(expectedActionEvents, assertMessage, {actionType} = {}) {
-  const snapshot = Services.telemetry.snapshotEvents(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, true);
+  const snapshot = Services.telemetry.snapshotEvents(Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS, true);
 
   ok(snapshot.parent && snapshot.parent.length > 0, "Got parent telemetry events in the snapshot");
 
   const events = snapshot.parent.filter(([timestamp, category, method, object, value, extra]) => {
     return category === "addonsManager" && method === "action" && (
       !actionType ? true : extra && extra.action === actionType
     );
   }).map(([timestamp, category, method, object, value, extra]) => {
--- a/toolkit/components/telemetry/Events.yaml
+++ b/toolkit/components/telemetry/Events.yaml
@@ -752,16 +752,26 @@ devtools.main:
     record_in_processes: ["main"]
     description: Debugger has paused in a script due to a breakpoint or exception.
     release_channel_collection: opt-out
     expiry_version: never
     extra_keys:
       reason: caught-exception, uncaught-exception, pausing, debugger-statement or breakpoint.
       lib_stacks: Number of collapsed callstacks in the call tree. These are call stacks that are part of external libraries e.g. react, which are collapsed  by default.
       session_id: The start time of the session in milliseconds since epoch (Unix Timestamp) e.g. 1396381378123.
+  persist_changed:
+    objects: ["netmonitor", "webconsole"]
+    bug_numbers: [1531395]
+    notification_emails: ["dev-developer-tools@lists.mozilla.org", "hkirschner@mozilla.com"]
+    record_in_processes: ["main"]
+    description: User has changed log persist status.
+    release_channel_collection: opt-out
+    expiry_version: never
+    extra_keys:
+      session_id: The start time of the session in milliseconds since epoch (Unix Timestamp) e.g. 1396381378123.   
   pretty_print:
     objects: ["debugger"]
     bug_numbers: [1463125]
     notification_emails: ["dev-developer-tools@lists.mozilla.org", "hkirschner@mozilla.com"]
     record_in_processes: ["main"]
     description: User clicked the pretty print button to pretty print a script.
     release_channel_collection: opt-out
     expiry_version: never
--- a/toolkit/components/telemetry/docs/data/environment.rst
+++ b/toolkit/components/telemetry/docs/data/environment.rst
@@ -42,16 +42,17 @@ Structure:
           name: <string>, // engine name, e.g. "Yahoo"; or "NONE" if no default
           loadPath: <string>, // where the engine line is located; missing if no default
           origin: <string>, // 'default', 'verified', 'unverified', or 'invalid'; based on the presence and validity of the engine's loadPath verification hash.
           submissionURL: <string> // set for default engines or well known search domains
         },
         searchCohort: <string>, // optional, contains an identifier for any active search A/B experiments
         launcherProcessState: <integer>, // optional, values correspond to values of mozilla::LauncherRegistryInfo::EnabledState enum
         e10sEnabled: <bool>, // whether e10s is on, i.e. browser tabs open by default in a different process
+        e10sMultiProcesses: <integer>, // Maximum number of processes that will be launched for regular web content
         telemetryEnabled: <bool>, // false on failure
         locale: <string>, // e.g. "it", null on failure
         intl: {
           requestedLocales: [ <string>, ... ], // The locales that are being requested.
           availableLocales: [ <string>, ... ], // The locales that are available for use.
           appLocales: [ <string>, ... ], // The negotiated locales that are being used.
           systemLocales: [ <string>, ... ], // The locales for the OS.
           regionalPrefsLocales: [ <string>, ... ], // The regional preferences for the OS.
--- a/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js
@@ -382,16 +382,17 @@ function checkBuildSection(data) {
   Assert.equal(data.build.updaterAvailable, AppConstants.MOZ_UPDATER,
                "build.updaterAvailable must equal AppConstants.MOZ_UPDATER");
 }
 
 function checkSettingsSection(data) {
   const EXPECTED_FIELDS_TYPES = {
     blocklistEnabled: "boolean",
     e10sEnabled: "boolean",
+    e10sMultiProcesses: "number",
     intl: "object",
     locale: "string",
     telemetryEnabled: "boolean",
     update: "object",
     userPrefs: "object",
   };
 
   Assert.ok("settings" in data, "There must be a settings section in Environment.");
--- a/toolkit/library/rust/shared/Cargo.toml
+++ b/toolkit/library/rust/shared/Cargo.toml
@@ -3,16 +3,17 @@ name = "gkrust-shared"
 version = "0.1.0"
 authors = ["nobody@mozilla.org"]
 license = "MPL-2.0"
 description = "Shared Rust code for libxul"
 
 [dependencies]
 geckoservo = { path = "../../../../servo/ports/geckolib", optional = true }
 kvstore = { path = "../../../components/kvstore" }
+lmdb-rkv-sys = { version = "0.8.3", features = ["mdb_idl_logn_9"] }
 mp4parse_capi = { path = "../../../../media/mp4parse-rust/mp4parse_capi" }
 nserror = { path = "../../../../xpcom/rust/nserror" }
 nsstring = { path = "../../../../xpcom/rust/nsstring" }
 netwerk_helper = { path = "../../../../netwerk/base/rust-helper" }
 xpcom = { path = "../../../../xpcom/rust/xpcom" }
 prefs_parser = { path = "../../../../modules/libpref/parser" }
 profiler_helper = { path = "../../../../tools/profiler/rust-helper", optional = true }
 mozurl = { path = "../../../../netwerk/base/mozurl" }
--- a/toolkit/mozapps/update/content/updates.js
+++ b/toolkit/mozapps/update/content/updates.js
@@ -211,59 +211,38 @@ var gUpdates = {
     // which will clear app.update.elevate.never preference.
     if (gAUS.elevationRequired) {
       Services.prefs.setCharPref(PREF_APP_UPDATE_ELEVATE_NEVER,
                                  this.update.appVersion);
     }
   },
 
   /**
-   * A hash of |pageid| attribute to page object. Can be used to dispatch
-   * function calls to the appropriate page.
-   */
-  _pages: { },
-
-  /**
    * Called when the user presses the "Finish" button on the wizard, dispatches
    * the function call to the selected page.
    */
   onWizardFinish() {
     this._runUnload = false;
     var pageid = document.documentElement.currentPage.pageid;
-    if ("onWizardFinish" in this._pages[pageid])
-      this._pages[pageid].onWizardFinish();
     this._submitTelemetry(pageid);
   },
 
   /**
    * Called when the user presses the "Cancel" button on the wizard, dispatches
    * the function call to the selected page.
    */
   onWizardCancel() {
     this._runUnload = false;
     var pageid = document.documentElement.currentPage.pageid;
-    if ("onWizardCancel" in this._pages[pageid])
-      this._pages[pageid].onWizardCancel();
+    let cancelEvent = new CustomEvent("wizardpagecancel", { });
+    document.documentElement.currentPage.dispatchEvent(cancelEvent);
     this._submitTelemetry(pageid);
   },
 
   /**
-   * Called when the user presses the "Next" button on the wizard, dispatches
-   * the function call to the selected page.
-   */
-  onWizardNext() {
-    var cp = document.documentElement.currentPage;
-    if (!cp)
-      return;
-    var pageid = cp.pageid;
-    if ("onWizardNext" in this._pages[pageid])
-      this._pages[pageid].onWizardNext();
-  },
-
-  /**
    * The checking process that spawned this update UI. There are two types:
    * SRCEVT_FOREGROUND:
    *   Some user-generated event caused this UI to appear, e.g. the Help
    *   menu item or the button in preferences. When in this mode, the UI
    *   should remain active for the duration of the download.
    * SRCEVT_BACKGROUND:
    *   A background update check caused this UI to appear, probably because
    *   UpdateUtils.getAppUpdateAutoEnabled returned false, indicating that the
@@ -289,51 +268,61 @@ var gUpdates = {
     this.wiz = document.documentElement;
 
     gLogEnabled = Services.prefs.getBoolPref(PREF_APP_UPDATE_LOG, false);
 
     this.strings = document.getElementById("updateStrings");
     var brandStrings = document.getElementById("brandStrings");
     this.brandName = brandStrings.getString("brandShortName");
 
-    var pages = this.wiz.childNodes;
-    for (var i = 0; i < pages.length; ++i) {
-      var page = pages[i];
-      if (page.localName == "wizardpage")
-        // eslint-disable-next-line no-eval
-        this._pages[page.pageid] = eval(page.getAttribute("object"));
-    }
-
     // Cache the standard button labels in case we need to restore them
     this._cacheButtonStrings("next");
     this._cacheButtonStrings("finish");
     this._cacheButtonStrings("extra1");
     this._cacheButtonStrings("extra2");
 
+    // Add wizardFinish eventListeners globally and to the pages that need one.
     document.addEventListener("wizardfinish", function() { gUpdates.onWizardFinish(); });
+    document.getElementById("finished").addEventListener("wizardfinish", function() { gFinishedPage.onWizardFinish(); });
+    document.getElementById("finishedBackground").addEventListener("wizardfinish", function() { gFinishedPage.onWizardFinish(); });
+
+    // Add wizardcancel eventListener globally.
     document.addEventListener("wizardcancel", function() { gUpdates.onWizardCancel(); });
-    document.addEventListener("wizardnext", function() { gUpdates.onWizardNext(); });
+
+    // Add wizardpagecancel eventListener to the pages that need one.
+    document.getElementById("checking").addEventListener("wizardpagecancel", function() { gCheckingPage.onWizardCancel(); });
+    document.getElementById("downloading").addEventListener("wizardpagecancel", function() { gDownloadingPage.onWizardCancel(); });
 
+    // Add special wizardNext eventListener for the errorpatching-page.
+    document.addEventListener("wizardnext", function() {
+        if (document.documentElement.currentPage.pageid == "errorpatching") {
+          gErrorPatchingPage.onWizardNext();
+        }
+    });
+
+    // Add pageShow eventListeners to all pages.
     document.getElementById("checking").addEventListener("pageshow", function() { gCheckingPage.onPageShow(); });
     document.getElementById("noupdatesfound").addEventListener("pageshow", function() { gNoUpdatesPage.onPageShow(); });
     document.getElementById("manualUpdate").addEventListener("pageshow", function() { gManualUpdatePage.onPageShow(); });
     document.getElementById("unsupported").addEventListener("pageshow", function() { gUnsupportedPage.onPageShow(); });
     document.getElementById("updatesfoundbasic").addEventListener("pageshow", function() { gUpdatesFoundBasicPage.onPageShow(); });
     document.getElementById("downloading").addEventListener("pageshow", function() { gDownloadingPage.onPageShow(); });
     document.getElementById("errors").addEventListener("pageshow", function() { gErrorsPage.onPageShow(); });
     document.getElementById("errorextra").addEventListener("pageshow", function() { gErrorExtraPage.onPageShow(); });
     document.getElementById("errorpatching").addEventListener("pageshow", function() { gErrorPatchingPage.onPageShow(); });
     document.getElementById("finished").addEventListener("pageshow", function() { gFinishedPage.onPageShow(); });
     document.getElementById("finishedBackground").addEventListener("pageshow", function() { gFinishedPage.onPageShowBackground(); });
 
+    // Add extra1 eventListeners to the pages that need one.
     document.getElementById("updatesfoundbasic").addEventListener("extra1", function() { gUpdatesFoundBasicPage.onExtra1(); });
     document.getElementById("downloading").addEventListener("extra1", function() { gDownloadingPage.onHide(); });
     document.getElementById("finished").addEventListener("extra1", function() { gFinishedPage.onExtra1(); });
     document.getElementById("finishedBackground").addEventListener("extra1", function() { gFinishedPage.onExtra1(); });
 
+    // Add extra2 eventListeners to the pages that need one.
     document.getElementById("updatesfoundbasic").addEventListener("extra2", function() { gUpdatesFoundBasicPage.onExtra2(); });
     document.getElementById("finishedBackground").addEventListener("extra2", function() { gFinishedPage.onExtra2(); });
 
     // Advance to the Start page.
     this.getStartPageID(function(startPageID) {
       LOG("gUpdates", "onLoad - setting current page to startpage " + startPageID);
       gUpdates.wiz.currentPage = document.getElementById(startPageID);
     });
--- a/toolkit/mozapps/update/content/updates.xul
+++ b/toolkit/mozapps/update/content/updates.xul
@@ -38,64 +38,60 @@
 
   <stringbundleset id="updateSet">
     <stringbundle id="brandStrings" src="chrome://branding/locale/brand.properties"/>
     <stringbundle id="updateStrings" src="chrome://mozapps/locale/update/updates.properties"/>
   </stringbundleset>
 
   <wizardpage id="dummy" pageid="dummy" firstpage="true"/>
 
-  <wizardpage id="checking" pageid="checking" next="noupdatesfound"
-              object="gCheckingPage">
+  <wizardpage id="checking" pageid="checking" next="noupdatesfound">
     <updateheader label="&checking.title;"/>
     <vbox class="update-content" flex="1">
       <label>&updateCheck.label;</label>
       <separator class="thin"/>
       <html:progress id="checkingProgress"/>
     </vbox>
   </wizardpage>
 
-  <wizardpage id="noupdatesfound" pageid="noupdatesfound"
-              object="gNoUpdatesPage">
+  <wizardpage id="noupdatesfound" pageid="noupdatesfound">
     <updateheader label="&noupdatesfound.title;"/>
     <vbox class="update-content" flex="1">
       <label id="noUpdatesAutoEnabled" hidden="true">&noupdatesautoenabled.intro;</label>
       <label id="noUpdatesAutoDisabled" hidden="true">&noupdatesautodisabled.intro;</label>
     </vbox>
   </wizardpage>
 
-  <wizardpage id="manualUpdate" pageid="manualUpdate" object="gManualUpdatePage">
+  <wizardpage id="manualUpdate" pageid="manualUpdate">
     <updateheader label="&manualUpdate.title;"/>
     <vbox class="update-content" flex="1">
       <label id="manualUpdateDesc">&manualUpdate.desc;</label>
       <label id="manualUpdateSpaceDesc"
              hidden="true">&manualUpdate.space.desc;</label>
       <separator class="thin"/>
       <label>&manualUpdateGetMsg.label;</label>
       <hbox>
         <label id="manualUpdateLinkLabel" value="" is="text-link"
                onclick="openUpdateURL(event);"/>
       </hbox>
     </vbox>
   </wizardpage>
 
-  <wizardpage id="unsupported" pageid="unsupported"
-              object="gUnsupportedPage">
+  <wizardpage id="unsupported" pageid="unsupported">
     <updateheader label="&unsupported.title;"/>
     <vbox class="update-content" flex="1">
       <description flex="1">&unsupported.label;
         <label id="unsupportedLinkLabel" class="inline-link" onclick="openUpdateURL(event);" is="text-link">
           &unsupportedLink.label;
         </label>
       </description>
     </vbox>
   </wizardpage>
 
-  <wizardpage id="updatesfoundbasic" pageid="updatesfoundbasic"
-              object="gUpdatesFoundBasicPage" next="downloading">
+  <wizardpage id="updatesfoundbasic" pageid="updatesfoundbasic" next="downloading">
     <updateheader id="updatesFoundBasicHeader" label=""/>
     <vbox class="update-content" flex="1">
       <label id="updatesFoundInto"/>
       <separator class="thin"/>
       <label id="updateName" crop="right" value=""/>
       <separator id="updateNameSep" class="thin"/>
       <label id="upgradeEvangelism">&evangelism.desc;</label>
       <separator id="upgradeEvangelismSep" flex="1"/>
@@ -103,18 +99,17 @@
         <hbox id="moreInfoURL">
           <label id="updateMoreInfoURL" is="text-link"
                  value="&clickHere.label;" onclick="openUpdateURL(event);"/>
         </hbox>
       </vbox>
     </vbox>
   </wizardpage>
 
-  <wizardpage id="downloading" pageid="downloading"
-              object="gDownloadingPage">
+  <wizardpage id="downloading" pageid="downloading">
     <updateheader label="&downloadPage.title;"/>
     <vbox class="update-content" flex="1">
       <hbox id="downloadStatusProgress">
         <html:progress id="downloadProgress" max="100"/>
         <button id="pauseButton" oncommand="gDownloadingPage.onPause();"
                 paused="false"/>
       </hbox>
       <separator class="thin"/>
@@ -124,60 +119,57 @@
       <separator/>
       <hbox id="verificationFailed" align="start" hidden="true">
         <image id="verificationFailedIcon"/>
         <label flex="1">&verificationFailedText.label;</label>
       </hbox>
     </vbox>
   </wizardpage>
 
-  <wizardpage id="errors" pageid="errors" object="gErrorsPage">
+  <wizardpage id="errors" pageid="errors">
     <updateheader label="&error.title;"/>
     <vbox class="update-content" flex="1">
       <label id="errorIntro">&error.label;</label>
       <separator/>
       <html:textarea class="plain" readonly="readonly" id="errorReason" rows="3"/>
       <separator/>
       <label id="errorManual">&errorManual.label;</label>
       <hbox>
         <label id="errorLinkLabel" value="" is="text-link"
                onclick="openUpdateURL(event);"/>
       </hbox>
     </vbox>
   </wizardpage>
 
-  <wizardpage id="errorextra" pageid="errorextra"
-              object="gErrorExtraPage">
+  <wizardpage id="errorextra" pageid="errorextra">
     <updateheader label="&error.title;"/>
     <vbox class="update-content" flex="1">
       <label id="bgErrorLabel">&genericBackgroundError.label;</label>
       <hbox>
         <label id="errorExtraLinkLabel" is="text-link"
                value="" onclick="openUpdateURL(event);"/>
       </hbox>
     </vbox>
   </wizardpage>
 
-  <wizardpage id="errorpatching" pageid="errorpatching" next="downloading"
-              object="gErrorPatchingPage">
+  <wizardpage id="errorpatching" pageid="errorpatching" next="downloading">
     <updateheader label="&error.title;"/>
     <vbox class="update-content" flex="1">
       <label>&errorpatching.intro;</label>
     </vbox>
   </wizardpage>
 
-  <wizardpage id="finished" pageid="finished" object="gFinishedPage">
+  <wizardpage id="finished" pageid="finished">
     <updateheader label="&finishedPage.title;"/>
     <vbox class="update-content" flex="1">
       <label>&finishedPage.text;</label>
     </vbox>
   </wizardpage>
 
-  <wizardpage id="finishedBackground" pageid="finishedBackground"
-              object="gFinishedPage">
+  <wizardpage id="finishedBackground" pageid="finishedBackground">
     <updateheader label="&finishedPage.title;"/>
     <vbox class="update-content" flex="1">
       <label>&finishedBackgroundPage.text;</label>
       <separator/>
       <hbox align="center">
         <label>&finishedBackground.name;</label>
         <label id="updateFinishedName" flex="1" crop="right" value=""/>
         <label id="finishedBackgroundLink" disabled="true" is="text-link"