Merge mozilla-central to mozilla-inbound. a=merge on a CLOSED TREE
authorRazvan Maries <rmaries@mozilla.com>
Tue, 20 Nov 2018 07:14:39 +0200
changeset 503645 a9202e921ae80c9614dbe4dcce51bd089e3caf47
parent 503644 fa29f8f43d2dde8d9cfaf6044053ad72fa7bbc01 (current diff)
parent 503598 74c27e915067b5afc43f4f64fd8fa2b0dc3bc81a (diff)
child 503646 0cb993c91806dfa218746ea7bb2aa300b7af58e8
push id10290
push userffxbld-merge
push dateMon, 03 Dec 2018 16:23:23 +0000
treeherdermozilla-beta@700bed2445e6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone65.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to mozilla-inbound. a=merge on a CLOSED TREE
testing/web-platform/meta/streams/readable-streams/brand-checks.dedicatedworker.html.ini
testing/web-platform/meta/streams/readable-streams/brand-checks.html.ini
testing/web-platform/meta/streams/readable-streams/brand-checks.serviceworker.https.html.ini
testing/web-platform/meta/streams/readable-streams/brand-checks.sharedworker.html.ini
testing/web-platform/meta/streams/readable-streams/tee.dedicatedworker.html.ini
testing/web-platform/meta/streams/readable-streams/tee.html.ini
testing/web-platform/meta/streams/readable-streams/tee.serviceworker.https.html.ini
testing/web-platform/meta/streams/readable-streams/tee.sharedworker.html.ini
--- a/browser/base/content/aboutDialog.js
+++ b/browser/base/content/aboutDialog.js
@@ -32,16 +32,17 @@ async function init(aEvent) {
       var distroField = document.getElementById("distribution");
       distroField.value = distroAbout;
       distroField.style.display = "block";
     }
   }
 
   // Include the build ID and display warning if this is an "a#" (nightly or aurora) build
   let versionField = document.getElementById("version");
+  versionField.textContent = AppConstants.MOZ_APP_VERSION_DISPLAY;
   let version = Services.appinfo.version;
   if (/a\d+$/.test(version)) {
     let buildID = Services.appinfo.appBuildID;
     let year = buildID.slice(0, 4);
     let month = buildID.slice(4, 6);
     let day = buildID.slice(6, 8);
     versionField.textContent += ` (${year}-${month}-${day})`;
 
@@ -72,17 +73,17 @@ async function init(aEvent) {
 
     let channelLabel = document.getElementById("currentChannel");
     let currentChannelText = document.getElementById("currentChannelText");
     channelLabel.value = UpdateUtils.UpdateChannel;
     if (/^release($|\-)/.test(channelLabel.value))
         currentChannelText.hidden = true;
   }
 
-  if (AppConstants.MOZ_UPDATE_CHANNEL == "esr") {
+  if (AppConstants.MOZ_APP_VERSION_DISPLAY.endsWith("esr")) {
     document.getElementById("release").hidden = false;
   }
   if (AppConstants.platform == "macosx") {
     // it may not be sized at this point, and we need its width to calculate its position
     window.sizeToContent();
     window.moveTo((screen.availWidth / 2) - (window.outerWidth / 2), screen.availHeight / 5);
   }
 }
--- a/browser/base/content/aboutDialog.xul
+++ b/browser/base/content/aboutDialog.xul
@@ -48,17 +48,17 @@
     <hbox id="clientBox">
       <vbox id="leftBox" flex="1"/>
       <vbox id="rightBox" flex="1">
         <label id="release" hidden="true">
         <!-- This string is explicitly not translated -->
           Extended Support Release
         </label>
         <hbox align="baseline">
-#expand   <label id="version">__MOZ_APP_VERSION_DISPLAY__</label>
+          <label id="version"/>
           <label id="releasenotes" class="text-link" hidden="true" data-l10n-id="releaseNotes-link"></label>
         </hbox>
 
         <label id="distribution" class="text-blurb"/>
         <label id="distributionId" class="text-blurb"/>
 
         <vbox id="detailsBox">
           <vbox id="updateBox">
--- a/browser/base/content/browser-contentblocking.js
+++ b/browser/base/content/browser-contentblocking.js
@@ -222,20 +222,38 @@ var ThirdPartyCookies = {
 
 var ContentBlocking = {
   // If the user ignores the doorhanger, we stop showing it after some time.
   MAX_INTROS: 20,
   PREF_ANIMATIONS_ENABLED: "toolkit.cosmeticAnimations.enabled",
   PREF_REPORT_BREAKAGE_ENABLED: "browser.contentblocking.reportBreakage.enabled",
   PREF_REPORT_BREAKAGE_URL: "browser.contentblocking.reportBreakage.url",
   PREF_INTRO_COUNT_CB: "browser.contentblocking.introCount",
+  PREF_CB_CATEGORY: "browser.contentblocking.category",
+  // The prefs inside CATEGORY_PREFS set expected behavior for each CB category.
+  // A null value means that pref is default.
+  CATEGORY_PREFS: {
+    strict: [
+      [TrackingProtection.PREF_TRACKING_TABLE, null],
+      [ThirdPartyCookies.PREF_ENABLED, Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN],
+      [TrackingProtection.PREF_ENABLED_IN_PRIVATE_WINDOWS, true],
+      [TrackingProtection.PREF_ENABLED_GLOBALLY, true],
+    ],
+    standard: [
+      [TrackingProtection.PREF_TRACKING_TABLE, null],
+      [ThirdPartyCookies.PREF_ENABLED, null],
+      [TrackingProtection.PREF_ENABLED_IN_PRIVATE_WINDOWS, null],
+      [TrackingProtection.PREF_ENABLED_GLOBALLY, null],
+    ],
+  },
   content: null,
   icon: null,
   activeTooltipText: null,
   disabledTooltipText: null,
+  switchingCategory: false,
 
   get prefIntroCount() {
     return this.PREF_INTRO_COUNT_CB;
   },
 
   get appMenuLabel() {
     delete this.appMenuLabel;
     return this.appMenuLabel = document.getElementById("appMenu-tp-label");
@@ -319,26 +337,38 @@ var ContentBlocking = {
 
     this.appMenuLabel.setAttribute("label", this.strings.appMenuTitle);
     this.appMenuLabel.setAttribute("tooltiptext", this.strings.appMenuTooltip);
 
     this.activeTooltipText =
       gNavigatorBundle.getString("trackingProtection.icon.activeTooltip");
     this.disabledTooltipText =
       gNavigatorBundle.getString("trackingProtection.icon.disabledTooltip");
+
+    this.matchCBCategory = this.matchCBCategory.bind(this);
+    this.updateCBCategory = this.updateCBCategory.bind(this);
+    this.matchCBCategory();
+    Services.prefs.addObserver(TrackingProtection.PREF_ENABLED_GLOBALLY, this.matchCBCategory);
+    Services.prefs.addObserver(TrackingProtection.PREF_ENABLED_IN_PRIVATE_WINDOWS, this.matchCBCategory);
+    Services.prefs.addObserver(ThirdPartyCookies.PREF_ENABLED, this.matchCBCategory);
+    Services.prefs.addObserver(this.PREF_CB_CATEGORY, this.updateCBCategory);
   },
 
   uninit() {
     for (let blocker of this.blockers) {
       if (blocker.uninit) {
         blocker.uninit();
       }
     }
 
     Services.prefs.removeObserver(this.PREF_ANIMATIONS_ENABLED, this.updateAnimationsEnabled);
+    Services.prefs.removeObserver(TrackingProtection.PREF_ENABLED_GLOBALLY, this.matchCBCategory);
+    Services.prefs.removeObserver(TrackingProtection.PREF_ENABLED_IN_PRIVATE_WINDOWS, this.matchCBCategory);
+    Services.prefs.removeObserver(ThirdPartyCookies.PREF_ENABLED, this.matchCBCategory);
+    Services.prefs.removeObserver(this.PREF_CB_CATEGORY, this.updateCBCategory);
   },
 
   hideIdentityPopupAndReload() {
     this.identityPopup.hidePopup();
     BrowserReload();
   },
 
   openPreferences(origin) {
@@ -605,9 +635,94 @@ var ContentBlocking = {
       },
     ];
 
     let panelTarget = await UITour.getTarget(window, "trackingProtection");
     UITour.initForBrowser(gBrowser.selectedBrowser, window);
     UITour.showInfo(window, panelTarget, introTitle, introDescription, undefined, buttons,
                     { closeButtonCallback: () => this.dontShowIntroPanelAgain() });
   },
+
+  /**
+   * Checks if CB prefs match perfectly with one of our pre-defined categories.
+   */
+  prefsMatch(category) {
+    // The category pref must be either unset, or match.
+    if (Services.prefs.prefHasUserValue(this.PREF_CB_CATEGORY) &&
+        Services.prefs.getStringPref(this.PREF_CB_CATEGORY) != category) {
+      return false;
+    }
+    for (let [pref, value] of this.CATEGORY_PREFS[category]) {
+      if (!value) {
+        if (Services.prefs.prefHasUserValue(pref)) {
+          return false;
+        }
+      } else {
+        let prefType = Services.prefs.getPrefType(pref);
+        if ((prefType == Services.prefs.PREF_BOOL && Services.prefs.getBoolPref(pref) != value) ||
+            (prefType == Services.prefs.PREF_INT && Services.prefs.getIntPref(pref) != value) ||
+            (prefType == Services.prefs.PREF_STRING && Services.prefs.getStringPref(pref) != value)) {
+          return false;
+        }
+      }
+    }
+    return true;
+  },
+
+  async matchCBCategory() {
+    if (this.switchingCategory) {
+      return;
+    }
+    // If PREF_CB_CATEGORY is not set match users to a Content Blocking category. Check if prefs fit
+    // perfectly into strict or standard, otherwise match with custom. If PREF_CB_CATEGORY has previously been set,
+    // a change of one of these prefs necessarily puts us in "custom".
+    if (this.prefsMatch("standard")) {
+      Services.prefs.setStringPref(this.PREF_CB_CATEGORY, "standard");
+    } else if (this.prefsMatch("strict")) {
+      Services.prefs.setStringPref(this.PREF_CB_CATEGORY, "strict");
+    } else {
+      Services.prefs.setStringPref(this.PREF_CB_CATEGORY, "custom");
+    }
+  },
+
+  updateCBCategory() {
+    if (this.switchingCategory) {
+      return;
+    }
+    // Turn on switchingCategory flag, to ensure that when the individual prefs that change as a result
+    // of the category change do not trigger yet another category change.
+    this.switchingCategory = true;
+    let value = Services.prefs.getStringPref(this.PREF_CB_CATEGORY);
+    this.setPrefsToCategory(value);
+    this.switchingCategory = false;
+  },
+
+  /**
+   * Sets all user-exposed content blocking preferences to values that match the selected category.
+   */
+  setPrefsToCategory(category) {
+    // Leave prefs as they were if we are switching to "custom" category.
+    if (category == "custom") {
+      return;
+    }
+
+    for (let [pref, value] of this.CATEGORY_PREFS[category]) {
+      // this.setPrefIfNotLocked(pref[0], pref[1]);
+      if (!Services.prefs.prefIsLocked(pref)) {
+        if (!value) {
+          Services.prefs.clearUserPref(pref);
+        } else {
+          switch (Services.prefs.getPrefType(pref)) {
+          case Services.prefs.PREF_BOOL:
+            Services.prefs.setBoolPref(pref, value);
+            break;
+          case Services.prefs.PREF_INT:
+            Services.prefs.setIntPref(pref, value);
+            break;
+          case Services.prefs.PREF_STRING:
+            Services.prefs.setStringPref(pref, value);
+            break;
+          }
+        }
+      }
+    }
+  },
 };
--- a/browser/components/payments/content/paymentDialogWrapper.js
+++ b/browser/components/payments/content/paymentDialogWrapper.js
@@ -12,21 +12,24 @@
 "use strict";
 
 const paymentSrv = Cc["@mozilla.org/dom/payments/payment-request-service;1"]
                      .getService(Ci.nsIPaymentRequestService);
 
 const paymentUISrv = Cc["@mozilla.org/dom/payments/payment-ui-service;1"]
                      .getService(Ci.nsIPaymentUIService);
 
+ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 ChromeUtils.defineModuleGetter(this, "BrowserWindowTracker",
                                "resource:///modules/BrowserWindowTracker.jsm");
+ChromeUtils.defineModuleGetter(this, "FormAutofillUtils",
+                               "resource://formautofill/FormAutofillUtils.jsm");
 ChromeUtils.defineModuleGetter(this, "OSKeyStore",
                                "resource://formautofill/OSKeyStore.jsm");
 ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
                                "resource://gre/modules/PrivateBrowsingUtils.jsm");
 
 XPCOMUtils.defineLazyGetter(this, "formAutofillStorage", () => {
   let storage;
   try {
@@ -36,16 +39,22 @@ XPCOMUtils.defineLazyGetter(this, "formA
   } catch (ex) {
     storage = null;
     Cu.reportError(ex);
   }
 
   return storage;
 });
 
+XPCOMUtils.defineLazyGetter(this, "reauthPasswordPromptMessage", () => {
+  const brandShortName = FormAutofillUtils.brandBundle.GetStringFromName("brandShortName");
+  return FormAutofillUtils.stringBundle.formatStringFromName(
+    `useCreditCardPasswordPrompt.${AppConstants.platform}`, [brandShortName], 1);
+});
+
 /**
  * Temporary/transient storage for address and credit card records
  *
  * Implements a subset of the FormAutofillStorage collection class interface, and delegates to
  * those classes for some utility methods
  */
 class TempCollection {
   constructor(type, data = {}) {
@@ -173,17 +182,18 @@ var paymentDialogWrapper = {
     let cardData = this.temporaryStore.creditCards.get(guid) ||
                    await formAutofillStorage.creditCards.get(guid);
     if (!cardData) {
       throw new Error(`Basic card not found in storage: ${guid}`);
     }
 
     let cardNumber;
     try {
-      cardNumber = await OSKeyStore.decrypt(cardData["cc-number-encrypted"], true);
+      cardNumber = await OSKeyStore.decrypt(
+        cardData["cc-number-encrypted"], reauthPasswordPromptMessage);
     } catch (ex) {
       if (ex.result != Cr.NS_ERROR_ABORT) {
         throw ex;
       }
       // User canceled master password entry
       return null;
     }
 
--- a/browser/components/preferences/in-content/privacy.js
+++ b/browser/components/preferences/in-content/privacy.js
@@ -15,22 +15,16 @@ ChromeUtils.defineModuleGetter(this, "Pl
   "resource://gre/modules/PluralForm.jsm");
 ChromeUtils.defineModuleGetter(this, "LoginHelper",
   "resource://gre/modules/LoginHelper.jsm");
 ChromeUtils.defineModuleGetter(this, "SiteDataManager",
   "resource:///modules/SiteDataManager.jsm");
 
 ChromeUtils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
 
-XPCOMUtils.defineLazyPreferenceGetter(this, "contentBlockingTrackingProtectionUiEnabled",
-                                      "browser.contentblocking.trackingprotection.ui.enabled");
-
-XPCOMUtils.defineLazyPreferenceGetter(this, "contentBlockingRejectTrackersUiEnabled",
-                                      "browser.contentblocking.rejecttrackers.ui.enabled");
-
 const PREF_UPLOAD_ENABLED = "datareporting.healthreport.uploadEnabled";
 
 const TRACKING_PROTECTION_KEY = "websites.trackingProtectionMode";
 const TRACKING_PROTECTION_PREFS = ["privacy.trackingprotection.enabled",
                                    "privacy.trackingprotection.pbmode.enabled"];
 
 const PREF_OPT_OUT_STUDIES_ENABLED = "app.shield.optoutstudies.enabled";
 const PREF_NORMANDY_ENABLED = "app.normandy.enabled";
@@ -47,21 +41,16 @@ XPCOMUtils.defineLazyGetter(this, "Alert
     return undefined;
   }
 });
 
 Preferences.addAll([
   // Tracking Protection
   { id: "privacy.trackingprotection.enabled", type: "bool" },
   { id: "privacy.trackingprotection.pbmode.enabled", type: "bool" },
-  // This isn't a configuration pref, rather it's for saving the previous state
-  // of the UI when we turn off the TP controls when the user checks off the
-  // All Detected Trackers under Content Blocking.  This pref isn't listed in
-  // all.js/firefox.js to make sure it doesn't appear in about:config by default.
-  { id: "browser.privacy.trackingprotection.menu", type: "string" },
 
   // Button prefs
   { id: "pref.privacy.disable_button.cookie_exceptions", type: "bool" },
   { id: "pref.privacy.disable_button.view_cookies", type: "bool" },
   { id: "pref.privacy.disable_button.change_blocklist", type: "bool" },
   { id: "pref.privacy.disable_button.tracking_protection_exceptions", type: "bool" },
 
   // Location Bar
@@ -72,16 +61,17 @@ Preferences.addAll([
   // History
   { id: "places.history.enabled", type: "bool" },
   { id: "browser.formfill.enable", type: "bool" },
   { id: "privacy.history.custom", type: "bool" },
   // Cookies
   { id: "network.cookie.cookieBehavior", type: "int" },
   { id: "network.cookie.lifetimePolicy", type: "int" },
   { id: "network.cookie.blockFutureCookies", type: "bool" },
+  { id: "browser.contentblocking.category", type: "string"},
   // Clear Private Data
   { id: "privacy.sanitize.sanitizeOnShutdown", type: "bool" },
   { id: "privacy.sanitize.timeSpan", type: "int" },
   // Do not track
   { id: "privacy.donottrackheader.enabled", type: "bool" },
 
   // Media
   { id: "media.autoplay.default", type: "int" },
@@ -170,16 +160,25 @@ var gPrivacyPane = {
       let disabled = isLocked || isControlled;
       let tpCheckbox =
         document.getElementById("contentBlockingTrackingProtectionCheckbox");
       // Only enable the TP menu if Detect All Trackers is enabled.
       document.getElementById("trackingProtectionMenu").disabled = disabled ||
         !tpCheckbox.checked;
       tpCheckbox.disabled = disabled;
 
+      document.getElementById("standardRadio").disabled = isControlled;
+      document.getElementById("strictRadio").disabled = isControlled;
+      document.getElementById("contentBlockingOptionStrict").classList.toggle("disabled", isControlled);
+      document.getElementById("contentBlockingOptionStandard").classList.toggle("disabled", isControlled);
+      let arrowButtons = document.querySelectorAll("button.arrowhead");
+      for (let button of arrowButtons) {
+        button.disabled = isControlled;
+      }
+
       // Notify observers that the TP UI has been updated.
       // This is needed since our tests need to be notified about the
       // trackingProtectionMenu element getting disabled/enabled at the right time.
       Services.obs.notifyObservers(window, "privacy-pane-tp-ui-updated");
     }
 
     if (isLocked) {
       // An extension can't control this setting if either pref is locked.
@@ -418,193 +417,148 @@ var gPrivacyPane = {
 
   // CONTENT BLOCKING
 
   /**
    * Initializes the content blocking section.
    */
   initContentBlocking() {
     setEventListener("changeBlockListLink", "click", this.showBlockLists);
-    setEventListener("contentBlockingRestoreDefaults", "command",
-      this.restoreContentBlockingPrefs);
     setEventListener("contentBlockingTrackingProtectionCheckbox", "command",
       this.trackingProtectionWritePrefs);
     setEventListener("contentBlockingTrackingProtectionCheckbox", "command",
       this._updateTrackingProtectionUI);
     setEventListener("trackingProtectionMenu", "command",
       this.trackingProtectionWritePrefs);
-    setEventListener("contentBlockingChangeCookieSettings", "command",
-      this.changeCookieSettings);
-    setEventListener("contentBlockingBlockCookiesCheckbox", "command",
-      this.writeBlockCookiesCheckbox);
+    setEventListener("standardArrow", "command", this.toggleExpansion);
+    setEventListener("strictArrow", "command", this.toggleExpansion);
+    setEventListener("customArrow", "command", this.toggleExpansion);
 
     Preferences.get("network.cookie.cookieBehavior").on("change",
-      gPrivacyPane.readBlockCookiesCheckbox.bind(gPrivacyPane));
+      gPrivacyPane.readBlockCookies.bind(gPrivacyPane));
+    Preferences.get("browser.contentblocking.category").on("change",
+      gPrivacyPane.highlightCBCategory);
 
-    this.readBlockCookiesCheckbox();
+    this.highlightCBCategory();
+    this.readBlockCookies();
 
     let link = document.getElementById("contentBlockingLearnMore");
     let url = Services.urlFormatter.formatURLPref("app.support.baseURL") + "tracking-protection";
     link.setAttribute("href", url);
 
-    // Honour our Content Blocking category UI prefs. If each pref is set to false,
-    // Make all descendants of the corresponding selector hidden.
-    let selectorPrefMap = {
-      ".tracking-protection-ui": contentBlockingTrackingProtectionUiEnabled,
-      ".reject-trackers-ui": contentBlockingRejectTrackersUiEnabled,
-    };
-
-    for (let selector in selectorPrefMap) {
-      let pref = selectorPrefMap[selector];
-      if (!pref) {
-        let elements = document.querySelectorAll(selector);
-        for (let element of elements) {
-          element.hidden = true;
-        }
-      }
+    let warningLinks = document.getElementsByClassName("content-blocking-warning-learn-how");
+    for (let warningLink of warningLinks) {
+      let warningUrl = Services.urlFormatter.formatURLPref("app.support.baseURL") + "content-blocking";
+      warningLink.setAttribute("href", warningUrl);
     }
   },
 
-  /**
-   * Resets all user-exposed content blocking preferences to their default values.
-   */
-  async restoreContentBlockingPrefs() {
-    function clearIfNotLocked(pref) {
-      if (!Services.prefs.prefIsLocked(pref)) {
-        Services.prefs.clearUserPref(pref);
-      }
-    }
-
-    clearIfNotLocked("urlclassifier.trackingTable");
-    clearIfNotLocked("network.cookie.cookieBehavior");
-    clearIfNotLocked("network.cookie.lifetimePolicy");
+  highlightCBCategory() {
+    let value = document.getElementById("contentBlockingCategoryRadio").value;
+    let standardEl = document.getElementById("contentBlockingOptionStandard");
+    let strictEl = document.getElementById("contentBlockingOptionStrict");
+    let customEl = document.getElementById("contentBlockingOptionCustom");
+    standardEl.classList.remove("selected");
+    strictEl.classList.remove("selected");
+    customEl.classList.remove("selected");
 
-    let controllingExtension = await getControllingExtension(
-      PREF_SETTING_TYPE, TRACKING_PROTECTION_KEY);
-    if (!controllingExtension) {
-      for (let preference of TRACKING_PROTECTION_PREFS) {
-        clearIfNotLocked(preference);
-      }
+    switch (value) {
+      case "standard":
+        standardEl.classList.add("selected");
+        break;
+      case "strict":
+        strictEl.classList.add("selected");
+        break;
+      case "custom":
+        customEl.classList.add("selected");
+        break;
     }
   },
 
-  /**
-   * Highlights the Cookies & Site Data UI section.
-   */
-  changeCookieSettings() {
-    gotoPref("privacy-sitedata");
-  },
-
   // TRACKING PROTECTION MODE
 
   /**
-   * Selects the right item of the Tracking Protection radiogroup.
+   * Selects the right item of the Tracking Protection menulist and checkbox.
    */
   trackingProtectionReadPrefs() {
     let enabledPref = Preferences.get("privacy.trackingprotection.enabled");
     let pbmPref = Preferences.get("privacy.trackingprotection.pbmode.enabled");
-    let btpmPref = Preferences.get("browser.privacy.trackingprotection.menu");
-    let tpControl = document.getElementById("trackingProtectionMenu");
+    let tpMenu = document.getElementById("trackingProtectionMenu");
     let tpCheckbox = document.getElementById("contentBlockingTrackingProtectionCheckbox");
 
-    let savedMenuValue;
-    // Only look at the backup pref when restoring the checkbox next to
-    // "All Detected Trackers".
-    if (["always", "private"].includes(btpmPref.value) &&
-        tpCheckbox.checked) {
-      savedMenuValue = btpmPref.value;
-    }
-
     this._updateTrackingProtectionUI();
 
     // Global enable takes precedence over enabled in Private Browsing.
     if (enabledPref.value) {
-      tpControl.value = "always";
-      if (tpCheckbox) {
-        tpCheckbox.checked = true;
-      }
+      tpMenu.value = "always";
+      tpCheckbox.checked = true;
     } else if (pbmPref.value) {
-      tpControl.value = "private";
-      if (tpCheckbox) {
-        tpCheckbox.checked = true;
-      }
-    } else if (!tpCheckbox) {
-      tpControl.value = "never";
+      tpMenu.value = "private";
+      tpCheckbox.checked = true;
     } else {
-      if (savedMenuValue) {
-        tpControl.value = savedMenuValue;
-      }
+      tpMenu.value = "never";
       tpCheckbox.checked = false;
     }
   },
 
   /**
    * Selects the right items of the new Cookies & Site Data UI.
    */
   networkCookieBehaviorReadPrefs() {
     let behavior = Preferences.get("network.cookie.cookieBehavior").value;
-    let blockCookiesCtrl = document.getElementById("blockCookies");
-    let blockCookiesLabel = document.getElementById("blockCookiesLabel");
     let blockCookiesMenu = document.getElementById("blockCookiesMenu");
     let deleteOnCloseCheckbox = document.getElementById("deleteOnClose");
 
-    let blockCookies = (behavior != 0);
+    let blockCookies = (behavior != Ci.nsICookieService.BEHAVIOR_ACCEPT);
     let cookieBehaviorLocked = Services.prefs.prefIsLocked("network.cookie.cookieBehavior");
     let blockCookiesControlsDisabled = !blockCookies || cookieBehaviorLocked;
-    blockCookiesLabel.disabled = blockCookiesMenu.disabled = blockCookiesControlsDisabled;
+    blockCookiesMenu.disabled = blockCookiesControlsDisabled;
 
-    let completelyBlockCookies = (behavior == 2);
+    let completelyBlockCookies = (behavior == Ci.nsICookieService.BEHAVIOR_REJECT);
     let privateBrowsing = Preferences.get("browser.privatebrowsing.autostart").value;
     let cookieExpirationLocked = Services.prefs.prefIsLocked("network.cookie.lifetimePolicy");
     deleteOnCloseCheckbox.disabled = privateBrowsing || completelyBlockCookies ||
                                      cookieExpirationLocked;
 
     switch (behavior) {
       case Ci.nsICookieService.BEHAVIOR_ACCEPT:
-        blockCookiesCtrl.value = "allow";
         break;
       case Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN:
-        blockCookiesCtrl.value = "disallow";
         blockCookiesMenu.value = "all-third-parties";
         break;
       case Ci.nsICookieService.BEHAVIOR_REJECT:
-        blockCookiesCtrl.value = "disallow";
         blockCookiesMenu.value = "always";
         break;
       case Ci.nsICookieService.BEHAVIOR_LIMIT_FOREIGN:
-        blockCookiesCtrl.value = "disallow";
         blockCookiesMenu.value = "unvisited";
         break;
       case Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER:
-        blockCookiesCtrl.value = "disallow";
         blockCookiesMenu.value = "trackers";
         break;
     }
   },
 
   /**
    * Sets the pref values based on the selected item of the radiogroup.
    */
   trackingProtectionWritePrefs() {
     let enabledPref = Preferences.get("privacy.trackingprotection.enabled");
     let pbmPref = Preferences.get("privacy.trackingprotection.pbmode.enabled");
-    let btpmPref = Preferences.get("browser.privacy.trackingprotection.menu");
-    let tpControl = document.getElementById("trackingProtectionMenu");
+    let tpMenu = document.getElementById("trackingProtectionMenu");
     let tpCheckbox = document.getElementById("contentBlockingTrackingProtectionCheckbox");
 
     let value;
-    if (tpCheckbox) {
-      if (tpCheckbox.checked) {
-        value = tpControl.value;
-        btpmPref.value = value;
-      } else {
-        value = "never";
+    if (tpCheckbox.checked) {
+      if (tpMenu.value == "never") {
+        tpMenu.value = "private";
       }
+      value = tpMenu.value;
     } else {
-      value = tpControl.value;
+      tpMenu.value = "never";
+      value = "never";
     }
 
     switch (value) {
       case "always":
         enabledPref.value = true;
         pbmPref.value = true;
         break;
       case "private":
@@ -613,16 +567,22 @@ var gPrivacyPane = {
         break;
       case "never":
         enabledPref.value = false;
         pbmPref.value = false;
         break;
     }
   },
 
+  toggleExpansion(e) {
+    let carat = e.target;
+    carat.classList.toggle("up");
+    carat.closest(".content-blocking-category").classList.toggle("expanded");
+  },
+
   // HISTORY MODE
 
   /**
    * The list of preferences which affect the initial history mode settings.
    * If the auto start private browsing mode pref is active, the initial
    * history mode would be set to "Don't remember anything".
    * If ALL of these preferences are set to the values that correspond
    * to keeping some part of history, and the auto-start
@@ -941,154 +901,41 @@ var gPrivacyPane = {
 
   writeDeleteOnClose() {
     let checkbox = document.getElementById("deleteOnClose");
     return checkbox.checked ? Ci.nsICookieService.ACCEPT_SESSION : Ci.nsICookieService.ACCEPT_NORMALLY;
   },
 
   /**
    * Reads the network.cookie.cookieBehavior preference value and
-   * enables/disables the rest of the new cookie & site data UI accordingly.
-   *
-   * Returns "allow" if cookies are accepted and "disallow" if they are entirely
-   * disabled.
+   * enables/disables the "blockCookiesMenu" menulist accordingly.
    */
   readBlockCookies() {
-    // enable the rest of the UI for anything other than "accept all cookies"
     let pref = Preferences.get("network.cookie.cookieBehavior");
-    let blockCookies = (pref.value != 0);
-
-    // Our top-level setting is a radiogroup that only sets "enable all"
-    // and "disable all", so convert the pref value accordingly.
-    return blockCookies ? "disallow" : "allow";
+    let bcControl = document.getElementById("blockCookiesMenu");
+    bcControl.disabled = pref.value == Ci.nsICookieService.BEHAVIOR_ACCEPT;
   },
 
   /**
    * Updates the "accept third party cookies" menu based on whether the
-   * "accept cookies" or "block cookies" radio buttons are selected.
+   * "contentBlockingBlockCookiesCheckbox" checkbox is checked.
    */
   writeBlockCookies() {
-    let block = document.getElementById("blockCookies");
+    let block = document.getElementById("contentBlockingBlockCookiesCheckbox");
     let blockCookiesMenu = document.getElementById("blockCookiesMenu");
 
-    // if we're disabling cookies, automatically select 'third-party trackers'
-    if (block.value == "disallow") {
+    if (block.checked) {
+      // Automatically select 'third-party trackers' as the default.
       blockCookiesMenu.selectedIndex = 0;
       return this.writeBlockCookiesFrom();
     }
 
     return Ci.nsICookieService.BEHAVIOR_ACCEPT;
   },
 
-  enableThirdPartyCookiesUI() {
-    document.getElementById("blockCookiesCBDeck").selectedIndex = 0;
-    document.getElementById("contentBlockingChangeCookieSettings").hidden = true;
-  },
-
-  disableThirdPartyCookiesUI(reason) {
-    let deckIndex = 0;
-    switch (reason) {
-      case "always":
-        deckIndex = 1;
-        break;
-      case "unvisited":
-        deckIndex = 2;
-        break;
-    }
-    document.getElementById("blockCookiesCBDeck").selectedIndex = deckIndex;
-    document.getElementById("contentBlockingChangeCookieSettings").hidden = false;
-  },
-
-  /**
-   * Converts between network.cookie.cookieBehavior and the new content blocking UI
-   */
-  readBlockCookiesCB() {
-    let pref = Preferences.get("network.cookie.cookieBehavior");
-    switch (pref.value) {
-      case Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN:
-        return "all-third-parties";
-      case Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER:
-        return "trackers";
-      default:
-        return undefined;
-    }
-  },
-
-  writeBlockCookiesCB() {
-    let block = document.getElementById("blockCookiesCB").selectedItem;
-    switch (block.value) {
-      case "trackers":
-        return Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER;
-      case "all-third-parties":
-        return Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN;
-      default:
-        return undefined;
-    }
-  },
-
-  writeBlockCookiesCheckbox() {
-    let pref = Preferences.get("network.cookie.cookieBehavior");
-    let bcCheckbox = document.getElementById("contentBlockingBlockCookiesCheckbox");
-    let bcControl = document.getElementById("blockCookiesCB");
-
-    let value;
-    if (bcCheckbox.checked) {
-      value = bcControl.selectedItem.value;
-    } else {
-      value = "none";
-    }
-
-    switch (value) {
-      case "trackers":
-      case "all-third-parties":
-        bcControl.disabled = false;
-        pref.value = this.writeBlockCookiesCB();
-        break;
-      default:
-        bcControl.disabled = true;
-        pref.value = Ci.nsICookieService.BEHAVIOR_ACCEPT;
-        break;
-    }
-  },
-
-  readBlockCookiesCheckbox() {
-    let pref = Preferences.get("network.cookie.cookieBehavior");
-    let bcCheckbox = document.getElementById("contentBlockingBlockCookiesCheckbox");
-    let bcControl = document.getElementById("blockCookiesCB");
-
-    switch (pref.value) {
-      case Ci.nsICookieService.BEHAVIOR_ACCEPT:
-        this.enableThirdPartyCookiesUI();
-        bcCheckbox.checked = false;
-        bcControl.disabled = true;
-        break;
-      case Ci.nsICookieService.BEHAVIOR_REJECT:
-        this.disableThirdPartyCookiesUI("always");
-        break;
-      case Ci.nsICookieService.BEHAVIOR_LIMIT_FOREIGN:
-        this.disableThirdPartyCookiesUI("unvisited");
-        break;
-      case Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN:
-        this.enableThirdPartyCookiesUI();
-        bcCheckbox.checked = true;
-        bcControl.disabled = false;
-        break;
-      case Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER:
-        this.enableThirdPartyCookiesUI();
-        bcCheckbox.checked = true;
-        bcControl.disabled = false;
-        break;
-      default:
-        break;
-    }
-  },
-
-  /**
-   * Converts between network.cookie.cookieBehavior and the new third-party cookies UI
-   */
   readBlockCookiesFrom() {
     let pref = Preferences.get("network.cookie.cookieBehavior");
     switch (pref.value) {
       case Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN:
         return "all-third-parties";
       case Ci.nsICookieService.BEHAVIOR_REJECT:
         return "always";
       case Ci.nsICookieService.BEHAVIOR_LIMIT_FOREIGN:
--- a/browser/components/preferences/in-content/privacy.xul
+++ b/browser/components/preferences/in-content/privacy.xul
@@ -18,129 +18,183 @@
 
 <!-- Tracking / Content Blocking -->
 <groupbox id="trackingGroup" data-category="panePrivacy" hidden="true" aria-describedby="contentBlockingDescription">
   <caption>
     <label id="contentBlockingHeader" data-l10n-id="content-blocking-header"/>
   </caption>
   <vbox data-subcategory="trackingprotection">
     <hbox align="start">
+      <image id="trackingProtectionShield"/>
       <vbox flex="1">
-        <description id="contentBlockingDescription" class="description-with-side-element" data-l10n-id="content-blocking-desc"></description>
-        <label id="contentBlockingLearnMore" data-l10n-id="content-blocking-learn-more" class="learnMore text-link"/>
+        <description class="description-with-side-element">
+          <html:span id="contentBlockingDescription" class="tail-with-learn-more" data-l10n-id="content-blocking-description"></html:span>
+          <label id="contentBlockingLearnMore" class="learnMore text-link" data-l10n-id="content-blocking-learn-more"/>
+        </description>
       </vbox>
       <vbox>
         <!-- Please don't remove the wrapping hbox/vbox/box for these elements. It's used to properly compute the search tooltip position. -->
         <hbox>
-          <button id="contentBlockingRestoreDefaults"
-                  class="accessory-button"
-                  flex="1"
-                  data-l10n-id="content-blocking-restore-defaults"/>
-        </hbox>
-        <!-- Please don't remove the wrapping hbox/vbox/box for these elements. It's used to properly compute the search tooltip position. -->
-        <hbox>
           <button id="trackingProtectionExceptions"
                   class="accessory-button"
                   flex="1"
-                  data-l10n-id="tracking-exceptions"
+                  data-l10n-id="tracking-manage-exceptions"
                   preference="pref.privacy.disable_button.tracking_protection_exceptions"
                   search-l10n-ids="
                     permissions-remove.label,
                     permissions-remove-all.label,
                     permissions-button-cancel.label,
                     permissions-button-ok.label,
                     permissions-exceptions-content-blocking-window.title,
                     permissions-exceptions-content-blocking-desc,
                   "/>
         </hbox>
       </vbox>
     </hbox>
     <vbox id="contentBlockingCategories">
-      <label id="content-blocking-categories-label" data-l10n-id="content-blocking-category-label"/>
-      <vbox>
-        <hbox class="content-blocking-category tracking-protection-ui">
-          <vbox>
-            <checkbox id="contentBlockingTrackingProtectionCheckbox"
-                      class="content-blocking-checkbox" flex="1"
-                      src="chrome://browser/skin/controlcenter/trackers.svg"
-                      data-l10n-id="content-blocking-tracking-protection-trackers-label"/>
-            <vbox class="content-blocking-category-labels" flex="1">
+        <radiogroup id="contentBlockingCategoryRadio"
+                    preference="browser.contentblocking.category"
+                    aria-labelledby="trackingProtectionMenuDesc">
+          <vbox id="contentBlockingOptionStandard" class="content-blocking-category">
+            <hbox>
+              <radio id="standardRadio"
+                     value="standard"
+                     data-l10n-id="content-blocking-setting-standard"
+                     flex="1"/>
+              <button id="standardArrow" class="arrowhead"/>
+            </hbox>
+            <vbox class="indent">
+              <description data-l10n-id="content-blocking-standard-desc"></description>
+              <vbox class="content-blocking-extra-information">
+                <vbox class="indent">
+                  <hbox class="extra-information-label">
+                    <image class="content-blocking-trackers-image"/>
+                    <label data-l10n-id="content-blocking-private-trackers"/>
+                  </hbox>
+                  <hbox class="extra-information-label">
+                    <image class="content-blocking-cookies-image"/>
+                    <label data-l10n-id="content-blocking-third-party-cookies"/>
+                  </hbox>
+                </vbox>
+              </vbox>
+            </vbox>
+          </vbox>
+          <vbox id="contentBlockingOptionStrict" class="content-blocking-category">
+            <hbox>
+              <radio id="strictRadio"
+                     value="strict"
+                     data-l10n-id="content-blocking-setting-strict"
+                     flex="1"/>
+              <button id="strictArrow" class="arrowhead"/>
+            </hbox>
+            <vbox class="indent">
+              <label data-l10n-id="content-blocking-strict-desc"></label>
+              <vbox class="content-blocking-extra-information">
+                <vbox class="indent">
+                  <hbox class="extra-information-label">
+                    <image class="content-blocking-trackers-image"/>
+                    <label data-l10n-id="content-blocking-all-windows-trackers"/>
+                  </hbox>
+                  <hbox class="extra-information-label">
+                    <image class="content-blocking-cookies-image"/>
+                    <label data-l10n-id="content-blocking-all-third-party-cookies"/>
+                  </hbox>
+                </vbox>
+                <vbox class="content-blocking-warning">
+                  <vbox class="indent">
+                    <hbox>
+                      <image class="content-blocking-warning-image"/>
+                      <label class="content-blocking-warning-title" data-l10n-id="content-blocking-warning-title"/>
+                    </hbox>
+                    <description class="indent">
+                      <html:span class="tail-with-learn-more" data-l10n-id="content-blocking-warning-desc"></html:span>
+                      <label class="text-link content-blocking-warning-learn-how" data-l10n-id="content-blocking-learn-how"/>
+                    </description>
+                  </vbox>
+                </vbox>
+              </vbox>
+            </vbox>
+          </vbox>
+        <vbox id="contentBlockingOptionCustom" class="tracking-protection-ui content-blocking-category">
+          <hbox>
+            <radio id="customRadio"
+                   value="custom"
+                   data-l10n-id="content-blocking-setting-custom"
+                   flex="1"/>
+            <button id="customArrow" class="arrowhead"/>
+          </hbox>
+          <vbox class="indent">
+            <description id="contentBlockingCustomDesc" data-l10n-id="content-blocking-custom-desc"></description>
+            <vbox class="content-blocking-extra-information">
               <hbox id="contentBlockingTrackingProtectionExtensionContentLabel"
                     align="center" hidden="true" class="extension-controlled">
                 <description control="contentBlockingDisableTrackingProtectionExtension" flex="1"/>
                 <button id="contentBlockingDisableTrackingProtectionExtension"
                         class="extension-controlled-button accessory-button"
                         data-l10n-id="disable-extension" hidden="true"/>
               </hbox>
-              <description data-l10n-id="content-blocking-tracking-protection-new-description"
-                           class="content-blocking-category-description"
-                           id="trackingProtectionMenuDesc"/>
-              <radiogroup id="trackingProtectionMenu"
-                          aria-labelledby="trackingProtectionMenuDesc">
-                <radio value="private"
-                       data-l10n-id="content-blocking-tracking-protection-option-private"
-                       flex="1" />
-                <radio value="always"
-                       data-l10n-id="content-blocking-tracking-protection-option-always"
-                       flex="1" />
-              </radiogroup>
-              <label id="changeBlockListLink"
-                     data-l10n-id="content-blocking-tracking-protection-change-block-list"
-                     class="text-link"
-                     search-l10n-ids="blocklist-window.title, blocklist-desc, blocklist-button-cancel.label, blocklist-button-ok.label"/>
+                <hbox class="custom-option">
+                  <checkbox id="contentBlockingTrackingProtectionCheckbox"
+                            class="content-blocking-checkbox" flex="1"
+                            data-l10n-id="content-blocking-tracking-protection-trackers-label"
+                            aria-describedby="contentBlockingCustomDesc"/>
+                  <vbox>
+                    <menulist id="trackingProtectionMenu">
+                      <menupopup>
+                        <menuitem data-l10n-id="content-blocking-tracking-protection-option-private" value="private"/>
+                        <menuitem data-l10n-id="content-blocking-tracking-protection-option-always" value="always"/>
+                      </menupopup>
+                    </menulist>
+                  </vbox>
+                </hbox>
+                <label id="changeBlockListLink"
+                       data-l10n-id="content-blocking-tracking-protection-change-block-list"
+                       class="text-link"
+                       search-l10n-ids="blocklist-window.title, blocklist-desc, blocklist-button-cancel.label, blocklist-button-ok.label"/>
+                <hbox class="reject-trackers-ui custom-option">
+                  <checkbox id="contentBlockingBlockCookiesCheckbox"
+                            class="content-blocking-checkbox" flex="1"
+                            data-l10n-id="content-blocking-cookies-label"
+                            aria-describedby="contentBlockingCustomDesc"
+                            preference="network.cookie.cookieBehavior"
+                            onsyncfrompreference="return gPrivacyPane.readBlockCookies();"
+                            onsynctopreference="return gPrivacyPane.writeBlockCookies();"/>
+                  <vbox>
+                    <!-- Please don't remove the wrapping hbox/vbox/box for these elements. It's used to properly compute the search tooltip position. -->
+                    <hbox>
+                      <menulist id="blockCookiesMenu"
+                                preference="network.cookie.cookieBehavior"
+                                onsyncfrompreference="return gPrivacyPane.readBlockCookiesFrom();"
+                                onsynctopreference="return gPrivacyPane.writeBlockCookiesFrom();">
+                        <menupopup>
+                          <menuitem data-l10n-id="sitedata-block-trackers-option-recommended" value="trackers"/>
+                          <menuitem data-l10n-id="sitedata-block-unvisited-option" value="unvisited"/>
+                          <menuitem data-l10n-id="sitedata-block-all-third-party-option" value="all-third-parties"/>
+                          <menuitem data-l10n-id="sitedata-block-all-option" value="always"/>
+                        </menupopup>
+                      </menulist>
+                    </hbox>
+                  </vbox>
+                </hbox>
+              <vbox class="content-blocking-warning">
+                <vbox class="indent">
+                  <hbox>
+                    <image class="content-blocking-warning-image"/>
+                    <label class="content-blocking-warning-title" data-l10n-id="content-blocking-warning-title"/>
+                  </hbox>
+                  <description class="indent">
+                    <html:span class="tail-with-learn-more" data-l10n-id="content-blocking-warning-desc"></html:span>
+                    <label class="text-link content-blocking-warning-learn-how" data-l10n-id="content-blocking-learn-how"/>
+                  </description>
+                </vbox>
+              </vbox>
             </vbox>
           </vbox>
-        </hbox>
-      </vbox>
-      <hbox class="content-blocking-category reject-trackers-ui">
-        <hbox flex="1">
-          <vbox class="content-blocking-category-checkbox">
-            <checkbox id="contentBlockingBlockCookiesCheckbox"
-                      class="content-blocking-checkbox" flex="1"
-                      src="chrome://browser/skin/controlcenter/3rdpartycookies.svg"
-                      data-l10n-id="content-blocking-third-party-cookies-label"/>
-            <vbox class="content-blocking-category-labels" flex="1">
-              <hbox>
-                <vbox flex="1">
-                  <deck id="blockCookiesCBDeck">
-                    <description id="blockCookiesCBDesc"
-                                 data-l10n-id="content-blocking-reject-trackers-description"
-                                 class="content-blocking-category-description"/>
-                    <description data-l10n-id="content-blocking-reject-trackers-warning-your-settings-prevent-changes"
-                                 class="content-blocking-category-description description-with-side-element warning-description"/>
-                    <description data-l10n-id="content-blocking-reject-trackers-warning-your-settings-prevent-changes"
-                                 class="content-blocking-category-description description-with-side-element warning-description"/>
-                  </deck>
-                </vbox>
-                <hbox align="center" pack="end">
-                  <vbox align="center">
-                    <button id="contentBlockingChangeCookieSettings"
-                            class="accessory-button"
-                            flex="1"
-                            hidden="true"
-                            data-l10n-id="content-blocking-change-cookie-settings"/>
-                  </vbox>
-                </hbox>
-              </hbox>
-              <radiogroup id="blockCookiesCB"
-                          aria-labelledby="blockCookiesCBDesc"
-                          preference="network.cookie.cookieBehavior"
-                          onsyncfrompreference="return gPrivacyPane.readBlockCookiesCB();"
-                          onsynctopreference="return gPrivacyPane.writeBlockCookiesCB();">
-                <radio value="trackers"
-                       data-l10n-id="content-blocking-reject-trackers-block-trackers-option-recommended"
-                       flex="1" />
-                <radio value="all-third-parties"
-                       data-l10n-id="content-blocking-reject-trackers-all-third-parties-option"
-                       flex="1" />
-              </radiogroup>
-            </vbox>
-          </vbox>
-        </hbox>
-      </hbox>
+        </vbox>
+      </radiogroup>
     </vbox>
     <vbox id="doNotTrackLearnMoreBox">
       <label><label class="tail-with-learn-more" data-l10n-id="do-not-track-description" id="doNotTrackDesc"></label><label
       class="learnMore text-link" href="https://www.mozilla.org/dnt"
       data-l10n-id="do-not-track-learn-more"></label></label>
       <radiogroup id="doNotTrackRadioGroup" aria-labelledby="doNotTrackDesc" preference="privacy.donottrackheader.enabled">
         <radio value="true" data-l10n-id="do-not-track-option-always"/>
         <radio value="false" data-l10n-id="do-not-track-option-default-content-blocking"/>
@@ -155,57 +209,27 @@
 
   <hbox data-subcategory="sitedata" align="baseline">
     <vbox flex="1">
       <description class="description-with-side-element" flex="1">
         <html:span id="totalSiteDataSize" class="tail-with-learn-more"></html:span>
         <label id="siteDataLearnMoreLink"
           class="learnMore text-link" data-l10n-id="sitedata-learn-more"/>
       </description>
-      <radiogroup id="blockCookies"
-                  preference="network.cookie.cookieBehavior"
-                  onsyncfrompreference="return gPrivacyPane.readBlockCookies();"
-                  onsynctopreference="return gPrivacyPane.writeBlockCookies();">
-        <radio value="allow"
-               data-l10n-id="sitedata-allow-cookies-option"
-               flex="1" />
-        <radio value="disallow"
-               data-l10n-id="sitedata-disallow-cookies-option"
-               flex="1" />
-        <hbox id="blockThirdPartyRow"
-              class="indent"
-              align="center">
-          <label id="blockCookiesLabel" control="blockCookiesMenu"
-                 data-l10n-id="sitedata-block-desc"/>
-          <!-- Please don't remove the wrapping hbox/vbox/box for these elements. It's used to properly compute the search tooltip position. -->
-          <hbox>
-            <menulist id="blockCookiesMenu" preference="network.cookie.cookieBehavior"
-                      onsyncfrompreference="return gPrivacyPane.readBlockCookiesFrom();"
-                      onsynctopreference="return gPrivacyPane.writeBlockCookiesFrom();">
-              <menupopup>
-                <menuitem data-l10n-id="sitedata-block-trackers-option-recommended" value="trackers"/>
-                <menuitem data-l10n-id="sitedata-block-unvisited-option" value="unvisited"/>
-                <menuitem data-l10n-id="sitedata-block-all-third-party-option" value="all-third-parties"/>
-                <menuitem data-l10n-id="sitedata-block-all-option" value="always"/>
-              </menupopup>
-            </menulist>
-          </hbox>
-        </hbox>
-        <hbox id="keepRow"
-              align="center">
-          <checkbox id="deleteOnClose"
-                    data-l10n-id="sitedata-delete-on-close"
-                    preference="network.cookie.lifetimePolicy"
-                    onsyncfrompreference="return gPrivacyPane.readDeleteOnClose();"
-                    onsynctopreference="return gPrivacyPane.writeDeleteOnClose();"
-                    flex="1" />
-        </hbox>
-      </radiogroup>
+      <hbox id="keepRow"
+            align="center">
+        <checkbox id="deleteOnClose"
+                  data-l10n-id="sitedata-delete-on-close"
+                  preference="network.cookie.lifetimePolicy"
+                  onsyncfrompreference="return gPrivacyPane.readDeleteOnClose();"
+                  onsynctopreference="return gPrivacyPane.writeDeleteOnClose();"
+                  flex="1" />
+      </hbox>
     </vbox>
-    <vbox>
+    <vbox align="end">
       <!-- Please don't remove the wrapping hbox/vbox/box for these elements. It's used to properly compute the search tooltip position. -->
       <hbox>
         <button id="clearSiteDataButton"
             class="accessory-button"
             icon="clear"
             search-l10n-ids="clear-site-data-cookies-empty.label, clear-site-data-cache-empty.label"
             data-l10n-id="sitedata-clear"/>
       </hbox>
@@ -220,17 +244,17 @@
                   site-data-column-storage.label,
                   site-data-settings-description,
                   site-data-remove-all.label,
                 "/>
       </hbox>
       <hbox>
         <button id="cookieExceptions"
                 class="accessory-button"
-                data-l10n-id="sitedata-cookies-exceptions"
+                data-l10n-id="sitedata-cookies-permissions"
                 preference="pref.privacy.disable_button.cookie_exceptions"
                 search-l10n-ids="
                   permissions-address,
                   permissions-block.label,
                   permissions-allow.label,
                   permissions-remove.label,
                   permissions-remove-all.label,
                   permissions-button-cancel.label,
--- a/browser/components/preferences/in-content/tests/browser_contentblocking.js
+++ b/browser/components/preferences/in-content/tests/browser_contentblocking.js
@@ -1,16 +1,17 @@
 /* eslint-env webextensions */
 
 const CB_TP_UI_PREF = "browser.contentblocking.trackingprotection.ui.enabled";
 const CB_RT_UI_PREF = "browser.contentblocking.rejecttrackers.ui.enabled";
 const TP_PREF = "privacy.trackingprotection.enabled";
 const TP_PBM_PREF = "privacy.trackingprotection.pbmode.enabled";
 const TP_LIST_PREF = "urlclassifier.trackingTable";
 const NCB_PREF = "network.cookie.cookieBehavior";
+const CAT_PREF = "browser.contentblocking.category";
 
 requestLongerTimeout(2);
 
 // Tests that the content blocking main category checkboxes have the correct default state.
 add_task(async function testContentBlockingMainCategory() {
   let prefs = [
     [TP_PREF, false],
     [TP_PBM_PREF, true],
@@ -53,63 +54,64 @@ add_task(async function testContentBlock
     "#trackingProtectionMenuDesc",
     ".content-blocking-category-name",
     "#changeBlockListLink",
   ];
 
   tpCheckbox.checked = true;
 
   // Select "Always" under "All Detected Trackers".
-  let always = doc.querySelector("#trackingProtectionMenu > radio[value=always]");
-  let private = doc.querySelector("#trackingProtectionMenu > radio[value=private]");
-  always.radioGroup.selectedItem = always;
+  let menu = doc.querySelector("#trackingProtectionMenu");
+  let always = doc.querySelector("#trackingProtectionMenu > menupopup > menuitem[value=always]");
+  let private = doc.querySelector("#trackingProtectionMenu > menupopup > menuitem[value=private]");
+  menu.selectedItem = always;
   ok(!private.selected, "The Only in private windows item should not be selected");
   ok(always.selected, "The Always item should be selected");
 
   // The first time, privacy-pane-tp-ui-updated won't be dispatched since the
   // assignment above is a no-op.
 
   // Ensure the dependent controls are enabled
   checkControlState(doc, dependentControls, true);
   checkControlState(doc, alwaysEnabledControls, true);
 
   let promise = TestUtils.topicObserved("privacy-pane-tp-ui-updated");
-  EventUtils.synthesizeMouseAtCenter(tpCheckbox, {}, doc.defaultView);
+  tpCheckbox.click();
 
   await promise;
   ok(!tpCheckbox.checked, "The checkbox should now be unchecked");
 
   // Ensure the dependent controls are disabled
   checkControlState(doc, dependentControls, false);
   checkControlState(doc, alwaysEnabledControls, true);
 
   // Make sure the selection in the tracking protection submenu persists after
   // a few times of checking and unchecking All Detected Trackers.
   // Doing this in a loop in order to avoid typing in the unrolled version manually.
   // We need to go from the checked state of the checkbox to unchecked back to
   // checked again...
   for (let i = 0; i < 3; ++i) {
     promise = TestUtils.topicObserved("privacy-pane-tp-ui-updated");
-    EventUtils.synthesizeMouseAtCenter(tpCheckbox, {}, doc.defaultView);
+    tpCheckbox.click();
 
     await promise;
     is(tpCheckbox.checked, i % 2 == 0, "The checkbox should now be unchecked");
-    ok(!private.selected, "The Only in private windows item should still not be selected");
-    ok(always.selected, "The Always item should still be selected");
+    is(private.selected, i % 2 == 0, "The Only in private windows item should be selected by default, when the checkbox is checked");
+    ok(!always.selected, "The Always item should no longer be selected");
   }
 
   gBrowser.removeCurrentTab();
 
   for (let pref of prefs) {
     SpecialPowers.clearUserPref(pref[0]);
   }
 });
 
-// Tests that the content blocking "Restore Defaults" button does what it's supposed to.
-add_task(async function testContentBlockingRestoreDefaults() {
+// Tests that the content blocking "Standard" category radio sets the prefs to their default values.
+add_task(async function testContentBlockingStandardCategory() {
   let prefs = {
     [TP_LIST_PREF]: null,
     [TP_PREF]: null,
     [TP_PBM_PREF]: null,
     [NCB_PREF]: null,
   };
 
   for (let pref in prefs) {
@@ -154,173 +156,121 @@ add_task(async function testContentBlock
     default:
       ok(false, `Unknown pref type for ${pref}`);
     }
   }
 
   await openPreferencesViaOpenPreferencesAPI("privacy", {leaveOpen: true});
   let doc = gBrowser.contentDocument;
 
-  let contentBlockingRestoreDefaults = doc.getElementById("contentBlockingRestoreDefaults");
-  contentBlockingRestoreDefaults.click();
+  let standardRadioOption = doc.getElementById("standardRadio");
+  standardRadioOption.click();
 
   // TP prefs are reset async to check for extensions controlling them.
   await TestUtils.waitForCondition(() => !Services.prefs.prefHasUserValue(TP_PREF));
 
   for (let pref in prefs) {
     ok(!Services.prefs.prefHasUserValue(pref), `reset the pref ${pref}`);
   }
+  is(Services.prefs.getStringPref(CAT_PREF), "standard", `${CAT_PREF} has been set to standard`);
+
+  gBrowser.removeCurrentTab();
+});
+
+// Tests that the content blocking "Strict" category radio sets the prefs to the expected values.
+add_task(async function testContentBlockingStrictCategory() {
+  Services.prefs.setBoolPref(TP_PREF, false);
+  Services.prefs.setBoolPref(TP_PBM_PREF, false);
+  Services.prefs.setIntPref(NCB_PREF, Ci.nsICookieService.BEHAVIOR_LIMIT_FOREIGN);
+  Services.prefs.setStringPref(TP_LIST_PREF, "test-track-simple,base-track-digest256,content-track-digest256");
+
+  await openPreferencesViaOpenPreferencesAPI("privacy", {leaveOpen: true});
+  let doc = gBrowser.contentDocument;
+
+  let strictRadioOption = doc.getElementById("strictRadio");
+  strictRadioOption.click();
+
+  // TP prefs are reset async to check for extensions controlling them.
+  await TestUtils.waitForCondition(() => Services.prefs.prefHasUserValue(TP_PREF));
+
+  is(Services.prefs.getStringPref(CAT_PREF), "strict", `${CAT_PREF} has been set to strict`);
+  is(Services.prefs.getBoolPref(TP_PREF), true, `${TP_PREF} has been set to true`);
+  is(Services.prefs.getBoolPref(TP_PBM_PREF), true, `${TP_PBM_PREF} has been set to true`);
+  is(Services.prefs.getIntPref(NCB_PREF), Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN, `${NCB_PREF} has been set to ${Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN}`);
+  ok(!Services.prefs.prefHasUserValue(TP_LIST_PREF), `reset the pref ${TP_LIST_PREF}`);
 
   gBrowser.removeCurrentTab();
 });
 
-// Tests that the content blocking "Restore Defaults" button does not restore prefs
-// that are controlled by extensions.
-add_task(async function testContentBlockingRestoreDefaultsSkipExtensionControlled() {
-  function background() {
-    browser.privacy.websites.trackingProtectionMode.set({value: "always"});
-  }
-
-  // Install an extension that sets Tracking Protection.
-  let extension = ExtensionTestUtils.loadExtension({
-    useAddonManager: "permanent",
-    manifest: {
-      name: "set_tp",
-      applications: {gecko: {id: "@set_tp"}},
-      permissions: ["privacy"],
-    },
-    background,
-  });
-
-  let resettable = {
-    [TP_LIST_PREF]: null,
-    [NCB_PREF]: null,
-  };
-
-  for (let pref in resettable) {
-    switch (Services.prefs.getPrefType(pref)) {
-    case Services.prefs.PREF_STRING:
-      resettable[pref] = Services.prefs.getCharPref(pref);
-      break;
-    case Services.prefs.PREF_INT:
-      resettable[pref] = Services.prefs.getIntPref(pref);
-      break;
-    default:
-      ok(false, `Unknown pref type for ${pref}`);
-    }
-  }
-
-  Services.prefs.setStringPref(TP_LIST_PREF, "test-track-simple,base-track-digest256,content-track-digest256");
-  Services.prefs.setIntPref(NCB_PREF, Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER);
-
-  for (let pref in resettable) {
-    switch (Services.prefs.getPrefType(pref)) {
-    case Services.prefs.PREF_STRING:
-      // Account for prefs that may have retained their default value
-      if (Services.prefs.getCharPref(pref) != resettable[pref]) {
-        ok(Services.prefs.prefHasUserValue(pref), `modified the pref ${pref}`);
-      }
-      break;
-    case Services.prefs.PREF_INT:
-      if (Services.prefs.getIntPref(pref) != resettable[pref]) {
-        ok(Services.prefs.prefHasUserValue(pref), `modified the pref ${pref}`);
-      }
-      break;
-    default:
-      ok(false, `Unknown pref type for ${pref}`);
-    }
-  }
-
-  await extension.startup();
-
-  await TestUtils.waitForCondition(() => Services.prefs.prefHasUserValue(TP_PREF));
-
-  let disabledControls = [
-    ".tracking-protection-ui .content-blocking-checkbox",
-    "#trackingProtectionMenu",
-    "[control=trackingProtectionMenu]",
-  ];
+// Tests that the content blocking "Custom" category behaves as expected.
+add_task(async function testContentBlockingCustomCategory() {
+  let prefs = [TP_LIST_PREF, TP_PREF, TP_PBM_PREF, NCB_PREF];
 
   await openPreferencesViaOpenPreferencesAPI("privacy", {leaveOpen: true});
   let doc = gBrowser.contentDocument;
+  let strictRadioOption = doc.getElementById("strictRadio");
+  let standardRadioOption = doc.getElementById("standardRadio");
+  let customRadioOption = doc.getElementById("customRadio");
 
-  checkControlState(doc, disabledControls, false);
+  standardRadioOption.click();
+  await TestUtils.waitForCondition(() => !Services.prefs.prefHasUserValue(TP_PREF));
 
-  let contentBlockingRestoreDefaults = doc.getElementById("contentBlockingRestoreDefaults");
-  contentBlockingRestoreDefaults.click();
+  customRadioOption.click();
+  await TestUtils.waitForCondition(() => Services.prefs.getStringPref(CAT_PREF) == "custom");
+  // The custom option does not force changes of any prefs, other than CAT_PREF, all other TP prefs should remain as they were for standard.
+  for (let pref of prefs) {
+    ok(!Services.prefs.prefHasUserValue(pref), `the pref ${pref} remains as default value`);
+  }
+  is(Services.prefs.getStringPref(CAT_PREF), "custom", `${CAT_PREF} has been set to custom`);
 
-  for (let pref in resettable) {
-    ok(!Services.prefs.prefHasUserValue(pref), `reset the pref ${pref}`);
-  }
+  strictRadioOption.click();
+  await TestUtils.waitForCondition(() => Services.prefs.prefHasUserValue(TP_PREF));
+
+  // Changing the TP_PREF should necessarily set CAT_PREF to "custom"
+  Services.prefs.setBoolPref(TP_PREF, false);
+  await TestUtils.waitForCondition(() => !Services.prefs.prefHasUserValue(TP_PREF));
+  is(Services.prefs.getStringPref(CAT_PREF), "custom", `${CAT_PREF} has been set to custom`);
 
-  ok(Services.prefs.prefHasUserValue(TP_PREF), "did not reset the TP pref");
+  strictRadioOption.click();
+  await TestUtils.waitForCondition(() => Services.prefs.getStringPref(CAT_PREF) == "strict");
 
-  await extension.unload();
+  // Changing the NCB_PREF should necessarily set CAT_PREF to "custom"
+  Services.prefs.setIntPref(NCB_PREF, 4);
+  await TestUtils.waitForCondition(() => !Services.prefs.prefHasUserValue(NCB_PREF));
+  is(Services.prefs.getStringPref(CAT_PREF), "custom", `${CAT_PREF} has been set to custom`);
 
   gBrowser.removeCurrentTab();
 });
 
 function checkControlState(doc, controls, enabled) {
   for (let selector of controls) {
     for (let control of doc.querySelectorAll(selector)) {
       if (enabled) {
         ok(!control.hasAttribute("disabled"), `${selector} is enabled.`);
       } else {
         is(control.getAttribute("disabled"), "true", `${selector} is disabled.`);
       }
     }
   }
 }
 
-// Checks that the controls for tracking protection are disabled when all TP prefs are off.
+// Checks that the menulists for tracking protection and cookie blocking are disabled when all TP prefs are off.
 add_task(async function testContentBlockingDependentTPControls() {
   SpecialPowers.pushPrefEnv({set: [
     [CB_TP_UI_PREF, true],
     [CB_RT_UI_PREF, true],
     [TP_PREF, false],
     [TP_PBM_PREF, false],
-    [NCB_PREF, Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER],
+    [NCB_PREF, Ci.nsICookieService.BEHAVIOR_ACCEPT],
+    [CAT_PREF, "custom"],
   ]});
 
   let disabledControls = [
     "#trackingProtectionMenu",
+    "#blockCookiesMenu",
   ];
 
   await openPreferencesViaOpenPreferencesAPI("privacy", {leaveOpen: true});
   let doc = gBrowser.contentDocument;
-
   checkControlState(doc, disabledControls, false);
 
   gBrowser.removeCurrentTab();
 });
-
-// Checks that the warnings in the Content Blocking Third-Party Cookies section correctly appear based on
-// the selections in the Cookies and Site Data section.
-add_task(async function testContentBlockingThirdPartyCookiesWarning() {
-  await SpecialPowers.pushPrefEnv({set: [
-    [CB_TP_UI_PREF, true],
-    [CB_RT_UI_PREF, true],
-  ]});
-
-  let expectedDeckIndex = new Map([
-    [Ci.nsICookieService.BEHAVIOR_ACCEPT, 0],
-    [Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN, 0],
-    [Ci.nsICookieService.BEHAVIOR_REJECT, 1],
-    [Ci.nsICookieService.BEHAVIOR_LIMIT_FOREIGN, 2],
-    [Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER, 0],
-  ]);
-
-  await openPreferencesViaOpenPreferencesAPI("privacy", {leaveOpen: true});
-  let doc = gBrowser.contentDocument;
-
-  let deck = doc.getElementById("blockCookiesCBDeck");
-
-  for (let obj of expectedDeckIndex) {
-    Services.prefs.setIntPref(NCB_PREF, obj[0]);
-
-    is(deck.selectedIndex, obj[1], "Correct deck index is being displayed");
-
-    Services.prefs.clearUserPref(NCB_PREF);
-  }
-
-  gBrowser.removeCurrentTab();
-});
-
--- a/browser/components/preferences/in-content/tests/browser_search_subdialogs_within_preferences_site_data.js
+++ b/browser/components/preferences/in-content/tests/browser_search_subdialogs_within_preferences_site_data.js
@@ -25,11 +25,11 @@ add_task(async function() {
 add_task(async function() {
   await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
   await evaluateSearchResults("cache", ["siteDataGroup"]);
   BrowserTestUtils.removeTab(gBrowser.selectedTab);
 });
 
 add_task(async function() {
   await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
-  await evaluateSearchResults("third-party", ["siteDataGroup", "trackingGroup"]);
+  await evaluateSearchResults("third-party", ["trackingGroup"]);
   BrowserTestUtils.removeTab(gBrowser.selectedTab);
 });
--- a/browser/components/preferences/in-content/tests/browser_spotlight.js
+++ b/browser/components/preferences/in-content/tests/browser_spotlight.js
@@ -62,19 +62,10 @@ add_task(async function test_change_cook
   await TestUtils.waitForCondition(() => doc.querySelector(".spotlight"),
     "Wait for the content-blocking section to be spotlighted.");
   is(doc.querySelector(".spotlight").getAttribute("data-subcategory"), "trackingprotection",
     "The tracking-protection section is spotlighted.");
   doc.defaultView.spotlight(null);
   is(doc.querySelector(".spotlight"), null,
     "The spotlighted section is cleared.");
 
-  let changeCookieSettings = doc.getElementById("contentBlockingChangeCookieSettings");
-  changeCookieSettings.doCommand();
-  await TestUtils.waitForCondition(() => doc.querySelector(".spotlight"),
-    "Wait for the content-blocking section to be spotlighted.");
-  is(doc.querySelector(".spotlight").getAttribute("data-subcategory"), "sitedata",
-    "The sitedata section is spotlighted.");
-  is(prefs.selectedPane, "panePrivacy", "Privacy pane is selected by default");
-  is(doc.location.hash, "#privacy", "The subcategory should be removed from the URI");
-
   BrowserTestUtils.removeTab(gBrowser.selectedTab);
 });
--- a/browser/components/preferences/in-content/tests/privacypane_tests_perwindow.js
+++ b/browser/components/preferences/in-content/tests/privacypane_tests_perwindow.js
@@ -48,17 +48,17 @@ function test_dependent_elements(win) {
     win.document.getElementById("rememberForms"),
     win.document.getElementById("deleteOnClose"),
     win.document.getElementById("alwaysClear"),
   ];
   controls.forEach(function(control) {
     ok(control, "the dependent controls should exist");
   });
   let independents = [
-    win.document.getElementById("blockCookies"),
+    win.document.getElementById("contentBlockingBlockCookiesCheckbox"),
   ];
   independents.forEach(function(control) {
     ok(control, "the independent controls should exist");
   });
   let cookieexceptions = win.document.getElementById("cookieExceptions");
   ok(cookieexceptions, "the cookie exceptions button should exist");
   let deleteOnCloseCheckbox = win.document.getElementById("deleteOnClose");
   ok(deleteOnCloseCheckbox, "the delete on close checkbox should exist");
@@ -107,63 +107,62 @@ function test_dependent_elements(win) {
   historymode.value = "custom";
   controlChanged(historymode);
   expect_disabled(false);
   check_independents(false);
 }
 
 function test_dependent_cookie_elements(win) {
   let deleteOnCloseCheckbox = win.document.getElementById("deleteOnClose");
-  let blockCookiesLabel = win.document.getElementById("blockCookiesLabel");
   let blockCookiesMenu = win.document.getElementById("blockCookiesMenu");
 
-  let controls = [blockCookiesLabel, blockCookiesMenu, deleteOnCloseCheckbox];
+  let controls = [blockCookiesMenu, deleteOnCloseCheckbox];
   controls.forEach(function(control) {
     ok(control, "the dependent cookie controls should exist");
   });
-  let blockcookies = win.document.getElementById("blockCookies");
-  ok(blockcookies, "the block cookies checkbox should exist");
+  let blockCookiesCheckbox = win.document.getElementById("contentBlockingBlockCookiesCheckbox");
+  ok(blockCookiesCheckbox, "the block cookies checkbox should exist");
 
   function expect_disabled(disabled, c = controls) {
     c.forEach(function(control) {
       is(control.disabled, disabled,
         control.getAttribute("id") + " should " + (disabled ? "" : "not ") + "be disabled");
     });
   }
 
-  blockcookies.value = "disallow";
-  controlChanged(blockcookies);
+  blockCookiesCheckbox.checked = true;
+  controlChanged(blockCookiesCheckbox);
   expect_disabled(false);
 
-  blockcookies.value = "allow";
-  controlChanged(blockcookies);
-  expect_disabled(true, [blockCookiesLabel, blockCookiesMenu]);
+  blockCookiesCheckbox.checked = false;
+  controlChanged(blockCookiesCheckbox);
+  expect_disabled(true, [blockCookiesMenu]);
   expect_disabled(false, [deleteOnCloseCheckbox]);
 
   blockCookiesMenu.value = "always";
   controlChanged(blockCookiesMenu);
   expect_disabled(true, [deleteOnCloseCheckbox]);
-  expect_disabled(false, [blockCookiesLabel, blockCookiesMenu]);
+  expect_disabled(false, [blockCookiesMenu]);
 
   if (win.contentBlockingCookiesAndSiteDataRejectTrackersEnabled) {
     blockCookiesMenu.value = "trackers";
   } else {
     blockCookiesMenu.value = "unvisited";
   }
   controlChanged(blockCookiesMenu);
   expect_disabled(false);
 
   let historymode = win.document.getElementById("historyMode");
 
   // The History mode setting for "never remember history" should still
   // disable the "keep cookies until..." menu.
   historymode.value = "dontremember";
   controlChanged(historymode);
   expect_disabled(true, [deleteOnCloseCheckbox]);
-  expect_disabled(false, [blockCookiesLabel, blockCookiesMenu]);
+  expect_disabled(false, [blockCookiesMenu]);
 
   historymode.value = "remember";
   controlChanged(historymode);
   expect_disabled(false);
 }
 
 function test_dependent_clearonclose_elements(win) {
   let historymode = win.document.getElementById("historyMode");
--- a/browser/extensions/formautofill/FormAutofillHandler.jsm
+++ b/browser/extensions/formautofill/FormAutofillHandler.jsm
@@ -5,27 +5,34 @@
 /*
  * Defines a handler object to represent forms that autofill can handle.
  */
 
 "use strict";
 
 var EXPORTED_SYMBOLS = ["FormAutofillHandler"];
 
+ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
-
+ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://formautofill/FormAutofill.jsm");
 
 ChromeUtils.defineModuleGetter(this, "FormAutofillUtils",
                                "resource://formautofill/FormAutofillUtils.jsm");
 ChromeUtils.defineModuleGetter(this, "FormAutofillHeuristics",
                                "resource://formautofill/FormAutofillHeuristics.jsm");
 ChromeUtils.defineModuleGetter(this, "FormLikeFactory",
                                "resource://gre/modules/FormLikeFactory.jsm");
 
+XPCOMUtils.defineLazyGetter(this, "reauthPasswordPromptMessage", () => {
+  const brandShortName = FormAutofillUtils.brandBundle.GetStringFromName("brandShortName");
+  return FormAutofillUtils.stringBundle.formatStringFromName(
+    `useCreditCardPasswordPrompt.${AppConstants.platform}`, [brandShortName], 1);
+});
+
 this.log = null;
 FormAutofill.defineLazyLogGetter(this, EXPORTED_SYMBOLS[0]);
 
 const {FIELD_STATES} = FormAutofillUtils;
 
 class FormAutofillSection {
   constructor(fieldDetails, winUtils) {
     this.fieldDetails = fieldDetails;
@@ -862,22 +869,20 @@ class FormAutofillCreditCardSection exte
    * Customize for filling prorifle.
    *
    * @param {Object} profile
    *        A profile for pre-processing before filling values.
    * @returns {boolean} Whether the profile should be filled.
    * @override
    */
   async prepareFillingProfile(profile) {
-    // When Master Password is enabled by users, the decryption process
-    // should prompt Master Password dialog to get the decrypted credit
-    // card number. Otherwise, the number can be decrypted with the default
-    // password.
+    // Prompt the OS login dialog to get the decrypted credit
+    // card number.
     if (profile["cc-number-encrypted"]) {
-      let decrypted = await this._decrypt(profile["cc-number-encrypted"], true);
+      let decrypted = await this._decrypt(profile["cc-number-encrypted"], reauthPasswordPromptMessage);
 
       if (!decrypted) {
         // Early return if the decrypted is empty or undefined
         return false;
       }
 
       profile["cc-number"] = decrypted;
     }
--- a/browser/extensions/formautofill/content/manageDialog.js
+++ b/browser/extensions/formautofill/content/manageDialog.js
@@ -4,29 +4,36 @@
 
 /* exported ManageAddresses, ManageCreditCards */
 
 "use strict";
 
 const EDIT_ADDRESS_URL = "chrome://formautofill/content/editAddress.xhtml";
 const EDIT_CREDIT_CARD_URL = "chrome://formautofill/content/editCreditCard.xhtml";
 
+ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
+ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://formautofill/FormAutofill.jsm");
-ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
 
 ChromeUtils.defineModuleGetter(this, "CreditCard",
                                "resource://gre/modules/CreditCard.jsm");
 ChromeUtils.defineModuleGetter(this, "formAutofillStorage",
                                "resource://formautofill/FormAutofillStorage.jsm");
 ChromeUtils.defineModuleGetter(this, "FormAutofillUtils",
                                "resource://formautofill/FormAutofillUtils.jsm");
 ChromeUtils.defineModuleGetter(this, "OSKeyStore",
                                "resource://formautofill/OSKeyStore.jsm");
 
+XPCOMUtils.defineLazyGetter(this, "reauthPasswordPromptMessage", () => {
+  const brandShortName = FormAutofillUtils.brandBundle.GetStringFromName("brandShortName");
+  return FormAutofillUtils.stringBundle.formatStringFromName(
+    `editCreditCardPasswordPrompt.${AppConstants.platform}`, [brandShortName], 1);
+});
+
 this.log = null;
 FormAutofill.defineLazyLogGetter(this, "manageAddresses");
 
 class ManageRecords {
   constructor(subStorageName, elements) {
     this._storageInitPromise = formAutofillStorage.initialize();
     this._subStorageName = subStorageName;
     this._elements = elements;
@@ -322,17 +329,17 @@ class ManageCreditCards extends ManageRe
 
   /**
    * Open the edit address dialog to create/edit a credit card.
    *
    * @param  {object} creditCard [optional]
    */
   async openEditDialog(creditCard) {
     // Ask for reauth if user is trying to edit an existing credit card.
-    if (!creditCard || await OSKeyStore.ensureLoggedIn(true)) {
+    if (!creditCard || await OSKeyStore.ensureLoggedIn(reauthPasswordPromptMessage)) {
       let decryptedCCNumObj = {};
       if (creditCard && creditCard["cc-number-encrypted"]) {
         try {
           decryptedCCNumObj["cc-number"] = await OSKeyStore.decrypt(creditCard["cc-number-encrypted"]);
         } catch (ex) {
           if (ex.result == Cr.NS_ERROR_ABORT) {
             // User shouldn't be ask to reauth here, but it could happen.
             // Return here and skip opening the dialog.
--- a/browser/extensions/formautofill/locales/en-US/formautofill.properties
+++ b/browser/extensions/formautofill/locales/en-US/formautofill.properties
@@ -194,8 +194,16 @@ cardNetwork.amex = American Express
 cardNetwork.cartebancaire = Carte Bancaire
 cardNetwork.diners = Diners Club
 cardNetwork.discover = Discover
 cardNetwork.jcb = JCB
 cardNetwork.mastercard = MasterCard
 cardNetwork.mir = MIR
 cardNetwork.unionpay = Union Pay
 cardNetwork.visa = Visa
+
+# LOCALIZATION NOTE (editCreditCardPasswordPrompt.*, useCreditCardPasswordPrompt.*): %S is brandShortName.
+editCreditCardPasswordPrompt.win = %S is trying to show credit card information. Confirm access to this Windows account below.
+editCreditCardPasswordPrompt.macosx = %S is trying to show credit card information.
+editCreditCardPasswordPrompt.linux = %S is trying to show credit card information.
+useCreditCardPasswordPrompt.win = %S is trying to use stored credit card information. Confirm access to this Windows account below.
+useCreditCardPasswordPrompt.macosx = %S is trying to use stored credit card information.
+useCreditCardPasswordPrompt.linux = %S is trying to use stored credit card information.
--- a/browser/locales/en-US/browser/preferences/preferences.ftl
+++ b/browser/locales/en-US/browser/preferences/preferences.ftl
@@ -782,19 +782,19 @@ sitedata-block-all-option =
 sitedata-clear =
     .label = Clear Data…
     .accesskey = l
 
 sitedata-settings =
     .label = Manage Data…
     .accesskey = M
 
-sitedata-cookies-exceptions =
-    .label = Exceptions…
-    .accesskey = E
+sitedata-cookies-permissions =
+    .label = Manage Permissions…
+    .accesskey = P
 
 ## Privacy Section - Address Bar
 
 addressbar-header = Address Bar
 
 addressbar-suggest = When using the address bar, suggest
 
 addressbar-locbar-history-option =
@@ -808,63 +808,65 @@ addressbar-locbar-openpage-option =
     .accesskey = O
 
 addressbar-suggestions-settings = Change preferences for search engine suggestions
 
 ## Privacy Section - Content Blocking
 
 content-blocking-header = Content Blocking
 
-content-blocking-desc = Block third-party content, like ads or code, that can slow your browsing and track you around the web. Customize your settings for the best balance of protection and performance.
+content-blocking-description = Block third-party content that tracks you around the web. Control how much of your online activity gets stored and shared between websites.
 
 content-blocking-learn-more = Learn more
-content-blocking-restore-defaults =
-  .label = Restore Defaults
-  .accesskey = R
+
+content-blocking-setting-standard =
+  .label = Standard
+  .accesskey = d
+content-blocking-setting-strict =
+  .label = Strict
+  .accesskey = r
+content-blocking-setting-custom =
+  .label = Custom
+  .accesskey = C
 
-content-blocking-category-label = Choose what to block
+content-blocking-standard-desc = Balanced for protection and performance. Allows some trackers so websites function properly.
+content-blocking-strict-desc = Blocks all trackers { -brand-short-name } detects. May cause some sites to break.
+content-blocking-custom-desc = Choose what to block.
+
+content-blocking-private-trackers = Known trackers only in Private Windows
+content-blocking-third-party-cookies = Third-party tracking cookies
+content-blocking-all-windows-trackers = Known trackers in all windows
+content-blocking-all-third-party-cookies = All third-party cookies
+
+content-blocking-warning-title = Heads up!
+content-blocking-warning-desc = Blocking cookies and trackers can cause some websites to break. It’s easy to disable blocking for sites you trust.
+content-blocking-learn-how = Learn how
 
 content-blocking-tracking-protection-trackers-label =
   .label = Trackers
   .accesskey = T
 content-blocking-tracking-protection-all-detected-trackers-label =
   .label = All Detected Trackers
   .accesskey = T
-content-blocking-tracking-protection-new-description = Block all known trackers. (May prevent some pages from loading.)
 content-blocking-tracking-protection-option-always =
   .label = Always
   .accesskey = A
 content-blocking-tracking-protection-option-private =
   .label = Only in private windows
   .accesskey = p
 content-blocking-tracking-protection-change-block-list = Change block list
 
-content-blocking-third-party-cookies-label =
-  .label = Third-Party Cookies
+content-blocking-cookies-label =
+  .label = Cookies
   .accesskey = C
-content-blocking-reject-trackers-description = Block all third-party cookies or just those set by trackers.
-# This is a warning message shown next to a yellow warning icon when the Third-Party Cookies subsection
-# of the Content Blocking UI in Preferences has been disabled due to the either the "All cookies" option
-# or the "Cookies from unvisited websites" option being selected in the Cookies and Site Data section of
-# the UI.
-content-blocking-reject-trackers-warning-your-settings-prevent-changes = Your settings in Cookies and Site Data are preventing changes to Third-Party Cookies settings.
-content-blocking-change-cookie-settings =
-  .label = Change Cookie Settings
-  .accesskey = S
-content-blocking-reject-trackers-block-trackers-option-recommended =
-  .label = Trackers (recommended)
-  .accesskey = k
-content-blocking-reject-trackers-all-third-parties-option =
-  .label = All third-party cookies (may cause websites to break)
-  .accesskey = A
 
 ## Privacy Section - Tracking
 
-tracking-exceptions =
-    .label = Exceptions…
+tracking-manage-exceptions =
+    .label = Manage Exceptions…
     .accesskey = x
 
 ## Privacy Section - Permissions
 
 permissions-header = Permissions
 
 permissions-location = Location
 permissions-location-settings =
--- a/browser/themes/shared/incontentprefs/privacy.css
+++ b/browser/themes/shared/incontentprefs/privacy.css
@@ -13,16 +13,33 @@
 }
 
 .content-blocking-checkbox .checkbox-icon {
   margin-inline-end: 8px;
   margin-inline-start: 4px;
   width: 16px;
 }
 
+#contentBlockingTrackingProtectionCheckbox > .checkbox-label-box {
+  list-style-image: url("chrome://browser/skin/controlcenter/trackers.svg");
+}
+
+#contentBlockingTrackingProtectionCheckbox[checked] > .checkbox-label-box {
+  list-style-image: url("chrome://browser/skin/controlcenter/trackers-disabled.svg");
+}
+
+#contentBlockingBlockCookiesCheckbox > .checkbox-label-box {
+  list-style-image: url("chrome://browser/skin/controlcenter/3rdpartycookies.svg");
+}
+
+#contentBlockingBlockCookiesCheckbox[checked] > .checkbox-label-box {
+  list-style-image: url("chrome://browser/skin/controlcenter/3rdpartycookies-disabled.svg");
+}
+
+
 .content-blocking-icon,
 .permission-icon {
   -moz-context-properties: fill;
   fill: currentColor;
 }
 
 .content-blocking-icon[disabled] {
   fill: GrayText;
@@ -45,54 +62,164 @@
 }
 
 .midi-icon {
   list-style-image: url(chrome://browser/skin/notification-icons/midi.svg);
 }
 
 /* Content Blocking */
 
-#contentBlockingLearnMore {
-  margin-top: 4px !important;
+/* Override styling that sets descriptions as grey */
+#trackingGroup description.indent,
+#trackingGroup .indent > description {
+  color: #000;
+}
+
+[data-subcategory="trackingprotection"] {
+  margin-top: 10px;
 }
 
 #contentBlockingCategories {
   margin-top: 16px;
 }
 
-.content-blocking-category {
-  margin: 16px 0;
+#trackingProtectionShield {
+  background-image: url("chrome://browser/skin/controlcenter/tracking-protection.svg");
+  -moz-context-properties: fill;
+  fill: #737373;
+  width: 64px;
+  height: 64px;
+  background-repeat: no-repeat;
+  background-size: contain;
+  margin-inline-end: 10px;
 }
 
-.content-blocking-category-labels {
-  padding-inline-start: 4px;
-  margin-inline-start: 25px !important;
+.content-blocking-category {
+  border-radius: 4px;
+  margin: 3px 0;
+  padding: 9px;
+  border: 1px solid #D7D7DB;
+  background-color: rgba(215, 215, 219, 0.2);
+}
+
+.content-blocking-category.disabled {
+  opacity: 0.5;
+}
+
+.content-blocking-category.disabled .radio-check {
+  opacity: 1;
+}
+
+.content-blocking-warning > .indent,
+.content-blocking-category > .indent {
+  margin-inline-end: 28px;
 }
 
-#trackingProtectionMenu {
-  margin-top: 0.75em;
+.arrowhead {
+  -moz-appearance: none;
+  border: none;
+  border-radius: 2px;
+  min-height: 20px;
+  min-width: 20px;
+  max-height: 20px;
+  max-width: 20px;
+  background-color: transparent;
+  background-image: url("chrome://global/skin/icons/arrow-dropdown-12.svg");
+  background-repeat: no-repeat;
+  background-position: center;
 }
 
-#blockCookiesCBDeck[selectedIndex]:not([selectedIndex="0"]) {
-  max-width: 444px;
+.arrowhead:not([disabled]):hover {
+  cursor: pointer;
+  background-color: var(--grey-90-a10);
+}
+
+.arrowhead:not([disabled]):hover:active {
+  background-color: var(--grey-90-a20);
 }
 
-#blockCookiesCBDeck:not([selectedIndex]) > .warning-description,
-#blockCookiesCBDeck[selectedIndex="0"] > .warning-description {
+.arrowhead.up {
+  background-image: url("chrome://global/skin/icons/arrow-up-12.svg");
+}
+
+.arrowhead > .button-box {
+  padding: 0 !important;
+}
+
+.content-blocking-category.expanded:not(.selected) .content-blocking-warning {
+  background-color: var(--grey-90-a10);
+}
+
+.content-blocking-category.selected .arrowhead {
   display: none;
 }
 
-#blockCookiesCBDeck > .warning-description {
-  margin-bottom: 0.75em !important;
+.content-blocking-category.selected {
+	border: 1px solid #45A1FF;
+  background-color: rgba(69, 161, 255, 0.2);
+}
+
+.content-blocking-warning-title,
+.content-blocking-category .radio-label-box {
+  font-weight: bold;
+}
+
+.content-blocking-extra-information {
+  visibility: collapse;
+}
+
+.extra-information-label {
+  margin-top: 18px;
+}
+
+.extra-information-label:last-child {
+  margin-bottom: 18px;
+}
+
+.content-blocking-category.expanded .content-blocking-extra-information,
+.content-blocking-category.selected .content-blocking-extra-information {
+  visibility: visible;
+}
+
+.content-blocking-extra-information > .custom-option {
+  margin: 10px 0;
+}
+
+.content-blocking-warning {
+  background-color: rgba(69, 161, 255, 0.2);
+  border-radius: 4px;
+  padding: 10px 0;
+  margin: 10px 0;
+}
+
+.content-blocking-trackers-image {
+  list-style-image: url("chrome://browser/skin/controlcenter/trackers-disabled.svg");
+  margin-inline-end: 5px;
+}
+
+.content-blocking-cookies-image {
+  list-style-image: url("chrome://browser/skin/controlcenter/3rdpartycookies-disabled.svg");
+  margin-inline-end: 5px;
+}
+
+.content-blocking-warning-image {
+  list-style-image: url("chrome://global/skin/icons/warning.svg");
+  -moz-context-properties: fill;
+  fill: currentColor;
+  margin-inline-end: 8px;
+  margin-inline-start: 4px;
+}
+
+#blockCookiesMenu,
+#trackingProtectionMenu {
+  margin: 0;
 }
 
 #changeBlockListLink {
-  font-size: 90%;
-  /* In order to override the margins set in preferences.inc.css, we have to use !important. */
-  margin-top: 1em !important;
+  margin-inline-start: 56px;
 }
 
 .content-blocking-category-description {
   font-size: 90%;
   opacity: 0.6;
 }
 
 .warning-description {
--- a/build/debian-packages/valgrind-wheezy.diff
+++ b/build/debian-packages/valgrind-wheezy.diff
@@ -1,75 +1,64 @@
-diff -Nru valgrind-3.13.0/debian/patches/05_fix-callgrind_control.patch valgrind-3.14.0.git20180806/debian/patches/05_fix-callgrind_control.patch
---- valgrind-3.13.0/debian/patches/05_fix-callgrind_control.patch	2017-07-24 08:41:05.000000000 +0900
-+++ valgrind-3.14.0.git20180806/debian/patches/05_fix-callgrind_control.patch	2018-08-10 16:00:19.142494503 +0900
-@@ -7,9 +7,9 @@
+diff -Nru valgrind-3.14.0/debian/changelog valgrind-3.14.0/debian/changelog
+--- valgrind-3.14.0/debian/changelog	2018-11-15 09:21:43.000000000 +0900
++++ valgrind-3.14.0/debian/changelog	2018-11-15 11:45:25.000000000 +0900
+@@ -1,3 +1,14 @@
++valgrind (1:3.14.0-1.deb7moz1) wheezy; urgency=medium
++
++  * Mozilla backport for wheezy.
++  * debian/control, debian/compat: Drop debhelper compat back to 7, which
++    requires adding back an explicit dependency on dh-autoreconf.
++  * debian/rules: Debhelper only defaulted to --parallel in compat >= 10, so
++    add --parallel back.
++  * debian/valgrind-mpi.install: Use non-multiarch path.
++
++ -- Mike Hommey <glandium@mozilla.com>  Thu, 15 Nov 2018 11:45:25 +0900
++
+ valgrind (1:3.14.0-1) unstable; urgency=medium
  
- --- a/callgrind/callgrind_control.in
- +++ b/callgrind/callgrind_control.in
--@@ -29,7 +29,7 @@
-+@@ -33,7 +33,7 @@
-    @pids = ();
--   open LIST, "vgdb $vgdbPrefixOption -l|";
-+   open LIST, $vgdb_exe . " $vgdbPrefixOption -l|";
-    while(<LIST>) {
- -      if (/^use --pid=(\d+) for \S*?valgrind\s+(.*?)\s*$/) {
- +      if (/^use --pid=(\d+) for \S*?valgrind\.bin\s+(.*?)\s*$/) {
-diff -Nru valgrind-3.13.0/debian/patches/07_fix-spelling-in-binary.patch valgrind-3.14.0.git20180806/debian/patches/07_fix-spelling-in-binary.patch
---- valgrind-3.13.0/debian/patches/07_fix-spelling-in-binary.patch	2017-07-24 08:41:05.000000000 +0900
-+++ valgrind-3.14.0.git20180806/debian/patches/07_fix-spelling-in-binary.patch	2018-08-10 16:01:46.110793537 +0900
-@@ -16,7 +16,7 @@
-            && VKI_S_ISREG(stat_buf.mode)
- --- a/coregrind/m_gdbserver/server.c
- +++ b/coregrind/m_gdbserver/server.c
--@@ -254,7 +254,7 @@
-+@@ -256,7 +256,7 @@
-  "    Valgrind internal host status/memory\n"
-  "  v.translate <addr> [<traceflags>]  : debug translation of <addr> with <traceflags>\n"
-  "    (default traceflags 0b00100000 : show after instrumentation)\n"
-@@ -27,7 +27,7 @@
-     case  1: /* v.set */
- --- a/VEX/priv/ir_defs.c
- +++ b/VEX/priv/ir_defs.c
--@@ -4643,7 +4643,7 @@
-+@@ -4698,7 +4698,7 @@
-     if (bb->stmts_used < 0 || bb->stmts_size < 8
-         || bb->stmts_used > bb->stmts_size)
-        /* this BB is so strange we can't even print it */
-@@ -60,7 +60,7 @@
-                  " N-plicated elts\n");
- --- a/coregrind/m_scheduler/scheduler.c
- +++ b/coregrind/m_scheduler/scheduler.c
--@@ -2147,7 +2147,7 @@
-+@@ -2208,7 +2208,7 @@
-        "to recompile such code, using the header files from this version of\n"
-        "Valgrind, and not any previous version.\n"
-        "\n"
-@@ -71,7 +71,7 @@
-        "   http://www.valgrind.org/support/bug_reports.html\n"
- --- a/coregrind/m_xtree.c
- +++ b/coregrind/m_xtree.c
--@@ -946,7 +946,7 @@
-+@@ -961,7 +961,7 @@
-        FP("n%u: %llu %s\n", n_groups, top_total, header->top_node_desc);
-  
-        /* Output depth 0 groups. */
-diff -Nru valgrind-3.13.0/debian/patches/09_fix-armhf-detect.patch valgrind-3.14.0.git20180806/debian/patches/09_fix-armhf-detect.patch
---- valgrind-3.13.0/debian/patches/09_fix-armhf-detect.patch	2017-07-24 08:41:05.000000000 +0900
-+++ valgrind-3.14.0.git20180806/debian/patches/09_fix-armhf-detect.patch	2018-08-10 16:02:01.462846319 +0900
-@@ -6,7 +6,7 @@
+   * New upstream release (Closes: #913208)
+diff -Nru valgrind-3.14.0/debian/compat valgrind-3.14.0/debian/compat
+--- valgrind-3.14.0/debian/compat	2018-11-15 09:21:43.000000000 +0900
++++ valgrind-3.14.0/debian/compat	2018-11-15 11:45:25.000000000 +0900
+@@ -1 +1 @@
+-11
++7
+diff -Nru valgrind-3.14.0/debian/control valgrind-3.14.0/debian/control
+--- valgrind-3.14.0/debian/control	2018-11-15 09:21:43.000000000 +0900
++++ valgrind-3.14.0/debian/control	2018-11-15 11:45:25.000000000 +0900
+@@ -2,7 +2,8 @@
+ Section: devel
+ Priority: optional
+ Maintainer: Alessandro Ghedini <ghedo@debian.org>
+-Build-Depends: debhelper (>= 11),
++Build-Depends: debhelper (>= 7.0.50~),
++ dh-autoreconf,
+  gdb,
+  gcc-multilib [amd64],
+  libc6-dev-i386 [amd64],
+diff -Nru valgrind-3.14.0/debian/rules valgrind-3.14.0/debian/rules
+--- valgrind-3.14.0/debian/rules	2018-11-15 09:21:43.000000000 +0900
++++ valgrind-3.14.0/debian/rules	2018-11-15 11:45:25.000000000 +0900
+@@ -11,7 +11,7 @@
+ LDFLAGS  = $(shell dpkg-buildflags --get LDFLAGS)
  
- --- a/configure.ac
- +++ b/configure.ac
--@@ -234,7 +234,7 @@
-+@@ -252,7 +252,7 @@
-          ARCH_MAX="s390x"
-          ;;
-  
-diff -Nru valgrind-3.13.0/debian/patches/series valgrind-3.14.0.git20180806/debian/patches/series
---- valgrind-3.13.0/debian/patches/series	2017-07-24 08:41:05.000000000 +0900
-+++ valgrind-3.14.0.git20180806/debian/patches/series	2018-08-10 15:59:43.434371705 +0900
-@@ -2,5 +2,4 @@
- 04_workaround-SIGSEGV-on-PPC.patch
- 05_fix-callgrind_control.patch
- 07_fix-spelling-in-binary.patch
--08_fix-spelling-in-manpage.patch
- 09_fix-armhf-detect.patch
+ %:
+-	dh $@ --with=autoreconf
++	dh $@ --parallel --with=autoreconf
+ 
+ override_dh_auto_configure:
+ 	dh_auto_configure -- --enable-tls CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)"
+@@ -20,7 +20,7 @@
+ 	: # do nothing for now
+ 
+ override_dh_auto_build:
+-	dh_auto_build
++	dh_auto_build --parallel
+ 	$(MAKE) -C docs FAQ.txt
+ 	$(MAKE) -C docs html-docs
+ 	$(MAKE) -C docs man-pages
+diff -Nru valgrind-3.14.0/debian/valgrind-mpi.install valgrind-3.14.0/debian/valgrind-mpi.install
+--- valgrind-3.14.0/debian/valgrind-mpi.install	2018-11-15 09:21:43.000000000 +0900
++++ valgrind-3.14.0/debian/valgrind-mpi.install	2018-11-15 11:45:25.000000000 +0900
+@@ -1 +1 @@
+-usr/lib/*/valgrind/libmpiwrap*
++usr/lib/valgrind/libmpiwrap*
--- a/devtools/client/debugger/new/README.mozilla
+++ b/devtools/client/debugger/new/README.mozilla
@@ -1,13 +1,13 @@
 This is the debugger.html project output.
 See https://github.com/devtools-html/debugger.html
 
-Version 102
+Version 103
 
-Comparison: https://github.com/devtools-html/debugger.html/compare/release-101...release-102
+Comparison: https://github.com/devtools-html/debugger.html/compare/release-102...release-103
 
 Packages:
 - babel-plugin-transform-es2015-modules-commonjs @6.26.2
 - babel-preset-react @6.24.1
 - react @16.4.1
 - react-dom @16.4.1
 - webpack @3.12.0
--- a/devtools/client/debugger/new/dist/parser-worker.js
+++ b/devtools/client/debugger/new/dist/parser-worker.js
@@ -729,207 +729,16 @@ var overArg = __webpack_require__(13);
 /** Built-in value references. */
 var getPrototype = overArg(Object.getPrototypeOf, Object);
 
 module.exports = getPrototype;
 
 
 /***/ }),
 
-/***/ 120:
-/***/ (function(module, exports) {
-
-// shim for using process in browser
-var process = module.exports = {};
-
-// cached from whatever global is present so that test runners that stub it
-// don't break things.  But we need to wrap it in a try catch in case it is
-// wrapped in strict mode code which doesn't define any globals.  It's inside a
-// function because try/catches deoptimize in certain engines.
-
-var cachedSetTimeout;
-var cachedClearTimeout;
-
-function defaultSetTimout() {
-    throw new Error('setTimeout has not been defined');
-}
-function defaultClearTimeout () {
-    throw new Error('clearTimeout has not been defined');
-}
-(function () {
-    try {
-        if (typeof setTimeout === 'function') {
-            cachedSetTimeout = setTimeout;
-        } else {
-            cachedSetTimeout = defaultSetTimout;
-        }
-    } catch (e) {
-        cachedSetTimeout = defaultSetTimout;
-    }
-    try {
-        if (typeof clearTimeout === 'function') {
-            cachedClearTimeout = clearTimeout;
-        } else {
-            cachedClearTimeout = defaultClearTimeout;
-        }
-    } catch (e) {
-        cachedClearTimeout = defaultClearTimeout;
-    }
-} ())
-function runTimeout(fun) {
-    if (cachedSetTimeout === setTimeout) {
-        //normal enviroments in sane situations
-        return setTimeout(fun, 0);
-    }
-    // if setTimeout wasn't available but was latter defined
-    if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {
-        cachedSetTimeout = setTimeout;
-        return setTimeout(fun, 0);
-    }
-    try {
-        // when when somebody has screwed with setTimeout but no I.E. maddness
-        return cachedSetTimeout(fun, 0);
-    } catch(e){
-        try {
-            // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
-            return cachedSetTimeout.call(null, fun, 0);
-        } catch(e){
-            // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error
-            return cachedSetTimeout.call(this, fun, 0);
-        }
-    }
-
-
-}
-function runClearTimeout(marker) {
-    if (cachedClearTimeout === clearTimeout) {
-        //normal enviroments in sane situations
-        return clearTimeout(marker);
-    }
-    // if clearTimeout wasn't available but was latter defined
-    if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {
-        cachedClearTimeout = clearTimeout;
-        return clearTimeout(marker);
-    }
-    try {
-        // when when somebody has screwed with setTimeout but no I.E. maddness
-        return cachedClearTimeout(marker);
-    } catch (e){
-        try {
-            // When we are in I.E. but the script has been evaled so I.E. doesn't  trust the global object when called normally
-            return cachedClearTimeout.call(null, marker);
-        } catch (e){
-            // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.
-            // Some versions of I.E. have different rules for clearTimeout vs setTimeout
-            return cachedClearTimeout.call(this, marker);
-        }
-    }
-
-
-
-}
-var queue = [];
-var draining = false;
-var currentQueue;
-var queueIndex = -1;
-
-function cleanUpNextTick() {
-    if (!draining || !currentQueue) {
-        return;
-    }
-    draining = false;
-    if (currentQueue.length) {
-        queue = currentQueue.concat(queue);
-    } else {
-        queueIndex = -1;
-    }
-    if (queue.length) {
-        drainQueue();
-    }
-}
-
-function drainQueue() {
-    if (draining) {
-        return;
-    }
-    var timeout = runTimeout(cleanUpNextTick);
-    draining = true;
-
-    var len = queue.length;
-    while(len) {
-        currentQueue = queue;
-        queue = [];
-        while (++queueIndex < len) {
-            if (currentQueue) {
-                currentQueue[queueIndex].run();
-            }
-        }
-        queueIndex = -1;
-        len = queue.length;
-    }
-    currentQueue = null;
-    draining = false;
-    runClearTimeout(timeout);
-}
-
-process.nextTick = function (fun) {
-    var args = new Array(arguments.length - 1);
-    if (arguments.length > 1) {
-        for (var i = 1; i < arguments.length; i++) {
-            args[i - 1] = arguments[i];
-        }
-    }
-    queue.push(new Item(fun, args));
-    if (queue.length === 1 && !draining) {
-        runTimeout(drainQueue);
-    }
-};
-
-// v8 likes predictible objects
-function Item(fun, array) {
-    this.fun = fun;
-    this.array = array;
-}
-Item.prototype.run = function () {
-    this.fun.apply(null, this.array);
-};
-process.title = 'browser';
-process.browser = true;
-process.env = {};
-process.argv = [];
-process.version = ''; // empty string to avoid regexp issues
-process.versions = {};
-
-function noop() {}
-
-process.on = noop;
-process.addListener = noop;
-process.once = noop;
-process.off = noop;
-process.removeListener = noop;
-process.removeAllListeners = noop;
-process.emit = noop;
-process.prependListener = noop;
-process.prependOnceListener = noop;
-
-process.listeners = function (name) { return [] }
-
-process.binding = function (name) {
-    throw new Error('process.binding is not supported');
-};
-
-process.cwd = function () { return '/' };
-process.chdir = function (dir) {
-    throw new Error('process.chdir is not supported');
-};
-process.umask = function() { return 0; };
-
-
-/***/ }),
-
 /***/ 1272:
 /***/ (function(module, exports, __webpack_require__) {
 
 module.exports = __webpack_require__(1618);
 
 
 /***/ }),
 
@@ -965,16 +774,17 @@ Object.defineProperty(exports, "__esModu
   value: true
 });
 
 var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; /* 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/>. */
 
 exports.parse = parse;
+exports.parseConsoleScript = parseConsoleScript;
 exports.parseScript = parseScript;
 exports.getAst = getAst;
 exports.clearASTs = clearASTs;
 exports.traverseAst = traverseAst;
 exports.hasNode = hasNode;
 
 var _parseScriptTags = __webpack_require__(1023);
 
@@ -1065,16 +875,24 @@ function parseVueScript(code) {
       ast.program.sourceType = "module";
     }
   } else {
     ast = parse(code, sourceOptions.original);
   }
   return ast;
 }
 
+function parseConsoleScript(text, opts) {
+  return _parse(text, _extends({
+    plugins: ["objectRestSpread"]
+  }, opts, {
+    allowAwaitOutsideFunction: true
+  }));
+}
+
 function parseScript(text, opts) {
   return _parse(text, opts);
 }
 
 function getAst(sourceId) {
   if (ASTs.has(sourceId)) {
     return ASTs.get(sourceId);
   }
@@ -16035,93 +15853,16 @@ function BigIntLiteral(node) {
     return;
   }
 
   this.token(node.value);
 }
 
 /***/ }),
 
-/***/ 2348:
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-Object.defineProperty(exports, "__esModule", {
-  value: true
-});
-exports.merge = merge;
-exports.validate = validate;
-exports.normalizeReplacements = normalizeReplacements;
-
-function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
-
-function merge(a, b) {
-  const {
-    placeholderWhitelist = a.placeholderWhitelist,
-    placeholderPattern = a.placeholderPattern,
-    preserveComments = a.preserveComments
-  } = b;
-  return {
-    parser: Object.assign({}, a.parser, b.parser),
-    placeholderWhitelist,
-    placeholderPattern,
-    preserveComments
-  };
-}
-
-function validate(opts) {
-  if (opts != null && typeof opts !== "object") {
-    throw new Error("Unknown template options.");
-  }
-
-  const _ref = opts || {},
-        {
-    placeholderWhitelist,
-    placeholderPattern,
-    preserveComments
-  } = _ref,
-        parser = _objectWithoutProperties(_ref, ["placeholderWhitelist", "placeholderPattern", "preserveComments"]);
-
-  if (placeholderWhitelist != null && !(placeholderWhitelist instanceof Set)) {
-    throw new Error("'.placeholderWhitelist' must be a Set, null, or undefined");
-  }
-
-  if (placeholderPattern != null && !(placeholderPattern instanceof RegExp) && placeholderPattern !== false) {
-    throw new Error("'.placeholderPattern' must be a RegExp, false, null, or undefined");
-  }
-
-  if (preserveComments != null && typeof preserveComments !== "boolean") {
-    throw new Error("'.preserveComments' must be a boolean, null, or undefined");
-  }
-
-  return {
-    parser,
-    placeholderWhitelist: placeholderWhitelist || undefined,
-    placeholderPattern: placeholderPattern == null ? undefined : placeholderPattern,
-    preserveComments: preserveComments == null ? false : preserveComments
-  };
-}
-
-function normalizeReplacements(replacements) {
-  if (Array.isArray(replacements)) {
-    return replacements.reduce((acc, replacement, i) => {
-      acc["$" + i] = replacement;
-      return acc;
-    }, {});
-  } else if (typeof replacements === "object" || replacements == null) {
-    return replacements || undefined;
-  }
-
-  throw new Error("Template replacements must be an array, object, null, or undefined");
-}
-
-/***/ }),
-
 /***/ 2353:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
@@ -16457,1196 +16198,16 @@ function ImportNamespaceSpecifier(node) 
   this.space();
   this.word("as");
   this.space();
   this.print(node.local, node);
 }
 
 /***/ }),
 
-/***/ 2355:
-/***/ (function(module, exports, __webpack_require__) {
-
-/* MIT license */
-var cssKeywords = __webpack_require__(2389);
-
-// NOTE: conversions should only return primitive values (i.e. arrays, or
-//       values that give correct `typeof` results).
-//       do not use box values types (i.e. Number(), String(), etc.)
-
-var reverseKeywords = {};
-for (var key in cssKeywords) {
-	if (cssKeywords.hasOwnProperty(key)) {
-		reverseKeywords[cssKeywords[key]] = key;
-	}
-}
-
-var convert = module.exports = {
-	rgb: {channels: 3, labels: 'rgb'},
-	hsl: {channels: 3, labels: 'hsl'},
-	hsv: {channels: 3, labels: 'hsv'},
-	hwb: {channels: 3, labels: 'hwb'},
-	cmyk: {channels: 4, labels: 'cmyk'},
-	xyz: {channels: 3, labels: 'xyz'},
-	lab: {channels: 3, labels: 'lab'},
-	lch: {channels: 3, labels: 'lch'},
-	hex: {channels: 1, labels: ['hex']},
-	keyword: {channels: 1, labels: ['keyword']},
-	ansi16: {channels: 1, labels: ['ansi16']},
-	ansi256: {channels: 1, labels: ['ansi256']},
-	hcg: {channels: 3, labels: ['h', 'c', 'g']},
-	apple: {channels: 3, labels: ['r16', 'g16', 'b16']},
-	gray: {channels: 1, labels: ['gray']}
-};
-
-// hide .channels and .labels properties
-for (var model in convert) {
-	if (convert.hasOwnProperty(model)) {
-		if (!('channels' in convert[model])) {
-			throw new Error('missing channels property: ' + model);
-		}
-
-		if (!('labels' in convert[model])) {
-			throw new Error('missing channel labels property: ' + model);
-		}
-
-		if (convert[model].labels.length !== convert[model].channels) {
-			throw new Error('channel and label counts mismatch: ' + model);
-		}
-
-		var channels = convert[model].channels;
-		var labels = convert[model].labels;
-		delete convert[model].channels;
-		delete convert[model].labels;
-		Object.defineProperty(convert[model], 'channels', {value: channels});
-		Object.defineProperty(convert[model], 'labels', {value: labels});
-	}
-}
-
-convert.rgb.hsl = function (rgb) {
-	var r = rgb[0] / 255;
-	var g = rgb[1] / 255;
-	var b = rgb[2] / 255;
-	var min = Math.min(r, g, b);
-	var max = Math.max(r, g, b);
-	var delta = max - min;
-	var h;
-	var s;
-	var l;
-
-	if (max === min) {
-		h = 0;
-	} else if (r === max) {
-		h = (g - b) / delta;
-	} else if (g === max) {
-		h = 2 + (b - r) / delta;
-	} else if (b === max) {
-		h = 4 + (r - g) / delta;
-	}
-
-	h = Math.min(h * 60, 360);
-
-	if (h < 0) {
-		h += 360;
-	}
-
-	l = (min + max) / 2;
-
-	if (max === min) {
-		s = 0;
-	} else if (l <= 0.5) {
-		s = delta / (max + min);
-	} else {
-		s = delta / (2 - max - min);
-	}
-
-	return [h, s * 100, l * 100];
-};
-
-convert.rgb.hsv = function (rgb) {
-	var rdif;
-	var gdif;
-	var bdif;
-	var h;
-	var s;
-
-	var r = rgb[0] / 255;
-	var g = rgb[1] / 255;
-	var b = rgb[2] / 255;
-	var v = Math.max(r, g, b);
-	var diff = v - Math.min(r, g, b);
-	var diffc = function (c) {
-		return (v - c) / 6 / diff + 1 / 2;
-	};
-
-	if (diff === 0) {
-		h = s = 0;
-	} else {
-		s = diff / v;
-		rdif = diffc(r);
-		gdif = diffc(g);
-		bdif = diffc(b);
-
-		if (r === v) {
-			h = bdif - gdif;
-		} else if (g === v) {
-			h = (1 / 3) + rdif - bdif;
-		} else if (b === v) {
-			h = (2 / 3) + gdif - rdif;
-		}
-		if (h < 0) {
-			h += 1;
-		} else if (h > 1) {
-			h -= 1;
-		}
-	}
-
-	return [
-		h * 360,
-		s * 100,
-		v * 100
-	];
-};
-
-convert.rgb.hwb = function (rgb) {
-	var r = rgb[0];
-	var g = rgb[1];
-	var b = rgb[2];
-	var h = convert.rgb.hsl(rgb)[0];
-	var w = 1 / 255 * Math.min(r, Math.min(g, b));
-
-	b = 1 - 1 / 255 * Math.max(r, Math.max(g, b));
-
-	return [h, w * 100, b * 100];
-};
-
-convert.rgb.cmyk = function (rgb) {
-	var r = rgb[0] / 255;
-	var g = rgb[1] / 255;
-	var b = rgb[2] / 255;
-	var c;
-	var m;
-	var y;
-	var k;
-
-	k = Math.min(1 - r, 1 - g, 1 - b);
-	c = (1 - r - k) / (1 - k) || 0;
-	m = (1 - g - k) / (1 - k) || 0;
-	y = (1 - b - k) / (1 - k) || 0;
-
-	return [c * 100, m * 100, y * 100, k * 100];
-};
-
-/**
- * See https://en.m.wikipedia.org/wiki/Euclidean_distance#Squared_Euclidean_distance
- * */
-function comparativeDistance(x, y) {
-	return (
-		Math.pow(x[0] - y[0], 2) +
-		Math.pow(x[1] - y[1], 2) +
-		Math.pow(x[2] - y[2], 2)
-	);
-}
-
-convert.rgb.keyword = function (rgb) {
-	var reversed = reverseKeywords[rgb];
-	if (reversed) {
-		return reversed;
-	}
-
-	var currentClosestDistance = Infinity;
-	var currentClosestKeyword;
-
-	for (var keyword in cssKeywords) {
-		if (cssKeywords.hasOwnProperty(keyword)) {
-			var value = cssKeywords[keyword];
-
-			// Compute comparative distance
-			var distance = comparativeDistance(rgb, value);
-
-			// Check if its less, if so set as closest
-			if (distance < currentClosestDistance) {
-				currentClosestDistance = distance;
-				currentClosestKeyword = keyword;
-			}
-		}
-	}
-
-	return currentClosestKeyword;
-};
-
-convert.keyword.rgb = function (keyword) {
-	return cssKeywords[keyword];
-};
-
-convert.rgb.xyz = function (rgb) {
-	var r = rgb[0] / 255;
-	var g = rgb[1] / 255;
-	var b = rgb[2] / 255;
-
-	// assume sRGB
-	r = r > 0.04045 ? Math.pow(((r + 0.055) / 1.055), 2.4) : (r / 12.92);
-	g = g > 0.04045 ? Math.pow(((g + 0.055) / 1.055), 2.4) : (g / 12.92);
-	b = b > 0.04045 ? Math.pow(((b + 0.055) / 1.055), 2.4) : (b / 12.92);
-
-	var x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805);
-	var y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722);
-	var z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505);
-
-	return [x * 100, y * 100, z * 100];
-};
-
-convert.rgb.lab = function (rgb) {
-	var xyz = convert.rgb.xyz(rgb);
-	var x = xyz[0];
-	var y = xyz[1];
-	var z = xyz[2];
-	var l;
-	var a;
-	var b;
-
-	x /= 95.047;
-	y /= 100;
-	z /= 108.883;
-
-	x = x > 0.008856 ? Math.pow(x, 1 / 3) : (7.787 * x) + (16 / 116);
-	y = y > 0.008856 ? Math.pow(y, 1 / 3) : (7.787 * y) + (16 / 116);
-	z = z > 0.008856 ? Math.pow(z, 1 / 3) : (7.787 * z) + (16 / 116);
-
-	l = (116 * y) - 16;
-	a = 500 * (x - y);
-	b = 200 * (y - z);
-
-	return [l, a, b];
-};
-
-convert.hsl.rgb = function (hsl) {
-	var h = hsl[0] / 360;
-	var s = hsl[1] / 100;
-	var l = hsl[2] / 100;
-	var t1;
-	var t2;
-	var t3;
-	var rgb;
-	var val;
-
-	if (s === 0) {
-		val = l * 255;
-		return [val, val, val];
-	}
-
-	if (l < 0.5) {
-		t2 = l * (1 + s);
-	} else {
-		t2 = l + s - l * s;
-	}
-
-	t1 = 2 * l - t2;
-
-	rgb = [0, 0, 0];
-	for (var i = 0; i < 3; i++) {
-		t3 = h + 1 / 3 * -(i - 1);
-		if (t3 < 0) {
-			t3++;
-		}
-		if (t3 > 1) {
-			t3--;
-		}
-
-		if (6 * t3 < 1) {
-			val = t1 + (t2 - t1) * 6 * t3;
-		} else if (2 * t3 < 1) {
-			val = t2;
-		} else if (3 * t3 < 2) {
-			val = t1 + (t2 - t1) * (2 / 3 - t3) * 6;
-		} else {
-			val = t1;
-		}
-
-		rgb[i] = val * 255;
-	}
-
-	return rgb;
-};
-
-convert.hsl.hsv = function (hsl) {
-	var h = hsl[0];
-	var s = hsl[1] / 100;
-	var l = hsl[2] / 100;
-	var smin = s;
-	var lmin = Math.max(l, 0.01);
-	var sv;
-	var v;
-
-	l *= 2;
-	s *= (l <= 1) ? l : 2 - l;
-	smin *= lmin <= 1 ? lmin : 2 - lmin;
-	v = (l + s) / 2;
-	sv = l === 0 ? (2 * smin) / (lmin + smin) : (2 * s) / (l + s);
-
-	return [h, sv * 100, v * 100];
-};
-
-convert.hsv.rgb = function (hsv) {
-	var h = hsv[0] / 60;
-	var s = hsv[1] / 100;
-	var v = hsv[2] / 100;
-	var hi = Math.floor(h) % 6;
-
-	var f = h - Math.floor(h);
-	var p = 255 * v * (1 - s);
-	var q = 255 * v * (1 - (s * f));
-	var t = 255 * v * (1 - (s * (1 - f)));
-	v *= 255;
-
-	switch (hi) {
-		case 0:
-			return [v, t, p];
-		case 1:
-			return [q, v, p];
-		case 2:
-			return [p, v, t];
-		case 3:
-			return [p, q, v];
-		case 4:
-			return [t, p, v];
-		case 5:
-			return [v, p, q];
-	}
-};
-
-convert.hsv.hsl = function (hsv) {
-	var h = hsv[0];
-	var s = hsv[1] / 100;
-	var v = hsv[2] / 100;
-	var vmin = Math.max(v, 0.01);
-	var lmin;
-	var sl;
-	var l;
-
-	l = (2 - s) * v;
-	lmin = (2 - s) * vmin;
-	sl = s * vmin;
-	sl /= (lmin <= 1) ? lmin : 2 - lmin;
-	sl = sl || 0;
-	l /= 2;
-
-	return [h, sl * 100, l * 100];
-};
-
-// http://dev.w3.org/csswg/css-color/#hwb-to-rgb
-convert.hwb.rgb = function (hwb) {
-	var h = hwb[0] / 360;
-	var wh = hwb[1] / 100;
-	var bl = hwb[2] / 100;
-	var ratio = wh + bl;
-	var i;
-	var v;
-	var f;
-	var n;
-
-	// wh + bl cant be > 1
-	if (ratio > 1) {
-		wh /= ratio;
-		bl /= ratio;
-	}
-
-	i = Math.floor(6 * h);
-	v = 1 - bl;
-	f = 6 * h - i;
-
-	if ((i & 0x01) !== 0) {
-		f = 1 - f;
-	}
-
-	n = wh + f * (v - wh); // linear interpolation
-
-	var r;
-	var g;
-	var b;
-	switch (i) {
-		default:
-		case 6:
-		case 0: r = v; g = n; b = wh; break;
-		case 1: r = n; g = v; b = wh; break;
-		case 2: r = wh; g = v; b = n; break;
-		case 3: r = wh; g = n; b = v; break;
-		case 4: r = n; g = wh; b = v; break;
-		case 5: r = v; g = wh; b = n; break;
-	}
-
-	return [r * 255, g * 255, b * 255];
-};
-
-convert.cmyk.rgb = function (cmyk) {
-	var c = cmyk[0] / 100;
-	var m = cmyk[1] / 100;
-	var y = cmyk[2] / 100;
-	var k = cmyk[3] / 100;
-	var r;
-	var g;
-	var b;
-
-	r = 1 - Math.min(1, c * (1 - k) + k);
-	g = 1 - Math.min(1, m * (1 - k) + k);
-	b = 1 - Math.min(1, y * (1 - k) + k);
-
-	return [r * 255, g * 255, b * 255];
-};
-
-convert.xyz.rgb = function (xyz) {
-	var x = xyz[0] / 100;
-	var y = xyz[1] / 100;
-	var z = xyz[2] / 100;
-	var r;
-	var g;
-	var b;
-
-	r = (x * 3.2406) + (y * -1.5372) + (z * -0.4986);
-	g = (x * -0.9689) + (y * 1.8758) + (z * 0.0415);
-	b = (x * 0.0557) + (y * -0.2040) + (z * 1.0570);
-
-	// assume sRGB
-	r = r > 0.0031308
-		? ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055)
-		: r * 12.92;
-
-	g = g > 0.0031308
-		? ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055)
-		: g * 12.92;
-
-	b = b > 0.0031308
-		? ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055)
-		: b * 12.92;
-
-	r = Math.min(Math.max(0, r), 1);
-	g = Math.min(Math.max(0, g), 1);
-	b = Math.min(Math.max(0, b), 1);
-
-	return [r * 255, g * 255, b * 255];
-};
-
-convert.xyz.lab = function (xyz) {
-	var x = xyz[0];
-	var y = xyz[1];
-	var z = xyz[2];
-	var l;
-	var a;
-	var b;
-
-	x /= 95.047;
-	y /= 100;
-	z /= 108.883;
-
-	x = x > 0.008856 ? Math.pow(x, 1 / 3) : (7.787 * x) + (16 / 116);
-	y = y > 0.008856 ? Math.pow(y, 1 / 3) : (7.787 * y) + (16 / 116);
-	z = z > 0.008856 ? Math.pow(z, 1 / 3) : (7.787 * z) + (16 / 116);
-
-	l = (116 * y) - 16;
-	a = 500 * (x - y);
-	b = 200 * (y - z);
-
-	return [l, a, b];
-};
-
-convert.lab.xyz = function (lab) {
-	var l = lab[0];
-	var a = lab[1];
-	var b = lab[2];
-	var x;
-	var y;
-	var z;
-
-	y = (l + 16) / 116;
-	x = a / 500 + y;
-	z = y - b / 200;
-
-	var y2 = Math.pow(y, 3);
-	var x2 = Math.pow(x, 3);
-	var z2 = Math.pow(z, 3);
-	y = y2 > 0.008856 ? y2 : (y - 16 / 116) / 7.787;
-	x = x2 > 0.008856 ? x2 : (x - 16 / 116) / 7.787;
-	z = z2 > 0.008856 ? z2 : (z - 16 / 116) / 7.787;
-
-	x *= 95.047;
-	y *= 100;
-	z *= 108.883;
-
-	return [x, y, z];
-};
-
-convert.lab.lch = function (lab) {
-	var l = lab[0];
-	var a = lab[1];
-	var b = lab[2];
-	var hr;
-	var h;
-	var c;
-
-	hr = Math.atan2(b, a);
-	h = hr * 360 / 2 / Math.PI;
-
-	if (h < 0) {
-		h += 360;
-	}
-
-	c = Math.sqrt(a * a + b * b);
-
-	return [l, c, h];
-};
-
-convert.lch.lab = function (lch) {
-	var l = lch[0];
-	var c = lch[1];
-	var h = lch[2];
-	var a;
-	var b;
-	var hr;
-
-	hr = h / 360 * 2 * Math.PI;
-	a = c * Math.cos(hr);
-	b = c * Math.sin(hr);
-
-	return [l, a, b];
-};
-
-convert.rgb.ansi16 = function (args) {
-	var r = args[0];
-	var g = args[1];
-	var b = args[2];
-	var value = 1 in arguments ? arguments[1] : convert.rgb.hsv(args)[2]; // hsv -> ansi16 optimization
-
-	value = Math.round(value / 50);
-
-	if (value === 0) {
-		return 30;
-	}
-
-	var ansi = 30
-		+ ((Math.round(b / 255) << 2)
-		| (Math.round(g / 255) << 1)
-		| Math.round(r / 255));
-
-	if (value === 2) {
-		ansi += 60;
-	}
-
-	return ansi;
-};
-
-convert.hsv.ansi16 = function (args) {
-	// optimization here; we already know the value and don't need to get
-	// it converted for us.
-	return convert.rgb.ansi16(convert.hsv.rgb(args), args[2]);
-};
-
-convert.rgb.ansi256 = function (args) {
-	var r = args[0];
-	var g = args[1];
-	var b = args[2];
-
-	// we use the extended greyscale palette here, with the exception of
-	// black and white. normal palette only has 4 greyscale shades.
-	if (r === g && g === b) {
-		if (r < 8) {
-			return 16;
-		}
-
-		if (r > 248) {
-			return 231;
-		}
-
-		return Math.round(((r - 8) / 247) * 24) + 232;
-	}
-
-	var ansi = 16
-		+ (36 * Math.round(r / 255 * 5))
-		+ (6 * Math.round(g / 255 * 5))
-		+ Math.round(b / 255 * 5);
-
-	return ansi;
-};
-
-convert.ansi16.rgb = function (args) {
-	var color = args % 10;
-
-	// handle greyscale
-	if (color === 0 || color === 7) {
-		if (args > 50) {
-			color += 3.5;
-		}
-
-		color = color / 10.5 * 255;
-
-		return [color, color, color];
-	}
-
-	var mult = (~~(args > 50) + 1) * 0.5;
-	var r = ((color & 1) * mult) * 255;
-	var g = (((color >> 1) & 1) * mult) * 255;
-	var b = (((color >> 2) & 1) * mult) * 255;
-
-	return [r, g, b];
-};
-
-convert.ansi256.rgb = function (args) {
-	// handle greyscale
-	if (args >= 232) {
-		var c = (args - 232) * 10 + 8;
-		return [c, c, c];
-	}
-
-	args -= 16;
-
-	var rem;
-	var r = Math.floor(args / 36) / 5 * 255;
-	var g = Math.floor((rem = args % 36) / 6) / 5 * 255;
-	var b = (rem % 6) / 5 * 255;
-
-	return [r, g, b];
-};
-
-convert.rgb.hex = function (args) {
-	var integer = ((Math.round(args[0]) & 0xFF) << 16)
-		+ ((Math.round(args[1]) & 0xFF) << 8)
-		+ (Math.round(args[2]) & 0xFF);
-
-	var string = integer.toString(16).toUpperCase();
-	return '000000'.substring(string.length) + string;
-};
-
-convert.hex.rgb = function (args) {
-	var match = args.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i);
-	if (!match) {
-		return [0, 0, 0];
-	}
-
-	var colorString = match[0];
-
-	if (match[0].length === 3) {
-		colorString = colorString.split('').map(function (char) {
-			return char + char;
-		}).join('');
-	}
-
-	var integer = parseInt(colorString, 16);
-	var r = (integer >> 16) & 0xFF;
-	var g = (integer >> 8) & 0xFF;
-	var b = integer & 0xFF;
-
-	return [r, g, b];
-};
-
-convert.rgb.hcg = function (rgb) {
-	var r = rgb[0] / 255;
-	var g = rgb[1] / 255;
-	var b = rgb[2] / 255;
-	var max = Math.max(Math.max(r, g), b);
-	var min = Math.min(Math.min(r, g), b);
-	var chroma = (max - min);
-	var grayscale;
-	var hue;
-
-	if (chroma < 1) {
-		grayscale = min / (1 - chroma);
-	} else {
-		grayscale = 0;
-	}
-
-	if (chroma <= 0) {
-		hue = 0;
-	} else
-	if (max === r) {
-		hue = ((g - b) / chroma) % 6;
-	} else
-	if (max === g) {
-		hue = 2 + (b - r) / chroma;
-	} else {
-		hue = 4 + (r - g) / chroma + 4;
-	}
-
-	hue /= 6;
-	hue %= 1;
-
-	return [hue * 360, chroma * 100, grayscale * 100];
-};
-
-convert.hsl.hcg = function (hsl) {
-	var s = hsl[1] / 100;
-	var l = hsl[2] / 100;
-	var c = 1;
-	var f = 0;
-
-	if (l < 0.5) {
-		c = 2.0 * s * l;
-	} else {
-		c = 2.0 * s * (1.0 - l);
-	}
-
-	if (c < 1.0) {
-		f = (l - 0.5 * c) / (1.0 - c);
-	}
-
-	return [hsl[0], c * 100, f * 100];
-};
-
-convert.hsv.hcg = function (hsv) {
-	var s = hsv[1] / 100;
-	var v = hsv[2] / 100;
-
-	var c = s * v;
-	var f = 0;
-
-	if (c < 1.0) {
-		f = (v - c) / (1 - c);
-	}
-
-	return [hsv[0], c * 100, f * 100];
-};
-
-convert.hcg.rgb = function (hcg) {
-	var h = hcg[0] / 360;
-	var c = hcg[1] / 100;
-	var g = hcg[2] / 100;
-
-	if (c === 0.0) {
-		return [g * 255, g * 255, g * 255];
-	}
-
-	var pure = [0, 0, 0];
-	var hi = (h % 1) * 6;
-	var v = hi % 1;
-	var w = 1 - v;
-	var mg = 0;
-
-	switch (Math.floor(hi)) {
-		case 0:
-			pure[0] = 1; pure[1] = v; pure[2] = 0; break;
-		case 1:
-			pure[0] = w; pure[1] = 1; pure[2] = 0; break;
-		case 2:
-			pure[0] = 0; pure[1] = 1; pure[2] = v; break;
-		case 3:
-			pure[0] = 0; pure[1] = w; pure[2] = 1; break;
-		case 4:
-			pure[0] = v; pure[1] = 0; pure[2] = 1; break;
-		default:
-			pure[0] = 1; pure[1] = 0; pure[2] = w;
-	}
-
-	mg = (1.0 - c) * g;
-
-	return [
-		(c * pure[0] + mg) * 255,
-		(c * pure[1] + mg) * 255,
-		(c * pure[2] + mg) * 255
-	];
-};
-
-convert.hcg.hsv = function (hcg) {
-	var c = hcg[1] / 100;
-	var g = hcg[2] / 100;
-
-	var v = c + g * (1.0 - c);
-	var f = 0;
-
-	if (v > 0.0) {
-		f = c / v;
-	}
-
-	return [hcg[0], f * 100, v * 100];
-};
-
-convert.hcg.hsl = function (hcg) {
-	var c = hcg[1] / 100;
-	var g = hcg[2] / 100;
-
-	var l = g * (1.0 - c) + 0.5 * c;
-	var s = 0;
-
-	if (l > 0.0 && l < 0.5) {
-		s = c / (2 * l);
-	} else
-	if (l >= 0.5 && l < 1.0) {
-		s = c / (2 * (1 - l));
-	}
-
-	return [hcg[0], s * 100, l * 100];
-};
-
-convert.hcg.hwb = function (hcg) {
-	var c = hcg[1] / 100;
-	var g = hcg[2] / 100;
-	var v = c + g * (1.0 - c);
-	return [hcg[0], (v - c) * 100, (1 - v) * 100];
-};
-
-convert.hwb.hcg = function (hwb) {
-	var w = hwb[1] / 100;
-	var b = hwb[2] / 100;
-	var v = 1 - b;
-	var c = v - w;
-	var g = 0;
-
-	if (c < 1) {
-		g = (v - c) / (1 - c);
-	}
-
-	return [hwb[0], c * 100, g * 100];
-};
-
-convert.apple.rgb = function (apple) {
-	return [(apple[0] / 65535) * 255, (apple[1] / 65535) * 255, (apple[2] / 65535) * 255];
-};
-
-convert.rgb.apple = function (rgb) {
-	return [(rgb[0] / 255) * 65535, (rgb[1] / 255) * 65535, (rgb[2] / 255) * 65535];
-};
-
-convert.gray.rgb = function (args) {
-	return [args[0] / 100 * 255, args[0] / 100 * 255, args[0] / 100 * 255];
-};
-
-convert.gray.hsl = convert.gray.hsv = function (args) {
-	return [0, 0, args[0]];
-};
-
-convert.gray.hwb = function (gray) {
-	return [0, 100, gray[0]];
-};
-
-convert.gray.cmyk = function (gray) {
-	return [0, 0, 0, gray[0]];
-};
-
-convert.gray.lab = function (gray) {
-	return [gray[0], 0, 0];
-};
-
-convert.gray.hex = function (gray) {
-	var val = Math.round(gray[0] / 100 * 255) & 0xFF;
-	var integer = (val << 16) + (val << 8) + val;
-
-	var string = integer.toString(16).toUpperCase();
-	return '000000'.substring(string.length) + string;
-};
-
-convert.rgb.gray = function (rgb) {
-	var val = (rgb[0] + rgb[1] + rgb[2]) / 3;
-	return [val / 255 * 100];
-};
-
-
-/***/ }),
-
-/***/ 2356:
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-Object.defineProperty(exports, "__esModule", {
-  value: true
-});
-exports.default = parseAndBuildMetadata;
-
-function t() {
-  const data = _interopRequireWildcard(__webpack_require__(2268));
-
-  t = function () {
-    return data;
-  };
-
-  return data;
-}
-
-function _parser() {
-  const data = __webpack_require__(3773);
-
-  _parser = function () {
-    return data;
-  };
-
-  return data;
-}
-
-function _codeFrame() {
-  const data = __webpack_require__(2415);
-
-  _codeFrame = function () {
-    return data;
-  };
-
-  return data;
-}
-
-function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }
-
-const PATTERN = /^[_$A-Z0-9]+$/;
-
-function parseAndBuildMetadata(formatter, code, opts) {
-  const ast = parseWithCodeFrame(code, opts.parser);
-  const {
-    placeholderWhitelist,
-    placeholderPattern = PATTERN,
-    preserveComments
-  } = opts;
-  t().removePropertiesDeep(ast, {
-    preserveComments
-  });
-  formatter.validate(ast);
-  const placeholders = [];
-  const placeholderNames = new Set();
-  t().traverse(ast, placeholderVisitorHandler, {
-    placeholders,
-    placeholderNames,
-    placeholderWhitelist,
-    placeholderPattern
-  });
-  return {
-    ast,
-    placeholders,
-    placeholderNames
-  };
-}
-
-function placeholderVisitorHandler(node, ancestors, state) {
-  let name;
-
-  if (t().isIdentifier(node) || t().isJSXIdentifier(node)) {
-    name = node.name;
-  } else if (t().isStringLiteral(node)) {
-    name = node.value;
-  } else {
-    return;
-  }
-
-  if ((!state.placeholderPattern || !state.placeholderPattern.test(name)) && (!state.placeholderWhitelist || !state.placeholderWhitelist.has(name))) {
-    return;
-  }
-
-  ancestors = ancestors.slice();
-  const {
-    node: parent,
-    key
-  } = ancestors[ancestors.length - 1];
-  let type;
-
-  if (t().isStringLiteral(node)) {
-    type = "string";
-  } else if (t().isNewExpression(parent) && key === "arguments" || t().isCallExpression(parent) && key === "arguments" || t().isFunction(parent) && key === "params") {
-    type = "param";
-  } else if (t().isExpressionStatement(parent)) {
-    type = "statement";
-    ancestors = ancestors.slice(0, -1);
-  } else {
-    type = "other";
-  }
-
-  state.placeholders.push({
-    name,
-    type,
-    resolve: ast => resolveAncestors(ast, ancestors),
-    isDuplicate: state.placeholderNames.has(name)
-  });
-  state.placeholderNames.add(name);
-}
-
-function resolveAncestors(ast, ancestors) {
-  let parent = ast;
-
-  for (let i = 0; i < ancestors.length - 1; i++) {
-    const {
-      key,
-      index
-    } = ancestors[i];
-
-    if (index === undefined) {
-      parent = parent[key];
-    } else {
-      parent = parent[key][index];
-    }
-  }
-
-  const {
-    key,
-    index
-  } = ancestors[ancestors.length - 1];
-  return {
-    parent,
-    key,
-    index
-  };
-}
-
-function parseWithCodeFrame(code, parserOpts) {
-  parserOpts = Object.assign({
-    allowReturnOutsideFunction: true,
-    allowSuperOutsideMethod: true,
-    sourceType: "module"
-  }, parserOpts);
-
-  try {
-    return (0, _parser().parse)(code, parserOpts);
-  } catch (err) {
-    const loc = err.loc;
-
-    if (loc) {
-      err.message += "\n" + (0, _codeFrame().codeFrameColumns)(code, {
-        start: loc
-      });
-      err.code = "BABEL_TEMPLATE_PARSE_ERROR";
-    }
-
-    throw err;
-  }
-}
-
-/***/ }),
-
-/***/ 2357:
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-Object.defineProperty(exports, "__esModule", {
-  value: true
-});
-exports.default = populatePlaceholders;
-
-function t() {
-  const data = _interopRequireWildcard(__webpack_require__(2268));
-
-  t = function () {
-    return data;
-  };
-
-  return data;
-}
-
-function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }
-
-function populatePlaceholders(metadata, replacements) {
-  const ast = t().cloneNode(metadata.ast);
-
-  if (replacements) {
-    metadata.placeholders.forEach(placeholder => {
-      if (!Object.prototype.hasOwnProperty.call(replacements, placeholder.name)) {
-        const placeholderName = placeholder.name;
-        throw new Error(`Error: No substitution given for "${placeholderName}". If this is not meant to be a
-            placeholder you may want to consider passing one of the following options to @babel/template:
-            - { placeholderPattern: false, placeholderWhitelist: new Set(['${placeholderName}'])}
-            - { placeholderPattern: /^${placeholderName}$/ }`);
-      }
-    });
-    Object.keys(replacements).forEach(key => {
-      if (!metadata.placeholderNames.has(key)) {
-        throw new Error(`Unknown substitution "${key}" given`);
-      }
-    });
-  }
-
-  metadata.placeholders.slice().reverse().forEach(placeholder => {
-    try {
-      applyReplacement(placeholder, ast, replacements && replacements[placeholder.name] || null);
-    } catch (e) {
-      e.message = `@babel/template placeholder "${placeholder.name}": ${e.message}`;
-      throw e;
-    }
-  });
-  return ast;
-}
-
-function applyReplacement(placeholder, ast, replacement) {
-  if (placeholder.isDuplicate) {
-    if (Array.isArray(replacement)) {
-      replacement = replacement.map(node => t().cloneNode(node));
-    } else if (typeof replacement === "object") {
-      replacement = t().cloneNode(replacement);
-    }
-  }
-
-  const {
-    parent,
-    key,
-    index
-  } = placeholder.resolve(ast);
-
-  if (placeholder.type === "string") {
-    if (typeof replacement === "string") {
-      replacement = t().stringLiteral(replacement);
-    }
-
-    if (!replacement || !t().isStringLiteral(replacement)) {
-      throw new Error("Expected string substitution");
-    }
-  } else if (placeholder.type === "statement") {
-    if (index === undefined) {
-      if (!replacement) {
-        replacement = t().emptyStatement();
-      } else if (Array.isArray(replacement)) {
-        replacement = t().blockStatement(replacement);
-      } else if (typeof replacement === "string") {
-        replacement = t().expressionStatement(t().identifier(replacement));
-      } else if (!t().isStatement(replacement)) {
-        replacement = t().expressionStatement(replacement);
-      }
-    } else {
-      if (replacement && !Array.isArray(replacement)) {
-        if (typeof replacement === "string") {
-          replacement = t().identifier(replacement);
-        }
-
-        if (!t().isStatement(replacement)) {
-          replacement = t().expressionStatement(replacement);
-        }
-      }
-    }
-  } else if (placeholder.type === "param") {
-    if (typeof replacement === "string") {
-      replacement = t().identifier(replacement);
-    }
-
-    if (index === undefined) throw new Error("Assertion failure.");
-  } else {
-    if (typeof replacement === "string") {
-      replacement = t().identifier(replacement);
-    }
-
-    if (Array.isArray(replacement)) {
-      throw new Error("Cannot replace single expression with an array.");
-    }
-  }
-
-  if (index === undefined) {
-    t().validate(parent, key, replacement);
-    parent[key] = replacement;
-  } else {
-    const items = parent[key].slice();
-
-    if (placeholder.type === "statement" || placeholder.type === "param") {
-      if (replacement == null) {
-        items.splice(index, 1);
-      } else if (Array.isArray(replacement)) {
-        items.splice(index, 1, ...replacement);
-      } else {
-        items[index] = replacement;
-      }
-    } else {
-      items[index] = replacement;
-    }
-
-    t().validate(parent, key, items);
-    parent[key] = items;
-  }
-}
-
-/***/ }),
-
 /***/ 2365:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
@@ -22079,819 +20640,16 @@ function tsPrintSignatureDeclarationBase
   this._parameters(parameters, node);
 
   this.token(")");
   this.print(node.typeAnnotation, node);
 }
 
 /***/ }),
 
-/***/ 2388:
-/***/ (function(module, exports, __webpack_require__) {
-
-var conversions = __webpack_require__(2355);
-var route = __webpack_require__(2390);
-
-var convert = {};
-
-var models = Object.keys(conversions);
-
-function wrapRaw(fn) {
-	var wrappedFn = function (args) {
-		if (args === undefined || args === null) {
-			return args;
-		}
-
-		if (arguments.length > 1) {
-			args = Array.prototype.slice.call(arguments);
-		}
-
-		return fn(args);
-	};
-
-	// preserve .conversion property if there is one
-	if ('conversion' in fn) {
-		wrappedFn.conversion = fn.conversion;
-	}
-
-	return wrappedFn;
-}
-
-function wrapRounded(fn) {
-	var wrappedFn = function (args) {
-		if (args === undefined || args === null) {
-			return args;
-		}
-
-		if (arguments.length > 1) {
-			args = Array.prototype.slice.call(arguments);
-		}
-
-		var result = fn(args);
-
-		// we're assuming the result is an array here.
-		// see notice in conversions.js; don't use box types
-		// in conversion functions.
-		if (typeof result === 'object') {
-			for (var len = result.length, i = 0; i < len; i++) {
-				result[i] = Math.round(result[i]);
-			}
-		}
-
-		return result;
-	};
-
-	// preserve .conversion property if there is one
-	if ('conversion' in fn) {
-		wrappedFn.conversion = fn.conversion;
-	}
-
-	return wrappedFn;
-}
-
-models.forEach(function (fromModel) {
-	convert[fromModel] = {};
-
-	Object.defineProperty(convert[fromModel], 'channels', {value: conversions[fromModel].channels});
-	Object.defineProperty(convert[fromModel], 'labels', {value: conversions[fromModel].labels});
-
-	var routes = route(fromModel);
-	var routeModels = Object.keys(routes);
-
-	routeModels.forEach(function (toModel) {
-		var fn = routes[toModel];
-
-		convert[fromModel][toModel] = wrapRounded(fn);
-		convert[fromModel][toModel].raw = wrapRaw(fn);
-	});
-});
-
-module.exports = convert;
-
-
-/***/ }),
-
-/***/ 2389:
-/***/ (function(module, exports) {
-
-module.exports = {
-	"aliceblue": [240, 248, 255],
-	"antiquewhite": [250, 235, 215],
-	"aqua": [0, 255, 255],
-	"aquamarine": [127, 255, 212],
-	"azure": [240, 255, 255],
-	"beige": [245, 245, 220],
-	"bisque": [255, 228, 196],
-	"black": [0, 0, 0],
-	"blanchedalmond": [255, 235, 205],
-	"blue": [0, 0, 255],
-	"blueviolet": [138, 43, 226],
-	"brown": [165, 42, 42],
-	"burlywood": [222, 184, 135],
-	"cadetblue": [95, 158, 160],
-	"chartreuse": [127, 255, 0],
-	"chocolate": [210, 105, 30],
-	"coral": [255, 127, 80],
-	"cornflowerblue": [100, 149, 237],
-	"cornsilk": [255, 248, 220],
-	"crimson": [220, 20, 60],
-	"cyan": [0, 255, 255],
-	"darkblue": [0, 0, 139],
-	"darkcyan": [0, 139, 139],
-	"darkgoldenrod": [184, 134, 11],
-	"darkgray": [169, 169, 169],
-	"darkgreen": [0, 100, 0],
-	"darkgrey": [169, 169, 169],
-	"darkkhaki": [189, 183, 107],
-	"darkmagenta": [139, 0, 139],
-	"darkolivegreen": [85, 107, 47],
-	"darkorange": [255, 140, 0],
-	"darkorchid": [153, 50, 204],
-	"darkred": [139, 0, 0],
-	"darksalmon": [233, 150, 122],
-	"darkseagreen": [143, 188, 143],
-	"darkslateblue": [72, 61, 139],
-	"darkslategray": [47, 79, 79],
-	"darkslategrey": [47, 79, 79],
-	"darkturquoise": [0, 206, 209],
-	"darkviolet": [148, 0, 211],
-	"deeppink": [255, 20, 147],
-	"deepskyblue": [0, 191, 255],
-	"dimgray": [105, 105, 105],
-	"dimgrey": [105, 105, 105],
-	"dodgerblue": [30, 144, 255],
-	"firebrick": [178, 34, 34],
-	"floralwhite": [255, 250, 240],
-	"forestgreen": [34, 139, 34],
-	"fuchsia": [255, 0, 255],
-	"gainsboro": [220, 220, 220],
-	"ghostwhite": [248, 248, 255],
-	"gold": [255, 215, 0],
-	"goldenrod": [218, 165, 32],
-	"gray": [128, 128, 128],
-	"green": [0, 128, 0],
-	"greenyellow": [173, 255, 47],
-	"grey": [128, 128, 128],
-	"honeydew": [240, 255, 240],
-	"hotpink": [255, 105, 180],
-	"indianred": [205, 92, 92],
-	"indigo": [75, 0, 130],
-	"ivory": [255, 255, 240],
-	"khaki": [240, 230, 140],
-	"lavender": [230, 230, 250],
-	"lavenderblush": [255, 240, 245],
-	"lawngreen": [124, 252, 0],
-	"lemonchiffon": [255, 250, 205],
-	"lightblue": [173, 216, 230],
-	"lightcoral": [240, 128, 128],
-	"lightcyan": [224, 255, 255],
-	"lightgoldenrodyellow": [250, 250, 210],
-	"lightgray": [211, 211, 211],
-	"lightgreen": [144, 238, 144],
-	"lightgrey": [211, 211, 211],
-	"lightpink": [255, 182, 193],
-	"lightsalmon": [255, 160, 122],
-	"lightseagreen": [32, 178, 170],
-	"lightskyblue": [135, 206, 250],
-	"lightslategray": [119, 136, 153],
-	"lightslategrey": [119, 136, 153],
-	"lightsteelblue": [176, 196, 222],
-	"lightyellow": [255, 255, 224],
-	"lime": [0, 255, 0],
-	"limegreen": [50, 205, 50],
-	"linen": [250, 240, 230],
-	"magenta": [255, 0, 255],
-	"maroon": [128, 0, 0],
-	"mediumaquamarine": [102, 205, 170],
-	"mediumblue": [0, 0, 205],
-	"mediumorchid": [186, 85, 211],
-	"mediumpurple": [147, 112, 219],
-	"mediumseagreen": [60, 179, 113],
-	"mediumslateblue": [123, 104, 238],
-	"mediumspringgreen": [0, 250, 154],
-	"mediumturquoise": [72, 209, 204],
-	"mediumvioletred": [199, 21, 133],
-	"midnightblue": [25, 25, 112],
-	"mintcream": [245, 255, 250],
-	"mistyrose": [255, 228, 225],
-	"moccasin": [255, 228, 181],
-	"navajowhite": [255, 222, 173],
-	"navy": [0, 0, 128],
-	"oldlace": [253, 245, 230],
-	"olive": [128, 128, 0],
-	"olivedrab": [107, 142, 35],
-	"orange": [255, 165, 0],
-	"orangered": [255, 69, 0],
-	"orchid": [218, 112, 214],
-	"palegoldenrod": [238, 232, 170],
-	"palegreen": [152, 251, 152],
-	"paleturquoise": [175, 238, 238],
-	"palevioletred": [219, 112, 147],
-	"papayawhip": [255, 239, 213],
-	"peachpuff": [255, 218, 185],
-	"peru": [205, 133, 63],
-	"pink": [255, 192, 203],
-	"plum": [221, 160, 221],
-	"powderblue": [176, 224, 230],
-	"purple": [128, 0, 128],
-	"rebeccapurple": [102, 51, 153],
-	"red": [255, 0, 0],
-	"rosybrown": [188, 143, 143],
-	"royalblue": [65, 105, 225],
-	"saddlebrown": [139, 69, 19],
-	"salmon": [250, 128, 114],
-	"sandybrown": [244, 164, 96],
-	"seagreen": [46, 139, 87],
-	"seashell": [255, 245, 238],
-	"sienna": [160, 82, 45],
-	"silver": [192, 192, 192],
-	"skyblue": [135, 206, 235],
-	"slateblue": [106, 90, 205],
-	"slategray": [112, 128, 144],
-	"slategrey": [112, 128, 144],
-	"snow": [255, 250, 250],
-	"springgreen": [0, 255, 127],
-	"steelblue": [70, 130, 180],
-	"tan": [210, 180, 140],
-	"teal": [0, 128, 128],
-	"thistle": [216, 191, 216],
-	"tomato": [255, 99, 71],
-	"turquoise": [64, 224, 208],
-	"violet": [238, 130, 238],
-	"wheat": [245, 222, 179],
-	"white": [255, 255, 255],
-	"whitesmoke": [245, 245, 245],
-	"yellow": [255, 255, 0],
-	"yellowgreen": [154, 205, 50]
-};
-
-/***/ }),
-
-/***/ 2390:
-/***/ (function(module, exports, __webpack_require__) {
-
-var conversions = __webpack_require__(2355);
-
-/*
-	this function routes a model to all other models.
-
-	all functions that are routed have a property `.conversion` attached
-	to the returned synthetic function. This property is an array
-	of strings, each with the steps in between the 'from' and 'to'
-	color models (inclusive).
-
-	conversions that are not possible simply are not included.
-*/
-
-function buildGraph() {
-	var graph = {};
-	// https://jsperf.com/object-keys-vs-for-in-with-closure/3
-	var models = Object.keys(conversions);
-
-	for (var len = models.length, i = 0; i < len; i++) {
-		graph[models[i]] = {
-			// http://jsperf.com/1-vs-infinity
-			// micro-opt, but this is simple.
-			distance: -1,
-			parent: null
-		};
-	}
-
-	return graph;
-}
-
-// https://en.wikipedia.org/wiki/Breadth-first_search
-function deriveBFS(fromModel) {
-	var graph = buildGraph();
-	var queue = [fromModel]; // unshift -> queue -> pop
-
-	graph[fromModel].distance = 0;
-
-	while (queue.length) {
-		var current = queue.pop();
-		var adjacents = Object.keys(conversions[current]);
-
-		for (var len = adjacents.length, i = 0; i < len; i++) {
-			var adjacent = adjacents[i];
-			var node = graph[adjacent];
-
-			if (node.distance === -1) {
-				node.distance = graph[current].distance + 1;
-				node.parent = current;
-				queue.unshift(adjacent);
-			}
-		}
-	}
-
-	return graph;
-}
-
-function link(from, to) {
-	return function (args) {
-		return to(from(args));
-	};
-}
-
-function wrapConversion(toModel, graph) {
-	var path = [graph[toModel].parent, toModel];
-	var fn = conversions[graph[toModel].parent][toModel];
-
-	var cur = graph[toModel].parent;
-	while (graph[cur].parent) {
-		path.unshift(graph[cur].parent);
-		fn = link(conversions[graph[cur].parent][cur], fn);
-		cur = graph[cur].parent;
-	}
-
-	fn.conversion = path;
-	return fn;
-}
-
-module.exports = function (fromModel) {
-	var graph = deriveBFS(fromModel);
-	var conversion = {};
-
-	var models = Object.keys(graph);
-	for (var len = models.length, i = 0; i < len; i++) {
-		var toModel = models[i];
-		var node = graph[toModel];
-
-		if (node.parent === null) {
-			// no possible conversion, or this node is the source model.
-			continue;
-		}
-
-		conversion[toModel] = wrapConversion(toModel, graph);
-	}
-
-	return conversion;
-};
-
-
-
-/***/ }),
-
-/***/ 2392:
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-const TEMPLATE_REGEX = /(?:\\(u[a-f\d]{4}|x[a-f\d]{2}|.))|(?:\{(~)?(\w+(?:\([^)]*\))?(?:\.\w+(?:\([^)]*\))?)*)(?:[ \t]|(?=\r?\n)))|(\})|((?:.|[\r\n\f])+?)/gi;
-const STYLE_REGEX = /(?:^|\.)(\w+)(?:\(([^)]*)\))?/g;
-const STRING_REGEX = /^(['"])((?:\\.|(?!\1)[^\\])*)\1$/;
-const ESCAPE_REGEX = /\\(u[a-f\d]{4}|x[a-f\d]{2}|.)|([^\\])/gi;
-
-const ESCAPES = new Map([
-	['n', '\n'],
-	['r', '\r'],
-	['t', '\t'],
-	['b', '\b'],
-	['f', '\f'],
-	['v', '\v'],
-	['0', '\0'],
-	['\\', '\\'],
-	['e', '\u001B'],
-	['a', '\u0007']
-]);
-
-function unescape(c) {
-	if ((c[0] === 'u' && c.length === 5) || (c[0] === 'x' && c.length === 3)) {
-		return String.fromCharCode(parseInt(c.slice(1), 16));
-	}
-
-	return ESCAPES.get(c) || c;
-}
-
-function parseArguments(name, args) {
-	const results = [];
-	const chunks = args.trim().split(/\s*,\s*/g);
-	let matches;
-
-	for (const chunk of chunks) {
-		if (!isNaN(chunk)) {
-			results.push(Number(chunk));
-		} else if ((matches = chunk.match(STRING_REGEX))) {
-			results.push(matches[2].replace(ESCAPE_REGEX, (m, escape, chr) => escape ? unescape(escape) : chr));
-		} else {
-			throw new Error(`Invalid Chalk template style argument: ${chunk} (in style '${name}')`);
-		}
-	}
-
-	return results;
-}
-
-function parseStyle(style) {
-	STYLE_REGEX.lastIndex = 0;
-
-	const results = [];
-	let matches;
-
-	while ((matches = STYLE_REGEX.exec(style)) !== null) {
-		const name = matches[1];
-
-		if (matches[2]) {
-			const args = parseArguments(name, matches[2]);
-			results.push([name].concat(args));
-		} else {
-			results.push([name]);
-		}
-	}
-
-	return results;
-}
-
-function buildStyle(chalk, styles) {
-	const enabled = {};
-
-	for (const layer of styles) {
-		for (const style of layer.styles) {
-			enabled[style[0]] = layer.inverse ? null : style.slice(1);
-		}
-	}
-
-	let current = chalk;
-	for (const styleName of Object.keys(enabled)) {
-		if (Array.isArray(enabled[styleName])) {
-			if (!(styleName in current)) {
-				throw new Error(`Unknown Chalk style: ${styleName}`);
-			}
-
-			if (enabled[styleName].length > 0) {
-				current = current[styleName].apply(current, enabled[styleName]);
-			} else {
-				current = current[styleName];
-			}
-		}
-	}
-
-	return current;
-}
-
-module.exports = (chalk, tmp) => {
-	const styles = [];
-	const chunks = [];
-	let chunk = [];
-
-	// eslint-disable-next-line max-params
-	tmp.replace(TEMPLATE_REGEX, (m, escapeChar, inverse, style, close, chr) => {
-		if (escapeChar) {
-			chunk.push(unescape(escapeChar));
-		} else if (style) {
-			const str = chunk.join('');
-			chunk = [];
-			chunks.push(styles.length === 0 ? str : buildStyle(chalk, styles)(str));
-			styles.push({inverse, styles: parseStyle(style)});
-		} else if (close) {
-			if (styles.length === 0) {
-				throw new Error('Found extraneous } in Chalk template literal');
-			}
-
-			chunks.push(buildStyle(chalk, styles)(chunk.join('')));
-			chunk = [];
-			styles.pop();
-		} else {
-			chunk.push(chr);
-		}
-	});
-
-	chunks.push(chunk.join(''));
-
-	if (styles.length > 0) {
-		const errMsg = `Chalk template literal is missing ${styles.length} closing bracket${styles.length === 1 ? '' : 's'} (\`}\`)`;
-		throw new Error(errMsg);
-	}
-
-	return chunks.join('');
-};
-
-
-/***/ }),
-
-/***/ 2397:
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-Object.defineProperty(exports, "__esModule", {
-  value: true
-});
-exports.default = exports.program = exports.expression = exports.statements = exports.statement = exports.smart = void 0;
-
-var formatters = _interopRequireWildcard(__webpack_require__(2398));
-
-var _builder = _interopRequireDefault(__webpack_require__(2399));
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }
-
-const smart = (0, _builder.default)(formatters.smart);
-exports.smart = smart;
-const statement = (0, _builder.default)(formatters.statement);
-exports.statement = statement;
-const statements = (0, _builder.default)(formatters.statements);
-exports.statements = statements;
-const expression = (0, _builder.default)(formatters.expression);
-exports.expression = expression;
-const program = (0, _builder.default)(formatters.program);
-exports.program = program;
-
-var _default = Object.assign(smart.bind(undefined), {
-  smart,
-  statement,
-  statements,
-  expression,
-  program,
-  ast: smart.ast
-});
-
-exports.default = _default;
-
-/***/ }),
-
-/***/ 2398:
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-Object.defineProperty(exports, "__esModule", {
-  value: true
-});
-exports.program = exports.expression = exports.statement = exports.statements = exports.smart = void 0;
-
-function makeStatementFormatter(fn) {
-  return {
-    code: str => `/* @babel/template */;\n${str}`,
-    validate: () => {},
-    unwrap: ast => {
-      return fn(ast.program.body.slice(1));
-    }
-  };
-}
-
-const smart = makeStatementFormatter(body => {
-  if (body.length > 1) {
-    return body;
-  } else {
-    return body[0];
-  }
-});
-exports.smart = smart;
-const statements = makeStatementFormatter(body => body);
-exports.statements = statements;
-const statement = makeStatementFormatter(body => {
-  if (body.length === 0) {
-    throw new Error("Found nothing to return.");
-  }
-
-  if (body.length > 1) {
-    throw new Error("Found multiple statements but wanted one");
-  }
-
-  return body[0];
-});
-exports.statement = statement;
-const expression = {
-  code: str => `(\n${str}\n)`,
-  validate: ({
-    program
-  }) => {
-    if (program.body.length > 1) {
-      throw new Error("Found multiple statements but wanted one");
-    }
-
-    const expression = program.body[0].expression;
-
-    if (expression.start === 0) {
-      throw new Error("Parse result included parens.");
-    }
-  },
-  unwrap: ast => ast.program.body[0].expression
-};
-exports.expression = expression;
-const program = {
-  code: str => str,
-  validate: () => {},
-  unwrap: ast => ast.program
-};
-exports.program = program;
-
-/***/ }),
-
-/***/ 2399:
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-Object.defineProperty(exports, "__esModule", {
-  value: true
-});
-exports.default = createTemplateBuilder;
-
-var _options = __webpack_require__(2348);
-
-var _string = _interopRequireDefault(__webpack_require__(2400));
-
-var _literal = _interopRequireDefault(__webpack_require__(2402));
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-const NO_PLACEHOLDER = (0, _options.validate)({
-  placeholderPattern: false
-});
-
-function createTemplateBuilder(formatter, defaultOpts) {
-  const templateFnCache = new WeakMap();
-  const templateAstCache = new WeakMap();
-  const cachedOpts = defaultOpts || (0, _options.validate)(null);
-  return Object.assign((tpl, ...args) => {
-    if (typeof tpl === "string") {
-      if (args.length > 1) throw new Error("Unexpected extra params.");
-      return extendedTrace((0, _string.default)(formatter, tpl, (0, _options.merge)(cachedOpts, (0, _options.validate)(args[0]))));
-    } else if (Array.isArray(tpl)) {
-      let builder = templateFnCache.get(tpl);
-
-      if (!builder) {
-        builder = (0, _literal.default)(formatter, tpl, cachedOpts);
-        templateFnCache.set(tpl, builder);
-      }
-
-      return extendedTrace(builder(args));
-    } else if (typeof tpl === "object" && tpl) {
-      if (args.length > 0) throw new Error("Unexpected extra params.");
-      return createTemplateBuilder(formatter, (0, _options.merge)(cachedOpts, (0, _options.validate)(tpl)));
-    }
-
-    throw new Error(`Unexpected template param ${typeof tpl}`);
-  }, {
-    ast: (tpl, ...args) => {
-      if (typeof tpl === "string") {
-        if (args.length > 1) throw new Error("Unexpected extra params.");
-        return (0, _string.default)(formatter, tpl, (0, _options.merge)((0, _options.merge)(cachedOpts, (0, _options.validate)(args[0])), NO_PLACEHOLDER))();
-      } else if (Array.isArray(tpl)) {
-        let builder = templateAstCache.get(tpl);
-
-        if (!builder) {
-          builder = (0, _literal.default)(formatter, tpl, (0, _options.merge)(cachedOpts, NO_PLACEHOLDER));
-          templateAstCache.set(tpl, builder);
-        }
-
-        return builder(args)();
-      }
-
-      throw new Error(`Unexpected template param ${typeof tpl}`);
-    }
-  });
-}
-
-function extendedTrace(fn) {
-  let rootStack = "";
-
-  try {
-    throw new Error();
-  } catch (error) {
-    if (error.stack) {
-      rootStack = error.stack.split("\n").slice(3).join("\n");
-    }
-  }
-
-  return arg => {
-    try {
-      return fn(arg);
-    } catch (err) {
-      err.stack += `\n    =============\n${rootStack}`;
-      throw err;
-    }
-  };
-}
-
-/***/ }),
-
-/***/ 2400:
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-Object.defineProperty(exports, "__esModule", {
-  value: true
-});
-exports.default = stringTemplate;
-
-var _options = __webpack_require__(2348);
-
-var _parse = _interopRequireDefault(__webpack_require__(2356));
-
-var _populate = _interopRequireDefault(__webpack_require__(2357));
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-function stringTemplate(formatter, code, opts) {
-  code = formatter.code(code);
-  let metadata;
-  return arg => {
-    const replacements = (0, _options.normalizeReplacements)(arg);
-    if (!metadata) metadata = (0, _parse.default)(formatter, code, opts);
-    return formatter.unwrap((0, _populate.default)(metadata, replacements));
-  };
-}
-
-/***/ }),
-
-/***/ 2402:
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-Object.defineProperty(exports, "__esModule", {
-  value: true
-});
-exports.default = literalTemplate;
-
-var _options = __webpack_require__(2348);
-
-var _parse = _interopRequireDefault(__webpack_require__(2356));
-
-var _populate = _interopRequireDefault(__webpack_require__(2357));
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-function literalTemplate(formatter, tpl, opts) {
-  const {
-    metadata,
-    names
-  } = buildLiteralData(formatter, tpl, opts);
-  return arg => {
-    const defaultReplacements = arg.reduce((acc, replacement, i) => {
-      acc[names[i]] = replacement;
-      return acc;
-    }, {});
-    return arg => {
-      const replacements = (0, _options.normalizeReplacements)(arg);
-
-      if (replacements) {
-        Object.keys(replacements).forEach(key => {
-          if (Object.prototype.hasOwnProperty.call(defaultReplacements, key)) {
-            throw new Error("Unexpected replacement overlap.");
-          }
-        });
-      }
-
-      return formatter.unwrap((0, _populate.default)(metadata, replacements ? Object.assign(replacements, defaultReplacements) : defaultReplacements));
-    };
-  };
-}
-
-function buildLiteralData(formatter, tpl, opts) {
-  let names;
-  let nameSet;
-  let metadata;
-  let prefix = "";
-
-  do {
-    prefix += "$";
-    const result = buildTemplateCode(tpl, prefix);
-    names = result.names;
-    nameSet = new Set(names);
-    metadata = (0, _parse.default)(formatter, formatter.code(result.code), {
-      parser: opts.parser,
-      placeholderWhitelist: new Set(result.names.concat(opts.placeholderWhitelist ? Array.from(opts.placeholderWhitelist) : [])),
-      placeholderPattern: opts.placeholderPattern,
-      preserveComments: opts.preserveComments
-    });
-  } while (metadata.placeholders.some(placeholder => placeholder.isDuplicate && nameSet.has(placeholder.name)));
-
-  return {
-    metadata,
-    names
-  };
-}
-
-function buildTemplateCode(tpl, prefix) {
-  const names = [];
-  let code = tpl[0];
-
-  for (let i = 1; i < tpl.length; i++) {
-    const value = `${prefix}${i - 1}`;
-    names.push(value);
-    code += value + tpl[i];
-  }
-
-  return {
-    names,
-    code
-  };
-}
-
-/***/ }),
-
 /***/ 2413:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
@@ -23701,197 +21459,16 @@ function stripModuleScope(rootScope) {
   rootLexicalScope.children = moduleScope.children;
   rootLexicalScope.children.forEach(child => {
     child.parent = rootLexicalScope;
   });
 }
 
 /***/ }),
 
-/***/ 2415:
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-/* WEBPACK VAR INJECTION */(function(process) {
-
-Object.defineProperty(exports, "__esModule", {
-  value: true
-});
-exports.codeFrameColumns = codeFrameColumns;
-exports.default = _default;
-
-function _highlight() {
-  const data = _interopRequireWildcard(__webpack_require__(3779));
-
-  _highlight = function () {
-    return data;
-  };
-
-  return data;
-}
-
-function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }
-
-let deprecationWarningShown = false;
-
-function getDefs(chalk) {
-  return {
-    gutter: chalk.grey,
-    marker: chalk.red.bold,
-    message: chalk.red.bold
-  };
-}
-
-const NEWLINE = /\r\n|[\n\r\u2028\u2029]/;
-
-function getMarkerLines(loc, source, opts) {
-  const startLoc = Object.assign({
-    column: 0,
-    line: -1
-  }, loc.start);
-  const endLoc = Object.assign({}, startLoc, loc.end);
-  const {
-    linesAbove = 2,
-    linesBelow = 3
-  } = opts || {};
-  const startLine = startLoc.line;
-  const startColumn = startLoc.column;
-  const endLine = endLoc.line;
-  const endColumn = endLoc.column;
-  let start = Math.max(startLine - (linesAbove + 1), 0);
-  let end = Math.min(source.length, endLine + linesBelow);
-
-  if (startLine === -1) {
-    start = 0;
-  }
-
-  if (endLine === -1) {
-    end = source.length;
-  }
-
-  const lineDiff = endLine - startLine;
-  const markerLines = {};
-
-  if (lineDiff) {
-    for (let i = 0; i <= lineDiff; i++) {
-      const lineNumber = i + startLine;
-
-      if (!startColumn) {
-        markerLines[lineNumber] = true;
-      } else if (i === 0) {
-        const sourceLength = source[lineNumber - 1].length;
-        markerLines[lineNumber] = [startColumn, sourceLength - startColumn];
-      } else if (i === lineDiff) {
-        markerLines[lineNumber] = [0, endColumn];
-      } else {
-        const sourceLength = source[lineNumber - i].length;
-        markerLines[lineNumber] = [0, sourceLength];
-      }
-    }
-  } else {
-    if (startColumn === endColumn) {
-      if (startColumn) {
-        markerLines[startLine] = [startColumn, 0];
-      } else {
-        markerLines[startLine] = true;
-      }
-    } else {
-      markerLines[startLine] = [startColumn, endColumn - startColumn];
-    }
-  }
-
-  return {
-    start,
-    end,
-    markerLines
-  };
-}
-
-function codeFrameColumns(rawLines, loc, opts = {}) {
-  const highlighted = (opts.highlightCode || opts.forceColor) && (0, _highlight().shouldHighlight)(opts);
-  const chalk = (0, _highlight().getChalk)(opts);
-  const defs = getDefs(chalk);
-
-  const maybeHighlight = (chalkFn, string) => {
-    return highlighted ? chalkFn(string) : string;
-  };
-
-  if (highlighted) rawLines = (0, _highlight().default)(rawLines, opts);
-  const lines = rawLines.split(NEWLINE);
-  const {
-    start,
-    end,
-    markerLines
-  } = getMarkerLines(loc, lines, opts);
-  const hasColumns = loc.start && typeof loc.start.column === "number";
-  const numberMaxWidth = String(end).length;
-  let frame = lines.slice(start, end).map((line, index) => {
-    const number = start + 1 + index;
-    const paddedNumber = ` ${number}`.slice(-numberMaxWidth);
-    const gutter = ` ${paddedNumber} | `;
-    const hasMarker = markerLines[number];
-    const lastMarkerLine = !markerLines[number + 1];
-
-    if (hasMarker) {
-      let markerLine = "";
-
-      if (Array.isArray(hasMarker)) {
-        const markerSpacing = line.slice(0, Math.max(hasMarker[0] - 1, 0)).replace(/[^\t]/g, " ");
-        const numberOfMarkers = hasMarker[1] || 1;
-        markerLine = ["\n ", maybeHighlight(defs.gutter, gutter.replace(/\d/g, " ")), markerSpacing, maybeHighlight(defs.marker, "^").repeat(numberOfMarkers)].join("");
-
-        if (lastMarkerLine && opts.message) {
-          markerLine += " " + maybeHighlight(defs.message, opts.message);
-        }
-      }
-
-      return [maybeHighlight(defs.marker, ">"), maybeHighlight(defs.gutter, gutter), line, markerLine].join("");
-    } else {
-      return ` ${maybeHighlight(defs.gutter, gutter)}${line}`;
-    }
-  }).join("\n");
-
-  if (opts.message && !hasColumns) {
-    frame = `${" ".repeat(numberMaxWidth + 1)}${opts.message}\n${frame}`;
-  }
-
-  if (highlighted) {
-    return chalk.reset(frame);
-  } else {
-    return frame;
-  }
-}
-
-function _default(rawLines, lineNumber, colNumber, opts = {}) {
-  if (!deprecationWarningShown) {
-    deprecationWarningShown = true;
-    const message = "Passing lineNumber and colNumber is deprecated to @babel/code-frame. Please use `codeFrameColumns`.";
-
-    if (process.emitWarning) {
-      process.emitWarning(message, "DeprecationWarning");
-    } else {
-      const deprecationError = new Error(message);
-      deprecationError.name = "DeprecationWarning";
-      console.warn(new Error(message));
-    }
-  }
-
-  colNumber = Math.max(colNumber, 0);
-  const location = {
-    start: {
-      column: colNumber,
-      line: lineNumber
-    }
-  };
-  return codeFrameColumns(rawLines, location, opts);
-}
-/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(120)))
-
-/***/ }),
-
 /***/ 248:
 /***/ (function(module, exports, __webpack_require__) {
 
 (function(){
   var crypt = __webpack_require__(249),
       utf8 = __webpack_require__(250).utf8,
       isBuffer = __webpack_require__(251),
       bin = __webpack_require__(250).bin,
@@ -25452,17 +23029,17 @@ function getFirstExpression(ast) {
   return statements[0].expression;
 }
 
 function locationKey(start) {
   return `${start.line}:${start.column}`;
 }
 
 function mapOriginalExpression(expression, mappings) {
-  const ast = (0, _ast.parseScript)(expression, { allowAwaitOutsideFunction: true });
+  const ast = (0, _ast.parseConsoleScript)(expression);
   const scopes = (0, _getScopes.buildScopeList)(ast, "");
   let shouldUpdate = false;
 
   const nodes = new Map();
   const replacements = new Map();
 
   // The ref-only global bindings are the ones that are accessed, but not
   // declared anywhere in the parsed code, meaning they are either global,
@@ -25986,98 +23563,118 @@ var _generator2 = _interopRequireDefault
 var _types = __webpack_require__(2268);
 
 var t = _interopRequireWildcard(_types);
 
 function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
 
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
-// translates new bindings `var a = 3` into `self.a = 3`
-// and existing bindings `var a = 3` into `a = 3` for re-assignments
 /* 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/>. */
 
+function getAssignmentTarget(node, bindings) {
+  if (t.isObjectPattern(node)) {
+    for (const property of node.properties) {
+      if (t.isRestElement(property)) {
+        property.argument = getAssignmentTarget(property.argument, bindings);
+      } else {
+        property.value = getAssignmentTarget(property.value, bindings);
+      }
+    }
+
+    return node;
+  }
+
+  if (t.isArrayPattern(node)) {
+    for (const [i, element] of node.elements.entries()) {
+      node.elements[i] = getAssignmentTarget(element, bindings);
+    }
+
+    return node;
+  }
+
+  if (t.isAssignmentPattern(node)) {
+    node.left = getAssignmentTarget(node.left, bindings);
+
+    return node;
+  }
+
+  if (t.isRestElement(node)) {
+    node.argument = getAssignmentTarget(node.argument, bindings);
+
+    return node;
+  }
+
+  if (t.isIdentifier(node)) {
+    return bindings.includes(node.name) ? node : t.memberExpression(t.identifier("self"), node);
+  }
+
+  return node;
+}
+
+// translates new bindings `var a = 3` into `self.a = 3`
+// and existing bindings `var a = 3` into `a = 3` for re-assignments
 function globalizeDeclaration(node, bindings) {
-  return node.declarations.map(declaration => {
-    const identifier = bindings.includes(declaration.id.name) ? declaration.id : t.memberExpression(t.identifier("self"), declaration.id);
-
-    return t.expressionStatement(t.assignmentExpression("=", identifier, declaration.init));
-  });
+  return node.declarations.map(declaration => t.expressionStatement(t.assignmentExpression("=", getAssignmentTarget(declaration.id, bindings), declaration.init)));
 }
 
 // translates new bindings `a = 3` into `self.a = 3`
 // and keeps assignments the same for existing bindings.
 function globalizeAssignment(node, bindings) {
-  if (bindings.includes(node.left.name)) {
-    return node;
-  }
-
-  const identifier = t.memberExpression(t.identifier("self"), node.left);
-  return t.assignmentExpression(node.operator, identifier, node.right);
+  return t.assignmentExpression(node.operator, getAssignmentTarget(node.left, bindings), node.right);
 }
 
 function replaceNode(ancestors, node) {
   const parent = ancestors[ancestors.length - 1];
 
   if (typeof parent.index === "number") {
     if (Array.isArray(node)) {
       parent.node[parent.key].splice(parent.index, 1, ...node);
     } else {
       parent.node[parent.key][parent.index] = node;
     }
   } else {
     parent.node[parent.key] = node;
   }
 }
 
-function hasDestructuring(node) {
-  return node.declarations.some(declaration => t.isPattern(declaration.id));
-}
-
 function mapExpressionBindings(expression, bindings = []) {
-  const ast = (0, _ast.parseScript)(expression, { allowAwaitOutsideFunction: true });
+  const ast = (0, _ast.parseConsoleScript)(expression);
+
   let isMapped = false;
   let shouldUpdate = true;
 
   t.traverse(ast, (node, ancestors) => {
     const parent = ancestors[ancestors.length - 1];
 
     if (t.isWithStatement(node)) {
       shouldUpdate = false;
       return;
     }
 
     if (!(0, _helpers.isTopLevel)(ancestors)) {
       return;
     }
 
     if (t.isAssignmentExpression(node)) {
-      if (t.isIdentifier(node.left)) {
+      if (t.isIdentifier(node.left) || t.isPattern(node.left)) {
         const newNode = globalizeAssignment(node, bindings);
         isMapped = true;
         return replaceNode(ancestors, newNode);
       }
 
-      if (t.isPattern(node.left)) {
-        shouldUpdate = false;
-        return;
-      }
+      return;
     }
 
     if (!t.isVariableDeclaration(node)) {
       return;
     }
 
-    if (hasDestructuring(node)) {
-      shouldUpdate = false;
-      return;
-    }
-
     if (!t.isForStatement(parent.node)) {
       const newNodes = globalizeDeclaration(node, bindings);
       isMapped = true;
       replaceNode(ancestors, newNodes);
     }
   });
 
   if (!shouldUpdate || !isMapped) {
@@ -46739,215 +44336,64 @@ exports.tokTypes = types;
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
 exports.default = mapTopLevelAwait;
 
-var _template = __webpack_require__(2397);
-
-var _template2 = _interopRequireDefault(_template);
-
 var _generator = __webpack_require__(2365);
 
 var _generator2 = _interopRequireDefault(_generator);
 
 var _types = __webpack_require__(2268);
 
 var t = _interopRequireWildcard(_types);
 
 var _ast = __webpack_require__(1375);
 
 var _helpers = __webpack_require__(1411);
 
 function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
 
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
+/* 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/>. */
+
 function hasTopLevelAwait(expression) {
-  const ast = (0, _ast.parse)(expression, { allowAwaitOutsideFunction: true });
+  const ast = (0, _ast.parseConsoleScript)(expression);
   const hasAwait = (0, _ast.hasNode)(ast, (node, ancestors, b) => t.isAwaitExpression(node) && (0, _helpers.isTopLevel)(ancestors));
 
   return hasAwait && ast;
-} /* 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/>. */
+}
 
 function wrapExpression(ast) {
   const statements = ast.program.body;
   const lastStatement = statements[statements.length - 1];
   const body = statements.slice(0, -1).concat(t.returnStatement(lastStatement.expression));
 
-  const newAst = t.arrowFunctionExpression([], t.blockStatement(body), true);
+  const newAst = t.expressionStatement(t.callExpression(t.arrowFunctionExpression([], t.blockStatement(body), true), []));
+
   return (0, _generator2.default)(newAst).code;
 }
 
 function mapTopLevelAwait(expression) {
   const ast = hasTopLevelAwait(expression);
   if (ast) {
-    const func = wrapExpression(ast);
-    return (0, _generator2.default)(_template2.default.ast(`(${func})();`)).code;
+    return wrapExpression(ast);
   }
 
   return expression;
 }
 
 /***/ }),
 
-/***/ 3779:
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-Object.defineProperty(exports, "__esModule", {
-  value: true
-});
-exports.shouldHighlight = shouldHighlight;
-exports.getChalk = getChalk;
-exports.default = highlight;
-
-function _jsTokens() {
-  const data = _interopRequireWildcard(__webpack_require__(630));
-
-  _jsTokens = function () {
-    return data;
-  };
-
-  return data;
-}
-
-function _esutils() {
-  const data = _interopRequireDefault(__webpack_require__(530));
-
-  _esutils = function () {
-    return data;
-  };
-
-  return data;
-}
-
-function _chalk() {
-  const data = _interopRequireDefault(__webpack_require__(631));
-
-  _chalk = function () {
-    return data;
-  };
-
-  return data;
-}
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }
-
-function getDefs(chalk) {
-  return {
-    keyword: chalk.cyan,
-    capitalized: chalk.yellow,
-    jsx_tag: chalk.yellow,
-    punctuator: chalk.yellow,
-    number: chalk.magenta,
-    string: chalk.green,
-    regex: chalk.magenta,
-    comment: chalk.grey,
-    invalid: chalk.white.bgRed.bold
-  };
-}
-
-const NEWLINE = /\r\n|[\n\r\u2028\u2029]/;
-const JSX_TAG = /^[a-z][\w-]*$/i;
-const BRACKET = /^[()[\]{}]$/;
-
-function getTokenType(match) {
-  const [offset, text] = match.slice(-2);
-  const token = (0, _jsTokens().matchToToken)(match);
-
-  if (token.type === "name") {
-    if (_esutils().default.keyword.isReservedWordES6(token.value)) {
-      return "keyword";
-    }
-
-    if (JSX_TAG.test(token.value) && (text[offset - 1] === "<" || text.substr(offset - 2, 2) == "</")) {
-      return "jsx_tag";
-    }
-
-    if (token.value[0] !== token.value[0].toLowerCase()) {
-      return "capitalized";
-    }
-  }
-
-  if (token.type === "punctuator" && BRACKET.test(token.value)) {
-    return "bracket";
-  }
-
-  if (token.type === "invalid" && (token.value === "@" || token.value === "#")) {
-    return "punctuator";
-  }
-
-  return token.type;
-}
-
-function highlightTokens(defs, text) {
-  return text.replace(_jsTokens().default, function (...args) {
-    const type = getTokenType(args);
-    const colorize = defs[type];
-
-    if (colorize) {
-      return args[0].split(NEWLINE).map(str => colorize(str)).join("\n");
-    } else {
-      return args[0];
-    }
-  });
-}
-
-function shouldHighlight(options) {
-  return _chalk().default.supportsColor || options.forceColor;
-}
-
-function getChalk(options) {
-  let chalk = _chalk().default;
-
-  if (options.forceColor) {
-    chalk = new (_chalk().default.constructor)({
-      enabled: true,
-      level: 1
-    });
-  }
-
-  return chalk;
-}
-
-function highlight(code, options = {}) {
-  if (shouldHighlight(options)) {
-    const chalk = getChalk(options);
-    const defs = getDefs(chalk);
-    return highlightTokens(defs, code);
-  } else {
-    return code;
-  }
-}
-
-/***/ }),
-
-/***/ 3780:
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-module.exports = {
-	stdout: false,
-	stderr: false
-};
-
-
-/***/ }),
-
 /***/ 398:
 /***/ (function(module, exports, __webpack_require__) {
 
 /* WEBPACK VAR INJECTION */(function(module) {var root = __webpack_require__(8);
 
 /** Detect free variable `exports`. */
 var freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports;
 
@@ -48974,476 +46420,16 @@ function baseRepeat(string, n) {
   return result;
 }
 
 module.exports = baseRepeat;
 
 
 /***/ }),
 
-/***/ 630:
-/***/ (function(module, exports) {
-
-// Copyright 2014, 2015, 2016, 2017 Simon Lydell
-// License: MIT. (See LICENSE.)
-
-Object.defineProperty(exports, "__esModule", {
-  value: true
-})
-
-// This regex comes from regex.coffee, and is inserted here by generate-index.js
-// (run `npm run build`).
-exports.default = /((['"])(?:(?!\2|\\).|\\(?:\r\n|[\s\S]))*(\2)?|`(?:[^`\\$]|\\[\s\S]|\$(?!\{)|\$\{(?:[^{}]|\{[^}]*\}?)*\}?)*(`)?)|(\/\/.*)|(\/\*(?:[^*]|\*(?!\/))*(\*\/)?)|(\/(?!\*)(?:\[(?:(?![\]\\]).|\\.)*\]|(?![\/\]\\]).|\\.)+\/(?:(?!\s*(?:\b|[\u0080-\uFFFF$\\'"~({]|[+\-!](?!=)|\.?\d))|[gmiyu]{1,5}\b(?![\u0080-\uFFFF$\\]|\s*(?:[+\-*%&|^<>!=?({]|\/(?![\/*])))))|(0[xX][\da-fA-F]+|0[oO][0-7]+|0[bB][01]+|(?:\d*\.\d+|\d+\.?)(?:[eE][+-]?\d+)?)|((?!\d)(?:(?!\s)[$\w\u0080-\uFFFF]|\\u[\da-fA-F]{4}|\\u\{[\da-fA-F]+\})+)|(--|\+\+|&&|\|\||=>|\.{3}|(?:[+\-\/%&|^]|\*{1,2}|<{1,2}|>{1,3}|!=?|={1,2})=?|[?~.,:;[\](){}])|(\s+)|(^$|[\s\S])/g
-
-exports.matchToToken = function(match) {
-  var token = {type: "invalid", value: match[0]}
-       if (match[ 1]) token.type = "string" , token.closed = !!(match[3] || match[4])
-  else if (match[ 5]) token.type = "comment"
-  else if (match[ 6]) token.type = "comment", token.closed = !!match[7]
-  else if (match[ 8]) token.type = "regex"
-  else if (match[ 9]) token.type = "number"
-  else if (match[10]) token.type = "name"
-  else if (match[11]) token.type = "punctuator"
-  else if (match[12]) token.type = "whitespace"
-  return token
-}
-
-
-/***/ }),
-
-/***/ 631:
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-/* WEBPACK VAR INJECTION */(function(process) {
-const escapeStringRegexp = __webpack_require__(632);
-const ansiStyles = __webpack_require__(633);
-const stdoutColor = __webpack_require__(3780).stdout;
-
-const template = __webpack_require__(2392);
-
-const isSimpleWindowsTerm = process.platform === 'win32' && !(Object({"NODE_ENV":"production","TARGET":"firefox-panel"}).TERM || '').toLowerCase().startsWith('xterm');
-
-// `supportsColor.level` → `ansiStyles.color[name]` mapping
-const levelMapping = ['ansi', 'ansi', 'ansi256', 'ansi16m'];
-
-// `color-convert` models to exclude from the Chalk API due to conflicts and such
-const skipModels = new Set(['gray']);
-
-const styles = Object.create(null);
-
-function applyOptions(obj, options) {
-	options = options || {};
-
-	// Detect level if not set manually
-	const scLevel = stdoutColor ? stdoutColor.level : 0;
-	obj.level = options.level === undefined ? scLevel : options.level;
-	obj.enabled = 'enabled' in options ? options.enabled : obj.level > 0;
-}
-
-function Chalk(options) {
-	// We check for this.template here since calling `chalk.constructor()`
-	// by itself will have a `this` of a previously constructed chalk object
-	if (!this || !(this instanceof Chalk) || this.template) {
-		const chalk = {};
-		applyOptions(chalk, options);
-
-		chalk.template = function () {
-			const args = [].slice.call(arguments);
-			return chalkTag.apply(null, [chalk.template].concat(args));
-		};
-
-		Object.setPrototypeOf(chalk, Chalk.prototype);
-		Object.setPrototypeOf(chalk.template, chalk);
-
-		chalk.template.constructor = Chalk;
-
-		return chalk.template;
-	}
-
-	applyOptions(this, options);
-}
-
-// Use bright blue on Windows as the normal blue color is illegible
-if (isSimpleWindowsTerm) {
-	ansiStyles.blue.open = '\u001B[94m';
-}
-
-for (const key of Object.keys(ansiStyles)) {
-	ansiStyles[key].closeRe = new RegExp(escapeStringRegexp(ansiStyles[key].close), 'g');
-
-	styles[key] = {
-		get() {
-			const codes = ansiStyles[key];
-			return build.call(this, this._styles ? this._styles.concat(codes) : [codes], this._empty, key);
-		}
-	};
-}
-
-styles.visible = {
-	get() {
-		return build.call(this, this._styles || [], true, 'visible');
-	}
-};
-
-ansiStyles.color.closeRe = new RegExp(escapeStringRegexp(ansiStyles.color.close), 'g');
-for (const model of Object.keys(ansiStyles.color.ansi)) {
-	if (skipModels.has(model)) {
-		continue;
-	}
-
-	styles[model] = {
-		get() {
-			const level = this.level;
-			return function () {
-				const open = ansiStyles.color[levelMapping[level]][model].apply(null, arguments);
-				const codes = {
-					open,
-					close: ansiStyles.color.close,
-					closeRe: ansiStyles.color.closeRe
-				};
-				return build.call(this, this._styles ? this._styles.concat(codes) : [codes], this._empty, model);
-			};
-		}
-	};
-}
-
-ansiStyles.bgColor.closeRe = new RegExp(escapeStringRegexp(ansiStyles.bgColor.close), 'g');
-for (const model of Object.keys(ansiStyles.bgColor.ansi)) {
-	if (skipModels.has(model)) {
-		continue;
-	}
-
-	const bgModel = 'bg' + model[0].toUpperCase() + model.slice(1);
-	styles[bgModel] = {
-		get() {
-			const level = this.level;
-			return function () {
-				const open = ansiStyles.bgColor[levelMapping[level]][model].apply(null, arguments);
-				const codes = {
-					open,
-					close: ansiStyles.bgColor.close,
-					closeRe: ansiStyles.bgColor.closeRe
-				};
-				return build.call(this, this._styles ? this._styles.concat(codes) : [codes], this._empty, model);
-			};
-		}
-	};
-}
-
-const proto = Object.defineProperties(() => {}, styles);
-
-function build(_styles, _empty, key) {
-	const builder = function () {
-		return applyStyle.apply(builder, arguments);
-	};
-
-	builder._styles = _styles;
-	builder._empty = _empty;
-
-	const self = this;
-
-	Object.defineProperty(builder, 'level', {
-		enumerable: true,
-		get() {
-			return self.level;
-		},
-		set(level) {
-			self.level = level;
-		}
-	});
-
-	Object.defineProperty(builder, 'enabled', {
-		enumerable: true,
-		get() {
-			return self.enabled;
-		},
-		set(enabled) {
-			self.enabled = enabled;
-		}
-	});
-
-	// See below for fix regarding invisible grey/dim combination on Windows
-	builder.hasGrey = this.hasGrey || key === 'gray' || key === 'grey';
-
-	// `__proto__` is used because we must return a function, but there is
-	// no way to create a function with a different prototype
-	builder.__proto__ = proto; // eslint-disable-line no-proto
-
-	return builder;
-}
-
-function applyStyle() {
-	// Support varags, but simply cast to string in case there's only one arg
-	const args = arguments;
-	const argsLen = args.length;
-	let str = String(arguments[0]);
-
-	if (argsLen === 0) {
-		return '';
-	}
-
-	if (argsLen > 1) {
-		// Don't slice `arguments`, it prevents V8 optimizations
-		for (let a = 1; a < argsLen; a++) {
-			str += ' ' + args[a];
-		}
-	}
-
-	if (!this.enabled || this.level <= 0 || !str) {
-		return this._empty ? '' : str;
-	}
-
-	// Turns out that on Windows dimmed gray text becomes invisible in cmd.exe,
-	// see https://github.com/chalk/chalk/issues/58
-	// If we're on Windows and we're dealing with a gray color, temporarily make 'dim' a noop.
-	const originalDim = ansiStyles.dim.open;
-	if (isSimpleWindowsTerm && this.hasGrey) {
-		ansiStyles.dim.open = '';
-	}
-
-	for (const code of this._styles.slice().reverse()) {
-		// Replace any instances already present with a re-opening code
-		// otherwise only the part of the string until said closing code
-		// will be colored, and the rest will simply be 'plain'.
-		str = code.open + str.replace(code.closeRe, code.open) + code.close;
-
-		// Close the styling before a linebreak and reopen
-		// after next line to fix a bleed issue on macOS
-		// https://github.com/chalk/chalk/pull/92
-		str = str.replace(/\r?\n/g, `${code.close}$&${code.open}`);
-	}
-
-	// Reset the original `dim` if we changed it to work around the Windows dimmed gray issue
-	ansiStyles.dim.open = originalDim;
-
-	return str;
-}
-
-function chalkTag(chalk, strings) {
-	if (!Array.isArray(strings)) {
-		// If chalk() was called by itself or with a string,
-		// return the string itself as a string.
-		return [].slice.call(arguments, 1).join(' ');
-	}
-
-	const args = [].slice.call(arguments, 2);
-	const parts = [strings.raw[0]];
-
-	for (let i = 1; i < strings.length; i++) {
-		parts.push(String(args[i - 1]).replace(/[{}\\]/g, '\\$&'));
-		parts.push(String(strings.raw[i]));
-	}
-
-	return template(chalk, parts.join(''));
-}
-
-Object.defineProperties(Chalk.prototype, styles);
-
-module.exports = Chalk(); // eslint-disable-line new-cap
-module.exports.supportsColor = stdoutColor;
-module.exports.default = module.exports; // For TypeScript
-
-/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(120)))
-
-/***/ }),
-
-/***/ 632:
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-var matchOperatorsRe = /[|\\{}()[\]^$+*?.]/g;
-
-module.exports = function (str) {
-	if (typeof str !== 'string') {
-		throw new TypeError('Expected a string');
-	}
-
-	return str.replace(matchOperatorsRe, '\\$&');
-};
-
-
-/***/ }),
-
-/***/ 633:
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-/* WEBPACK VAR INJECTION */(function(module) {
-const colorConvert = __webpack_require__(2388);
-
-const wrapAnsi16 = (fn, offset) => function () {
-	const code = fn.apply(colorConvert, arguments);
-	return `\u001B[${code + offset}m`;
-};
-
-const wrapAnsi256 = (fn, offset) => function () {
-	const code = fn.apply(colorConvert, arguments);
-	return `\u001B[${38 + offset};5;${code}m`;
-};
-
-const wrapAnsi16m = (fn, offset) => function () {
-	const rgb = fn.apply(colorConvert, arguments);
-	return `\u001B[${38 + offset};2;${rgb[0]};${rgb[1]};${rgb[2]}m`;
-};
-
-function assembleStyles() {
-	const codes = new Map();
-	const styles = {
-		modifier: {
-			reset: [0, 0],
-			// 21 isn't widely supported and 22 does the same thing
-			bold: [1, 22],
-			dim: [2, 22],
-			italic: [3, 23],
-			underline: [4, 24],
-			inverse: [7, 27],
-			hidden: [8, 28],
-			strikethrough: [9, 29]
-		},
-		color: {
-			black: [30, 39],
-			red: [31, 39],
-			green: [32, 39],
-			yellow: [33, 39],
-			blue: [34, 39],
-			magenta: [35, 39],
-			cyan: [36, 39],
-			white: [37, 39],
-			gray: [90, 39],
-
-			// Bright color
-			redBright: [91, 39],
-			greenBright: [92, 39],
-			yellowBright: [93, 39],
-			blueBright: [94, 39],
-			magentaBright: [95, 39],
-			cyanBright: [96, 39],
-			whiteBright: [97, 39]
-		},
-		bgColor: {
-			bgBlack: [40, 49],
-			bgRed: [41, 49],
-			bgGreen: [42, 49],
-			bgYellow: [43, 49],
-			bgBlue: [44, 49],
-			bgMagenta: [45, 49],
-			bgCyan: [46, 49],
-			bgWhite: [47, 49],
-
-			// Bright color
-			bgBlackBright: [100, 49],
-			bgRedBright: [101, 49],
-			bgGreenBright: [102, 49],
-			bgYellowBright: [103, 49],
-			bgBlueBright: [104, 49],
-			bgMagentaBright: [105, 49],
-			bgCyanBright: [106, 49],
-			bgWhiteBright: [107, 49]
-		}
-	};
-
-	// Fix humans
-	styles.color.grey = styles.color.gray;
-
-	for (const groupName of Object.keys(styles)) {
-		const group = styles[groupName];
-
-		for (const styleName of Object.keys(group)) {
-			const style = group[styleName];
-
-			styles[styleName] = {
-				open: `\u001B[${style[0]}m`,
-				close: `\u001B[${style[1]}m`
-			};
-
-			group[styleName] = styles[styleName];
-
-			codes.set(style[0], style[1]);
-		}
-
-		Object.defineProperty(styles, groupName, {
-			value: group,
-			enumerable: false
-		});
-
-		Object.defineProperty(styles, 'codes', {
-			value: codes,
-			enumerable: false
-		});
-	}
-
-	const ansi2ansi = n => n;
-	const rgb2rgb = (r, g, b) => [r, g, b];
-
-	styles.color.close = '\u001B[39m';
-	styles.bgColor.close = '\u001B[49m';
-
-	styles.color.ansi = {
-		ansi: wrapAnsi16(ansi2ansi, 0)
-	};
-	styles.color.ansi256 = {
-		ansi256: wrapAnsi256(ansi2ansi, 0)
-	};
-	styles.color.ansi16m = {
-		rgb: wrapAnsi16m(rgb2rgb, 0)
-	};
-
-	styles.bgColor.ansi = {
-		ansi: wrapAnsi16(ansi2ansi, 10)
-	};
-	styles.bgColor.ansi256 = {
-		ansi256: wrapAnsi256(ansi2ansi, 10)
-	};
-	styles.bgColor.ansi16m = {
-		rgb: wrapAnsi16m(rgb2rgb, 10)
-	};
-
-	for (let key of Object.keys(colorConvert)) {
-		if (typeof colorConvert[key] !== 'object') {
-			continue;
-		}
-
-		const suite = colorConvert[key];
-
-		if (key === 'ansi16') {
-			key = 'ansi';
-		}
-
-		if ('ansi16' in suite) {
-			styles.color.ansi[key] = wrapAnsi16(suite.ansi16, 0);
-			styles.bgColor.ansi[key] = wrapAnsi16(suite.ansi16, 10);
-		}
-
-		if ('ansi256' in suite) {
-			styles.color.ansi256[key] = wrapAnsi256(suite.ansi256, 0);
-			styles.bgColor.ansi256[key] = wrapAnsi256(suite.ansi256, 10);
-		}
-
-		if ('rgb' in suite) {
-			styles.color.ansi16m[key] = wrapAnsi16m(suite.rgb, 0);
-			styles.bgColor.ansi16m[key] = wrapAnsi16m(suite.rgb, 10);
-		}
-	}
-
-	return styles;
-}
-
-// Make the export immutable
-Object.defineProperty(module, 'exports', {
-	enumerable: true,
-	get: assembleStyles
-});
-
-/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(793)(module)))
-
-/***/ }),
-
 /***/ 67:
 /***/ (function(module, exports, __webpack_require__) {
 
 var baseGet = __webpack_require__(68);
 
 /**
  * Gets the value at `path` of `object`. If the resolved value is
  * `undefined`, the `defaultValue` is returned in its place.
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/BreakpointsContextMenu.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/BreakpointsContextMenu.js
@@ -69,24 +69,24 @@ export default function showContextMenu(
   );
   const editConditionKey = L10N.getStr(
     "breakpointMenuItem.editCondition2.accesskey"
   );
   const addConditionKey = L10N.getStr(
     "breakpointMenuItem.addCondition2.accesskey"
   );
 
-  const otherBreakpoints = breakpoints.filter(b => b !== breakpoint);
+  const otherBreakpoints = breakpoints.filter(b => b.id !== breakpoint.id);
   const enabledBreakpoints = breakpoints.filter(b => !b.disabled);
   const disabledBreakpoints = breakpoints.filter(b => b.disabled);
   const otherEnabledBreakpoints = breakpoints.filter(
-    b => !b.disabled && b !== breakpoint
+    b => !b.disabled && b.id !== breakpoint.id
   );
   const otherDisabledBreakpoints = breakpoints.filter(
-    b => b.disabled && b !== breakpoint
+    b => b.disabled && b.id !== breakpoint.id
   );
 
   const deleteSelfItem = {
     id: "node-menu-delete-self",
     label: deleteSelfLabel,
     accesskey: deleteSelfKey,
     disabled: false,
     click: () => removeBreakpoint(breakpoint.selectedLocation)
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Frames/Group.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Frames/Group.js
@@ -1,14 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 import React, { Component } from "react";
+import PropTypes from "prop-types";
 import classNames from "classnames";
 import Svg from "../../shared/Svg";
 import {
   getLibraryFromUrl,
   formatDisplayName
 } from "../../../utils/pause/frames";
 import FrameMenu from "./FrameMenu";
 
@@ -122,22 +123,30 @@ export default class Group extends Compo
         ))}
       </div>
     );
   }
 
   renderDescription() {
     const frame = this.props.group[0];
     const displayName = formatDisplayName(frame);
+
+    const l10NEntry = this.state.expanded
+      ? "callStack.group.collapseTooltip"
+      : "callStack.group.expandTooltip";
+    const { l10n } = this.context;
+    const title = l10n.getFormatStr(l10NEntry, frame.library);
+
     return (
       <li
         key={frame.id}
         className={classNames("group")}
         onClick={this.toggleFrames}
         tabIndex={0}
+        title={title}
       >
         <span className="title">{displayName}</span>
         <Badge>{this.props.group.length}</Badge>
         <FrameLocation frame={frame} />
       </li>
     );
   }
 
@@ -152,8 +161,9 @@ export default class Group extends Compo
         {this.renderDescription()}
         {this.renderFrames()}
       </div>
     );
   }
 }
 
 Group.displayName = "Group";
+Group.contextTypes = { l10n: PropTypes.object };
--- a/devtools/client/debugger/new/src/reducers/breakpoints.js
+++ b/devtools/client/debugger/new/src/reducers/breakpoints.js
@@ -72,17 +72,17 @@ function update(
       return removeBreakpoint(state, action);
     }
 
     case "REMAP_BREAKPOINTS": {
       return remapBreakpoints(state, action);
     }
 
     case "NAVIGATE": {
-      return initialBreakpointsState();
+      return initialBreakpointsState(state.xhrBreakpoints);
     }
 
     case "SET_XHR_BREAKPOINT": {
       return addXHRBreakpoint(state, action);
     }
 
     case "REMOVE_XHR_BREAKPOINT": {
       return removeXHRBreakpoint(state, action);
--- a/devtools/client/debugger/new/src/selectors/breakpointSources.js
+++ b/devtools/client/debugger/new/src/selectors/breakpointSources.js
@@ -9,38 +9,40 @@ import { createSelector } from "reselect
 import {
   getSources,
   getBreakpointsList,
   getSelectedSource
 } from "../selectors";
 import { isGenerated, getFilename } from "../utils/source";
 import { getSelectedLocation } from "../utils/source-maps";
 
-import type { Source, Breakpoint, Location } from "../types";
+import type { Source, Breakpoint, BreakpointId, Location } from "../types";
 import type { SourcesMap } from "../reducers/types";
 
 export type BreakpointSources = Array<{
   source: Source,
   breakpoints: FormattedBreakpoint[]
 }>;
 
 export type FormattedBreakpoint = {|
+  id: BreakpointId,
   condition: ?string,
   disabled: boolean,
   text: string,
   selectedLocation: Location
 |};
 
 function formatBreakpoint(
   breakpoint: Breakpoint,
   selectedSource: Source
 ): FormattedBreakpoint {
-  const { condition, disabled } = breakpoint;
+  const { id, condition, disabled } = breakpoint;
 
   return {
+    id,
     condition,
     disabled,
     text:
       selectedSource && isGenerated(selectedSource)
         ? breakpoint.text
         : breakpoint.originalText,
     selectedLocation: getSelectedLocation(breakpoint, selectedSource)
   };
--- a/devtools/client/debugger/new/src/utils/pause/frames/getLibraryFromUrl.js
+++ b/devtools/client/debugger/new/src/utils/pause/frames/getLibraryFromUrl.js
@@ -73,17 +73,18 @@ const libraryMap = [
     pattern: /vue(?:\.[a-z]+)*\.js/i
   },
   {
     label: "RxJS",
     pattern: /rxjs/i
   },
   {
     label: "Angular",
-    pattern: /angular/i
+    pattern: /angular/i,
+    contextPattern: /(zone\.js)/
   },
   {
     label: "Redux",
     pattern: /redux/i
   },
   {
     label: "Dojo",
     pattern: /dojo/i
@@ -101,19 +102,39 @@ const libraryMap = [
     pattern: /aframe/i
   },
   {
     label: "NextJS",
     pattern: /[\._]next/i
   }
 ];
 
-export function getLibraryFromUrl(frame: Frame) {
+export function getLibraryFromUrl(frame: Frame, callStack: Array<Frame> = []) {
   // @TODO each of these fns calls getFrameUrl, just call it once
   // (assuming there's not more complex logic to identify a lib)
   const frameUrl = getFrameUrl(frame);
-  const matches = libraryMap.filter(o => frameUrl.match(o.pattern));
-  if (matches.length == 0) {
-    return null;
+
+  // Let's first check if the frame match a defined pattern.
+  let match = libraryMap.find(o => frameUrl.match(o.pattern));
+  if (match) {
+    return match.label;
   }
 
-  return matches[0].label;
+  // If it does not, it might still be one of the case where the file is used
+  // by a library but the name has not enough specificity. In such case, we want
+  // to only return the library name if there are frames matching the library
+  // pattern in the callStack (e.g. `zone.js` is used by Angular, but the name
+  //  could be quite common and return false positive if evaluated alone. So we
+  // only return Angular if there are other frames matching Angular).
+  match = libraryMap.find(
+    o => o.contextPattern && frameUrl.match(o.contextPattern)
+  );
+  if (match) {
+    const contextMatch = callStack.some(f =>
+      libraryMap.find(o => frameUrl.match(o.pattern))
+    );
+    if (contextMatch) {
+      return match.label;
+    }
+  }
+
+  return null;
 }
--- a/devtools/client/debugger/new/test/mochitest/browser.ini
+++ b/devtools/client/debugger/new/test/mochitest/browser.ini
@@ -741,16 +741,17 @@ skip-if = os == 'linux' && !asan # bug 1
 [browser_dbg-sources-arrow-keys.js]
 [browser_dbg-sources-named-eval.js]
 [browser_dbg-sources-querystring.js]
 skip-if = true
 [browser_dbg-stepping.js]
 skip-if = debug || (verify && (os == 'win')) || (os == "win" && os_version == "6.1")
 [browser_dbg-tabs.js]
 [browser_dbg-tabs-pretty-print.js]
+[browser_dbg-tabs-without-urls.js]
 [browser_dbg-toggling-tools.js]
 [browser_dbg-react-app.js]
 skip-if = os == "win"
 [browser_dbg-wasm-sourcemaps.js]
 skip-if = true
 [browser_dbg_rr_breakpoints-01.js]
 skip-if = os != "mac" || debug || !nightly_build
 [browser_dbg_rr_breakpoints-02.js]
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-breakpoints-actions.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-breakpoints-actions.js
@@ -1,21 +1,70 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
-async function removeBreakpoint(dbg) {
-  rightClickElement(dbg, "breakpointItem", 3)
-  selectMenuItem(dbg, 1);
+function openFirstBreakpointContextMenu(dbg){
+  rightClickElement(dbg, "breakpointItem", 3);
 }
 
 
 // Tests to see if we can trigger a breakpoint action via the context menu
 add_task(async function() {
   const dbg = await initDebugger("doc-scripts.html");
   await selectSource(dbg, "simple2");
   await waitForSelectedSource(dbg, "simple2");
 
   await addBreakpoint(dbg, "simple2", 3);
-  await removeBreakpoint(dbg);
+
+  openFirstBreakpointContextMenu(dbg)
+  // select "Remove breakpoint"
+  selectMenuItem(dbg, 1);
 
   await waitForState(dbg, state => dbg.selectors.getBreakpointCount(state) === 0);
-  ok("successfully removed the breakpoint")
+  ok("successfully removed the breakpoint");
 });
+
+// Tests "disable others", "enable others" and "remove others" context actions
+add_task(async function() {
+  const dbg = await initDebugger("doc-scripts.html");
+  await selectSource(dbg, "simple1");
+  await waitForSelectedSource(dbg, "simple1");
+
+  await addBreakpoint(dbg, "simple1", 1);
+  await addBreakpoint(dbg, "simple1", 4);
+  await addBreakpoint(dbg, "simple1", 5);
+  await addBreakpoint(dbg, "simple1", 6);
+
+  openFirstBreakpointContextMenu(dbg)
+  // select "Disable Others"
+  selectMenuItem(dbg, 7);
+  await waitForState(dbg, state =>
+    dbg.selectors.getBreakpointsList(state)
+      .every(bp => (bp.location.line !== 1) === bp.disabled)
+  );
+  ok("breakpoint at 1 is the only enabled breakpoint");
+
+  openFirstBreakpointContextMenu(dbg)
+  // select "Disable All"
+  selectMenuItem(dbg, 9);
+  await waitForState(dbg, state =>
+    dbg.selectors.getBreakpointsList(state).every(bp => bp.disabled)
+  );
+  ok("all breakpoints are disabled")
+
+  openFirstBreakpointContextMenu(dbg)
+  // select "Enable Others"
+  selectMenuItem(dbg, 3);
+  await waitForState(dbg, state =>
+    dbg.selectors.getBreakpointsList(state)
+      .every(bp => (bp.location.line === 1) === bp.disabled)
+  );
+  ok("all breakpoints except line 1 are enabled");
+
+  openFirstBreakpointContextMenu(dbg)
+  // select "Remove Others"
+  selectMenuItem(dbg, 6);
+  await waitForState(dbg, state =>
+    dbg.selectors.getBreakpointsList(state).length === 1 &&
+    dbg.selectors.getBreakpointsList(state)[0].location.line === 1
+  );
+  ok("remaining breakpoint should be on line 1");
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-tabs-without-urls.js
@@ -0,0 +1,31 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function countTabs(dbg) {
+  return findElement(dbg, "sourceTabs").children.length;
+}
+
+// Test that URL-less sources have tabs added to the UI but 
+// do not persist upon reload
+add_task(async function() {
+  const dbg = await initDebugger("doc-scripts.html", "simple1", "simple2");
+
+  await selectSource(dbg, "simple1");
+  await selectSource(dbg, "simple2");
+
+  is(countTabs(dbg), 2);
+
+  invokeInTab("doEval");
+  await waitForPaused(dbg);
+  await resume(dbg);
+  is(countTabs(dbg), 3);
+
+  invokeInTab("doEval");
+  await waitForPaused(dbg);
+  await resume(dbg);
+  is(countTabs(dbg), 4);
+
+  // Test reloading the debugger
+  await reload(dbg, "simple1", "simple2");
+  is(countTabs(dbg), 2);
+});
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-tabs.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-tabs.js
@@ -32,33 +32,8 @@ add_task(async function() {
   await selectSource(dbg, "simple2");
   await closeTab(dbg, "simple1");
   await closeTab(dbg, "simple2");
 
   // Test reloading the debugger
   await reload(dbg, "simple1", "simple2");
   is(countTabs(dbg), 0);
 });
-
-// Test that URL-less sources have tabs added to the UI but 
-// do not persist upon reload
-add_task(async function() {
-  const dbg = await initDebugger("doc-scripts.html", "simple1", "simple2");
-
-  await selectSource(dbg, "simple1");
-  await selectSource(dbg, "simple2");
-
-  is(countTabs(dbg), 2);
-
-  invokeInTab("doEval");
-  await waitForPaused(dbg);
-  await resume(dbg);
-  is(countTabs(dbg), 3);
-
-  invokeInTab("doEval");
-  await waitForPaused(dbg);
-  await resume(dbg);
-  is(countTabs(dbg), 4);
-
-  // Test reloading the debugger
-  await reload(dbg, "simple1", "simple2");
-  is(countTabs(dbg), 2);
-});
--- a/devtools/client/locales/en-US/debugger.properties
+++ b/devtools/client/locales/en-US/debugger.properties
@@ -411,16 +411,30 @@ callStack.notPaused=Not paused
 # LOCALIZATION NOTE (callStack.collapse): Call Stack right sidebar pane
 # message to hide some of the frames that are shown.
 callStack.collapse=Collapse rows
 
 # LOCALIZATION NOTE (callStack.expand): Call Stack right sidebar pane
 # message to show more of the frames.
 callStack.expand=Expand rows
 
+# LOCALIZATION NOTE (callStack.group.expandTooltip): The text that will appear
+# when hovering a collapsed Group of frames in the callStack panel. `frames` is
+# always plural since a group can only exist if it contain more that 1 frame.
+# %S is replaced by the name of the library of the frames in the group.
+# example: `Show React frames`.
+callStack.group.expandTooltip=Show %S frames
+
+# LOCALIZATION NOTE (callStack.group.collapseTooltip): The text that will appear
+# when hovering an expanded Group of frames in the callStack panel. `frames` is
+# always plural since a group can only exist if it contain more that 1 frame.
+# %S is replaced by the name of the library of the frames in the group.
+# example: `Collapse React frames`.
+callStack.group.collapseTooltip=Collapse %S frames
+
 # LOCALIZATION NOTE (components.header): Header for the
 # Framework Components pane in the right sidebar.
 components.header=Components
 
 # LOCALIZATION NOTE (editor.searchResults): Editor Search bar message
 # for the summarizing the selected search result. e.g. 5 of 10 results.
 editor.searchResults=%d of %d results
 
--- a/devtools/shared/builtin-modules.js
+++ b/devtools/shared/builtin-modules.js
@@ -17,16 +17,17 @@ const { Cu, CC, Cc, Ci } = require("chro
 const promise = require("resource://gre/modules/Promise.jsm").Promise;
 const jsmScope = require("resource://devtools/shared/Loader.jsm");
 const { Services } = jsmScope;
 // Steal various globals only available in JSM scope (and not Sandbox one)
 const {
   console,
   DOMPoint,
   DOMQuad,
+  DOMRect,
   HeapSnapshot,
   StructuredCloneHolder,
   TelemetryStopwatch,
 } = Cu.getGlobalForObject(jsmScope);
 
 // Create a single Sandbox to access global properties needed in this module.
 // Sandbox are memory expensive, so we should create as little as possible.
 const {
@@ -282,16 +283,17 @@ exports.globals = {
   // Bug 1248830 will work out a better plan here for our content module
   // loading needs, especially as we head towards devtools.html.
   define(factory) {
     factory(this.require, this.exports, this.module);
   },
   DOMParser,
   DOMPoint,
   DOMQuad,
+  DOMRect,
   Element,
   Event,
   FormData,
   isWorker: false,
   loader: {
     lazyGetter: defineLazyGetter,
     lazyImporter: defineLazyModuleGetter,
     lazyServiceGetter: defineLazyServiceGetter,
--- a/dom/base/ChromeUtils.cpp
+++ b/dom/base/ChromeUtils.cpp
@@ -650,32 +650,39 @@ ChromeUtils::ClearRecentJSDevError(Globa
   MOZ_ASSERT(runtime);
 
   runtime->ClearRecentDevError();
 }
 #endif // NIGHTLY_BUILD
 
 /* static */
 already_AddRefed<Promise>
-ChromeUtils::RequestPerformanceMetrics(GlobalObject& aGlobal,
-                                       ErrorResult& aRv)
+ChromeUtils::RequestPerformanceMetrics(GlobalObject& aGlobal, ErrorResult& aRv)
 {
   MOZ_ASSERT(XRE_IsParentProcess());
 
-  // Creating a promise
+  // Creating a JS promise
   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
   MOZ_ASSERT(global);
   RefPtr<Promise> domPromise = Promise::Create(global, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
   MOZ_ASSERT(domPromise);
+  RefPtr<nsISerialEventTarget> target =
+    global->EventTargetFor(TaskCategory::Performance);
 
   // requesting metrics, that will be returned into the promise
-  PerformanceMetricsCollector::RequestMetrics(domPromise);
+  PerformanceMetricsCollector::RequestMetrics()->Then(
+    target,
+    __func__,
+    [domPromise, target](nsTArray<dom::PerformanceInfoDictionary>&& aResults) {
+      domPromise->MaybeResolve(std::move(aResults));
+    },
+    [domPromise](const nsresult& aRv) { domPromise->MaybeReject(aRv); });
 
   // sending back the promise instance
   return domPromise.forget();
 }
 
 constexpr auto kSkipSelfHosted = JS::SavedFrameSelfHosted::Exclude;
 
 /* static */ void
--- a/dom/base/DocGroup.cpp
+++ b/dom/base/DocGroup.cpp
@@ -2,16 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/DocGroup.h"
 #include "mozilla/dom/DOMTypes.h"
 #include "mozilla/dom/TabGroup.h"
+#include "mozilla/PerformanceUtils.h"
 #include "mozilla/StaticPrefs.h"
 #include "mozilla/Telemetry.h"
 #include "nsIDocShell.h"
 #include "nsDOMMutationObserver.h"
 #if defined(XP_WIN)
 #include <processthreadsapi.h>  // for GetCurrentProcessId()
 #else
 #include <unistd.h> // for getpid()
@@ -63,31 +64,33 @@ DocGroup::~DocGroup()
   if (!NS_IsMainThread()) {
     nsIEventTarget* target = EventTargetFor(TaskCategory::Other);
     NS_ProxyRelease("DocGroup::mReactionsStack", target, mReactionsStack.forget());
   }
 
   mTabGroup->mDocGroups.RemoveEntry(mKey);
 }
 
-PerformanceInfo
+RefPtr<PerformanceInfoPromise>
 DocGroup::ReportPerformanceInfo()
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(mPerformanceCounter);
 #if defined(XP_WIN)
   uint32_t pid = GetCurrentProcessId();
 #else
   uint32_t pid = getpid();
 #endif
   uint64_t windowID = 0;
   uint16_t count = 0;
   uint64_t duration = 0;
   bool isTopLevel = false;
   nsCString host;
+  nsCOMPtr<nsPIDOMWindowOuter> top;
+  RefPtr<AbstractThread> mainThread;
 
   // iterating on documents until we find the top window
   for (const auto& document : *this) {
     nsCOMPtr<nsIDocument> doc = document;
     MOZ_ASSERT(doc);
     nsCOMPtr<nsIURI> docURI = doc->GetDocumentURI();
     if (!docURI) {
       continue;
@@ -101,22 +104,23 @@ DocGroup::ReportPerformanceInfo()
     nsPIDOMWindowOuter* win = doc->GetWindow();
     if (!win) {
       continue;
     }
     nsPIDOMWindowOuter* outer = win->GetOuterWindow();
     if (!outer) {
       continue;
     }
-    nsCOMPtr<nsPIDOMWindowOuter> top = outer->GetTop();
+    top = outer->GetTop();
     if (!top) {
       continue;
     }
     windowID = top->WindowID();
     isTopLevel = outer->IsTopLevelWindow();
+    mainThread = AbstractMainThreadFor(TaskCategory::Performance);
     break;
   }
 
   MOZ_ASSERT(!host.IsEmpty());
   duration = mPerformanceCounter->GetExecutionDuration();
   FallibleTArray<CategoryDispatch> items;
 
   // now that we have the host and window ids, let's look at the perf counters
@@ -125,18 +129,55 @@ DocGroup::ReportPerformanceInfo()
     count = mPerformanceCounter->GetDispatchCount(DispatchCategory(category));
     CategoryDispatch item = CategoryDispatch(index, count);
     if (!items.AppendElement(item, fallible)) {
       NS_ERROR("Could not complete the operation");
       break;
     }
   }
 
-  return PerformanceInfo(host, pid, windowID, duration, mPerformanceCounter->GetID(),
-                         false, isTopLevel, items);
+  if (!isTopLevel) {
+    return PerformanceInfoPromise::CreateAndResolve(
+      PerformanceInfo(host,
+                      pid,
+                      windowID,
+                      duration,
+                      mPerformanceCounter->GetID(),
+                      false,
+                      isTopLevel,
+                      PerformanceMemoryInfo(), // Empty memory info
+                      items),
+      __func__);
+  }
+
+  MOZ_ASSERT(mainThread);
+  RefPtr<DocGroup> self = this;
+
+  return CollectMemoryInfo(top, mainThread)
+    ->Then(mainThread,
+           __func__,
+           [self, host, pid, windowID, duration, isTopLevel, items](
+             const PerformanceMemoryInfo& aMemoryInfo) {
+             PerformanceInfo info =
+               PerformanceInfo(host,
+                               pid,
+                               windowID,
+                               duration,
+                               self->mPerformanceCounter->GetID(),
+                               false,
+                               isTopLevel,
+                               aMemoryInfo,
+                               items);
+
+             return PerformanceInfoPromise::CreateAndResolve(std::move(info),
+                                                             __func__);
+           },
+           [self](const nsresult rv) {
+             return PerformanceInfoPromise::CreateAndReject(rv, __func__);
+           });
 }
 
 nsresult
 DocGroup::Dispatch(TaskCategory aCategory,
                    already_AddRefed<nsIRunnable>&& aRunnable)
 {
   if (mPerformanceCounter) {
     mPerformanceCounter->IncrementDispatchCounter(DispatchCategory(aCategory));
--- a/dom/base/DocGroup.h
+++ b/dom/base/DocGroup.h
@@ -12,24 +12,22 @@
 #include "nsTHashtable.h"
 #include "nsString.h"
 
 #include "mozilla/dom/TabGroup.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/dom/CustomElementRegistry.h"
 #include "mozilla/dom/HTMLSlotElement.h"
 #include "mozilla/PerformanceCounter.h"
-
+#include "mozilla/PerformanceTypes.h"
 
 namespace mozilla {
 class AbstractThread;
 namespace dom {
 
-class PerformanceInfo;
-
 // Two browsing contexts are considered "related" if they are reachable from one
 // another through window.opener, window.parent, or window.frames. This is the
 // spec concept of a "unit of related browsing contexts"
 //
 // Two browsing contexts are considered "similar-origin" if they can be made to
 // have the same origin by setting document.domain. This is the spec concept of
 // a "unit of similar-origin related browsing contexts"
 //
@@ -59,18 +57,17 @@ public:
     return aKey == mKey;
   }
 
   PerformanceCounter* GetPerformanceCounter()
   {
     return mPerformanceCounter;
   }
 
-  PerformanceInfo
-  ReportPerformanceInfo();
+  RefPtr<PerformanceInfoPromise> ReportPerformanceInfo();
 
   TabGroup* GetTabGroup()
   {
     return mTabGroup;
   }
   mozilla::dom::CustomElementReactionsStack* CustomElementReactionsStack()
   {
     MOZ_ASSERT(NS_IsMainThread());
--- a/dom/base/nsINode.cpp
+++ b/dom/base/nsINode.cpp
@@ -2801,53 +2801,50 @@ nsINode::ParseSelectorList(const nsAStri
     );
   }
 
   auto* ret = selectorList.get();
   cache.CacheList(aSelectorString, std::move(selectorList));
   return ret;
 }
 
-namespace {
-struct SelectorMatchInfo {
-};
-} // namespace
-
 // Given an id, find first element with that id under aRoot.
 // If none found, return nullptr. aRoot must be in the document.
 inline static Element*
-FindMatchingElementWithId(const nsAString& aId, nsINode* aRoot)
+FindMatchingElementWithId(const nsAString& aId,
+                          const Element& aRoot,
+                          const DocumentOrShadowRoot& aContainingDocOrShadowRoot)
 {
-  MOZ_ASSERT(aRoot->IsInUncomposedDoc(),
-             "Don't call me if the root is not in the document");
-  // FIXME(emilio): It'd be nice to optimize this for shadow roots too.
-  MOZ_ASSERT(aRoot->IsElement() || aRoot->IsDocument(),
-             "The optimization below to check ContentIsDescendantOf only for "
-             "elements depends on aRoot being either an element or a "
-             "document if it's in the document.  Note that document fragments "
-             "can't be IsInUncomposedDoc(), so should never show up here.");
-
-  const nsTArray<Element*>* elements = aRoot->OwnerDoc()->GetAllElementsForId(aId);
+  MOZ_ASSERT(aRoot.SubtreeRoot() == &aContainingDocOrShadowRoot.AsNode());
+  MOZ_ASSERT(aRoot.IsInUncomposedDoc() || aRoot.IsInShadowTree(),
+             "Don't call me if the root is not in the document or in a shadow tree");
+
+  const nsTArray<Element*>* elements =
+    aContainingDocOrShadowRoot.GetAllElementsForId(aId);
   if (!elements) {
     // Nothing to do; we're done
     return nullptr;
   }
 
-  // XXXbz: Should we fall back to the tree walk if aRoot is not the
-  // document and |elements| is long, for some value of "long"?
-  for (size_t i = 0; i < elements->Length(); ++i) {
-    Element* element = (*elements)[i];
-    if (!aRoot->IsElement() ||
-        (element != aRoot &&
-           nsContentUtils::ContentIsDescendantOf(element, aRoot))) {
-      // We have an element with the right id and it's a strict descendant
-      // of aRoot.
-      return element;
+  // XXXbz: Should we fall back to the tree walk if |elements| is long,
+  // for some value of "long"?
+  for (Element* element : *elements) {
+    if (MOZ_UNLIKELY(element == &aRoot)) {
+      continue;
     }
+
+    if (!nsContentUtils::ContentIsDescendantOf(element, &aRoot)) {
+      continue;
+    }
+
+    // We have an element with the right id and it's a strict descendant
+    // of aRoot.
+    return element;
   }
+
   return nullptr;
 }
 
 Element*
 nsINode::QuerySelector(const nsAString& aSelector, ErrorResult& aResult)
 {
   AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING(
       "nsINode::QuerySelector", DOM, aSelector);
@@ -2876,20 +2873,27 @@ nsINode::QuerySelectorAll(const nsAStrin
   const bool useInvalidation = false;
   Servo_SelectorList_QueryAll(this, list, contentList.get(), useInvalidation);
   return contentList.forget();
 }
 
 Element*
 nsINode::GetElementById(const nsAString& aId)
 {
+  MOZ_ASSERT(!IsShadowRoot(), "Should use the faster version");
   MOZ_ASSERT(IsElement() || IsDocumentFragment(),
              "Bogus this object for GetElementById call");
   if (IsInUncomposedDoc()) {
-    return FindMatchingElementWithId(aId, this);
+    MOZ_ASSERT(IsElement(), "Huh? A fragment in a document?");
+    return FindMatchingElementWithId(aId, *AsElement(), *OwnerDoc());
+  }
+
+  if (ShadowRoot* containingShadow = AsContent()->GetContainingShadow()) {
+    MOZ_ASSERT(IsElement(), "Huh? A fragment in a ShadowRoot?");
+    return FindMatchingElementWithId(aId, *AsElement(), *containingShadow);
   }
 
   for (nsIContent* kid = GetFirstChild(); kid; kid = kid->GetNextNode(this)) {
     if (!kid->IsElement()) {
       continue;
     }
     nsAtom* id = kid->AsElement()->GetID();
     if (id && id->Equals(aId)) {
--- a/dom/chrome-webidl/ChromeUtils.webidl
+++ b/dom/chrome-webidl/ChromeUtils.webidl
@@ -365,30 +365,46 @@ partial namespace ChromeUtils {
   [ChromeOnly]
   sequence<BrowsingContext> getRootBrowsingContexts();
 };
 
 /**
  * Dictionaries duplicating IPDL types in dom/ipc/DOMTypes.ipdlh
  * Used by requestPerformanceMetrics
  */
+
+dictionary MediaMemoryInfoDictionary {
+  unsigned long long audioSize = 0;
+  unsigned long long videoSize = 0;
+  unsigned long long resourcesSize = 0;
+};
+
+dictionary MemoryInfoDictionary {
+  unsigned long long domDom = 0;
+  unsigned long long domStyle = 0;
+  unsigned long long domOther = 0;
+  unsigned long long GCHeapUsage = 0;
+  required MediaMemoryInfoDictionary media;
+};
+
 dictionary CategoryDispatchDictionary
 {
   unsigned short category = 0;
   unsigned short count = 0;
 };
 
 dictionary PerformanceInfoDictionary {
   ByteString host = "";
   unsigned long pid = 0;
   unsigned long long windowId = 0;
   unsigned long long duration = 0;
   unsigned long long counterId = 0;
   boolean isWorker = false;
   boolean isTopLevel = false;
+  required MemoryInfoDictionary memoryInfo;
   sequence<CategoryDispatchDictionary> items = [];
 };
 
 /**
  * Used by requestIOActivity() to return the number of bytes
  * that were read (rx) and/or written (tx) for a given location.
  *
  * Locations can be sockets or files.
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -656,23 +656,19 @@ EventStateManager::PreHandleEvent(nsPres
       aEvent->mMessage = eVoidEvent;
       break;
     }
     MOZ_FALLTHROUGH;
   case eMouseMove:
   case ePointerDown:
     if (aEvent->mMessage == ePointerDown) {
       PointerEventHandler::ImplicitlyCapturePointer(aTargetFrame, aEvent);
-#ifndef MOZ_WIDGET_ANDROID
-      // Pointer events aren't enabled on Android yet, but when they
-      // are enabled, we should not activate on pointerdown, as that
-      // fires for touches that turn into moves on Android, and we don't
-      // want to gesture activate for scroll actions.
-      NotifyTargetUserActivation(aEvent, aTargetContent);
-#endif
+      if (mouseEvent->inputSource != MouseEvent_Binding::MOZ_SOURCE_TOUCH) {
+        NotifyTargetUserActivation(aEvent, aTargetContent);
+      }
     }
     MOZ_FALLTHROUGH;
   case ePointerMove: {
     // on the Mac, GenerateDragGesture() may not return until the drag
     // has completed and so |aTargetFrame| may have been deleted (moving
     // a bookmark, for example).  If this is the case, however, we know
     // that ClearFrameRefs() has been called and it cleared out
     // |mCurrentTarget|. As a result, we should pass |mCurrentTarget|
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -1405,19 +1405,31 @@ ContentChild::GetResultForRenderingInitF
   gfxCriticalNote << "Could not initialize rendering with GPU process";
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 ContentChild::RecvRequestPerformanceMetrics(const nsID& aID)
 {
   MOZ_ASSERT(mozilla::StaticPrefs::dom_performance_enable_scheduler_timing());
-  nsTArray<PerformanceInfo> info;
-  CollectPerformanceInfo(info);
-  SendAddPerformanceMetrics(aID, info);
+  RefPtr<ContentChild> self = this;
+  RefPtr<AbstractThread> mainThread = SystemGroup::AbstractMainThreadFor(
+    TaskCategory::Performance);
+  nsTArray<RefPtr<PerformanceInfoPromise>> promises = CollectPerformanceInfo();
+
+  PerformanceInfoPromise::All(mainThread, promises)
+    ->Then(mainThread,
+           __func__,
+           [self, aID](const nsTArray<mozilla::dom::PerformanceInfo>& aResult) {
+             self->SendAddPerformanceMetrics(aID, aResult);
+           },
+           []() {  /* silently fails -- the parent times out
+                      and proceeds when the data is not coming back */}
+          );
+
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 ContentChild::RecvInitRendering(Endpoint<PCompositorManagerChild>&& aCompositor,
                                 Endpoint<PImageBridgeChild>&& aImageBridge,
                                 Endpoint<PVRManagerChild>&& aVRBridge,
                                 Endpoint<PVideoDecoderManagerChild>&& aVideoManager,
--- a/dom/ipc/DOMTypes.ipdlh
+++ b/dom/ipc/DOMTypes.ipdlh
@@ -120,25 +120,44 @@ struct CreatedWindowInfo
   uint32_t maxTouchPoints;
   DimensionInfo dimensions;
   bool hasSiblings;
 };
 
 
 /**
  * PerformanceInfo is used to pass performance info stored
- * in WorkerPrivate & DocGroup instances
+ * in WorkerPrivate and DocGroup instances, as well as
+ * memory-related information.
  *
  * Each (host, pid, windowId) is unique to a given DocGroup or
  * Worker, and we collect the number of dispatches per Dispatch
- * category and total execution duration.
+ * category and total execution duration as well as the current
+ * Zone JS Heap usage.
  *
- * This IPDL struct reflects the data collected in Performance counters.
+ * This IPDL struct reflects the data collected in Performance counters,
+ * in addition of some memory usage information.
+ *
  * see xpcom/threads/PerformanceCounter.h
  */
+
+struct MediaMemoryInfo {
+  uint64_t audioSize;
+  uint64_t videoSize;
+  uint64_t resourcesSize;
+};
+
+struct PerformanceMemoryInfo {
+  MediaMemoryInfo media;
+  uint64_t domDom;
+  uint64_t domStyle;
+  uint64_t domOther;
+  uint64_t GCHeapUsage;
+};
+
 struct CategoryDispatch
 {
   // DispatchCategory value
   uint16_t category;
   // Number of dispatch
   uint16_t count;
 };
 
@@ -153,14 +172,16 @@ struct PerformanceInfo
   // Execution time in microseconds
   uint64_t duration;
   // Counter ID (unique across processes)
   uint64_t counterId;
   // True if the data is collected in a worker
   bool isWorker;
   // True if the document window is the top window
   bool isTopLevel;
+  // Memory
+  PerformanceMemoryInfo memory;
   // Counters per category. For workers, a single entry
   CategoryDispatch[] items;
 };
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -10,16 +10,17 @@
 #include "Layers.h"
 #include "MediaDecoderStateMachine.h"
 #include "MediaFormatReader.h"
 #include "MediaResource.h"
 #include "MediaShutdownManager.h"
 #include "VideoFrameContainer.h"
 #include "VideoUtils.h"
 #include "mozilla/AbstractThread.h"
+#include "mozilla/dom/DOMTypes.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/StaticPrefs.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/Telemetry.h"
 #include "Visibility.h"
 #include "mozilla/Unused.h"
@@ -111,20 +112,62 @@ class MediaMemoryTracker : public nsIMem
 
   static void RemoveMediaDecoder(MediaDecoder* aDecoder) {
     DecodersArray& decoders = Decoders();
     decoders.RemoveElement(aDecoder);
     if (decoders.IsEmpty()) {
       sUniqueInstance = nullptr;
     }
   }
+
+  static RefPtr<MediaMemoryPromise> GetSizes()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    DecodersArray& decoders = Decoders();
+
+    // if we don't have any decoder, we can bail
+    if (decoders.IsEmpty()) {
+      // and release the instance that was created by calling Decoders()
+      sUniqueInstance = nullptr;
+      return MediaMemoryPromise::CreateAndResolve(MediaMemoryInfo(), __func__);
+    }
+
+    RefPtr<MediaDecoder::ResourceSizes> resourceSizes =
+      new MediaDecoder::ResourceSizes(MediaMemoryTracker::MallocSizeOf);
+
+    size_t videoSize = 0;
+    size_t audioSize = 0;
+
+    for (auto&& decoder : decoders) {
+      videoSize += decoder->SizeOfVideoQueue();
+      audioSize += decoder->SizeOfAudioQueue();
+      decoder->AddSizeOfResources(resourceSizes);
+    }
+
+    return resourceSizes->Promise()->Then(
+      SystemGroup::AbstractMainThreadFor(TaskCategory::Performance),
+      __func__,
+      [videoSize, audioSize](size_t resourceSize) {
+        return MediaMemoryPromise::CreateAndResolve(
+          MediaMemoryInfo(videoSize, audioSize, resourceSize), __func__);
+      },
+      [](size_t) {
+        return MediaMemoryPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
+      });
+  }
 };
 
 StaticRefPtr<MediaMemoryTracker> MediaMemoryTracker::sUniqueInstance;
 
+RefPtr<MediaMemoryPromise>
+GetMediaMemorySizes()
+{
+  return MediaMemoryTracker::GetSizes();
+}
+
 LazyLogModule gMediaTimerLog("MediaTimer");
 
 constexpr TimeUnit MediaDecoder::DEFAULT_NEXT_FRAME_AVAILABLE_BUFFERED;
 
 void MediaDecoder::InitStatics() {
   MOZ_ASSERT(NS_IsMainThread());
   // Eagerly init gMediaDecoderLog to work around bug 1415441.
   MOZ_LOG(gMediaDecoderLog, LogLevel::Info, ("MediaDecoder::InitStatics"));
--- a/dom/media/MediaDecoder.h
+++ b/dom/media/MediaDecoder.h
@@ -30,16 +30,20 @@
 #include "nsIObserver.h"
 #include "nsISupports.h"
 #include "nsITimer.h"
 
 class nsIPrincipal;
 
 namespace mozilla {
 
+namespace dom {
+class MediaMemoryInfo;
+}
+
 class AbstractThread;
 class FrameStatistics;
 class VideoFrameContainer;
 class MediaFormatReader;
 class MediaDecoderStateMachine;
 struct MediaPlaybackEvent;
 
 enum class Visibility : uint8_t;
@@ -639,11 +643,15 @@ class MediaDecoder : public DecoderDocto
   // Notify owner when the audible state changed
   void NotifyAudibleStateChanged();
 
   bool mTelemetryReported;
   const MediaContainerType mContainerType;
   bool mCanPlayThrough = false;
 };
 
+typedef MozPromise<mozilla::dom::MediaMemoryInfo, nsresult, true> MediaMemoryPromise;
+
+RefPtr<MediaMemoryPromise> GetMediaMemorySizes();
+
 }  // namespace mozilla
 
 #endif
--- a/dom/tests/browser/perfmetrics/browser.ini
+++ b/dom/tests/browser/perfmetrics/browser.ini
@@ -1,19 +1,21 @@
 [DEFAULT]
 prefs =
   dom.performance.enable_scheduler_timing=true
-  dom.performance.children_results_ipc_timeout=500
+  dom.performance.children_results_ipc_timeout=2000
 
 support-files =
   dummy.html
   ping_worker.html
   ping_worker2.html
   ping_worker.js
   setinterval.html
   settimeout.html
   unresponsive.html
+  hello.ogg
+  sound.html
 
 [browser_test_performance_metrics.js]
 skip-if = verify
 
 [browser_test_unresponsive.js]
 skip-if = verify
--- a/dom/tests/browser/perfmetrics/browser_test_performance_metrics.js
+++ b/dom/tests/browser/perfmetrics/browser_test_performance_metrics.js
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const ROOT_URL = "http://example.com/browser/dom/tests/browser/perfmetrics";
 const DUMMY_URL = ROOT_URL + "/dummy.html";
 const WORKER_URL = ROOT_URL + "/ping_worker.html";
 const WORKER_URL2 = ROOT_URL + "/ping_worker2.html";
 const INTERVAL_URL = ROOT_URL + "/setinterval.html";
 const TIMEOUT_URL = ROOT_URL + "/settimeout.html";
+const SOUND_URL = ROOT_URL + "/sound.html";
 const CATEGORY_TIMER = 2;
 
 let nextId = 0;
 
 function jsonrpc(tab, method, params) {
   let currentId = nextId++;
   let messageManager = tab.linkedBrowser.messageManager;
   messageManager.sendAsyncMessage("jsonrpc", {
@@ -70,27 +71,30 @@ add_task(async function test() {
     let aboutMemoryFound = false;
     let parentProcessEvent = false;
     let workerEvent = false;
     let subFrameIds = [];
     let topLevelIds = [];
     let sharedWorker = false;
     let counterIds = [];
     let timerCalls = 0;
+    let heapUsage = 0;
+    let mediaMemory = 0;
 
     function exploreResults(data, filterByWindowId) {
       for (let entry of data) {
         if (filterByWindowId && entry.windowId != filterByWindowId) {
           continue;
         }
         if (!counterIds.includes(entry.pid + ":" + entry.counterId)) {
           counterIds.push(entry.pid + ":" + entry.counterId);
         }
         sharedWorker = entry.host.endsWith("shared_worker.js") || sharedWorker;
-
+        heapUsage += entry.memoryInfo.GCHeapUsage;
+        mediaMemory += entry.memoryInfo.media.audioSize + entry.memoryInfo.media.resourcesSize;
         Assert.ok(entry.host != "" || entry.windowId !=0,
                   "An entry should have a host or a windowId");
         if (entry.windowId != 0 && !entry.isToplevel && !entry.isWorker && !subFrameIds.includes(entry.windowId)) {
           subFrameIds.push(entry.windowId);
         }
         if (entry.isTopLevel && !topLevelIds.includes(entry.windowId)) {
           topLevelIds.push(entry.windowId);
         }
@@ -130,19 +134,20 @@ add_task(async function test() {
 
     Assert.ok(workerDuration > 0, "Worker duration should be positive");
     Assert.ok(workerTotal > 0, "Worker count should be positive");
     Assert.ok(duration > 0, "Duration should be positive");
     Assert.ok(total > 0, "Should get a positive count");
     Assert.ok(parentProcessEvent, "parent process sent back some events");
     Assert.ok(isTopLevel, "example.com as a top level window");
     Assert.ok(aboutMemoryFound, "about:memory");
+    Assert.ok(heapUsage > 0, "got some memory value reported");
     Assert.ok(sharedWorker, "We got some info from a shared worker");
     let numCounters = counterIds.length;
-    Assert.ok(numCounters > 10, "This test generated at least " + numCounters + " unique ounters");
+    Assert.ok(numCounters > 5, "This test generated at least " + numCounters + " unique counters");
 
     // checking that subframes are not orphans
     for (let frameId of subFrameIds) {
       Assert.ok(topLevelIds.includes(frameId), "subframe is not orphan ");
     }
 
     // Doing a second call, we shoud get bigger values
     let previousWorkerDuration = workerDuration;
@@ -172,14 +177,23 @@ add_task(async function test() {
     await BrowserTestUtils.withNewTab({ gBrowser, url: TIMEOUT_URL },
       async function(browser) {
         let tabId = gBrowser.selectedBrowser.outerWindowID;
         let previousTimerCalls = timerCalls;
         results = await ChromeUtils.requestPerformanceMetrics();
         exploreResults(results, tabId);
         Assert.ok(timerCalls > previousTimerCalls, "Got timer calls");
     });
+
+    // load a tab with a sound
+    await BrowserTestUtils.withNewTab({ gBrowser, url: SOUND_URL },
+      async function(browser) {
+        let tabId = gBrowser.selectedBrowser.outerWindowID;
+        results = await ChromeUtils.requestPerformanceMetrics();
+        exploreResults(results, tabId);
+        Assert.ok(mediaMemory > 0, "Got some memory used for media");
+    });
   });
 
   BrowserTestUtils.removeTab(page1);
   BrowserTestUtils.removeTab(page2);
   BrowserTestUtils.removeTab(page3);
 });
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..7a80926065871e35a3c480b99bb409f3f4b4df0b
GIT binary patch
literal 11328
zc$~d>by!qi*XW^3LL`O|2c-t-6cB|06oz!@AtWV-8cGQf22emi7<xcDq+<pU1?fgQ
zM8cp%LIf=09{j!U_kQ<2_qosa*FCe(KI@#d_u6aim3#D^oD2a{;9u9n*Ue66&!H=m
zKS_8<{5^f_Trg)JNWyOXGa{_zpJy3~{@G06zt1EApVaH@9t=vS|Ly7~`-jX`5Nqb*
z<t(P};{<hgu`~Jm6{-c5l8}^=kdlyw3hARgT)cc7F^(R-u)nKP{4@13T+M_8Pyql4
zoJp9X(mr0XkCiu$BR>~D0PDR7ALT9JQ+uq~BijqHzuoK*1Mhz{nrIB1O(^6xHSp0A
zUBhDvAf`j!>rfC{fjSL&)9`~(yuw<NPrT$mSOy>1*|<k6w`06ZARtu84ML@75L&V^
z$!A0XgjS)^NS`X%Gc3f3!H4dQO3xX+%`?hzvW=7fjQdX)C;4y4LvoD`Q}r(d_dnmD
zG7hXjA^_y(3B`OAVqoH|qXqf^fJw!h<@Pfc?ON`^F+O@~dUFbJGXQFEoMu#^W=xS;
z{5_W(fd~0Qnfb!ybuI&SB7=3CFU9^&*8~8AJ1pH^EL4BzIe<E`5sE3e7XR5SsmMHs
z3L8~H3WNawIXLdz?OGPyZ+vg}uD|{Mf3|;?g@S4b7jl4*{aXdpyqp|r*+^zfS~fWM
zd9{o15w)_Sh-FwLzx}vjg(@{~@Q(72#h{&kRKNrPwk+2kKkDV9=Ed5v2&#J*LqTYT
z&urlSIv@2y1QWaoA2iqu&iK0(pp0*zSSC0)2JB#kIHV@5INdG=*2|3<lI(>>oReD5
zP1aNFFHF)?9N;U6N4miXEQUI@D{S_aglRmcS`}s=C)oq}Pj}_79H78VlpGF;AcI(z
zzeIgNdffkr1tQN}fLjB}ciV^Wb`oEIl89FF^}+hHraP&w%W6DjjGj3}-#tVxO*t+d
zI?hiyTBSNJ5YSfBR#r29|9kr@dKftn7XSI$-=-N~s7d=*If*3TmV^~ti}|Dp>LZ@t
zJW0SM)#-j}$^FdICp>?LfQn)I$JO%xs*TS)wb&)6<V@KwUc?lay?Ei8S?u!vZlrm<
z<p4kf02h0`xo>-O_j;d6^>>G%WM?{6yTh&FBhs5BqYcV9q5gK?@{XLJp(*;`N`U_3
z)xDVOVCx6B0tAw;YfZ@L{tw&P#*&^HA5(H7La)~J7OaP_!CJVGC3!NaG~G@|ajOhY
z38QL`M(`HpPAp0lB_huSm88S_AYbw#5+x&RX5L}mzW%`jD(^g?4n~$9Xb+QppQHeP
z1hZr>p9Avp8Xl*I5GX1%K@AmR(-E=HFnS0<{$wPIuFNqB;l^JKs+5!)yhX4eFXX@-
z0C+5^2`gklA|y-F)ex{=oV_M&1@EJ$(3|edB)kUkWk3|-eBu>K@EB$(suT<ZTp$}%
z13(4dba|tRtRyOU@I4U|BT0SUo(WC^i!?yKz$cHZYL!JKA+^f7laO&K7^ET%%#Rr<
z4C^*S#)uXg*}I6&j3b|l_TVkzUPs~VM9Zd7sPg7+Gfb@Pk3I!NHf~xGR#`Ods#`hr
zPRm8KaN5pAv}f9G7}kTAhE+zU+FdQ{p0yj4C8T3OQY`jwVLdr+70tc)2#LxmJZey(
zJ3VNqb}b#ln1sQBr2LR5?aHZ4RJm;OILf86ClM8Uws@?h6A~p-)@_E0k?c;##EQ<0
zqN>0(3}Y2~Q4vYCht{a_Gg2{?ASnsC-W(-FbL#X1vC0E$j2L|SeXO(S+N3L^a8C|q
zq<!{%1XJY--u0_;_iWI%0%RVuTRFXKF|5&tj~H$KSsWyBYwBIZ5P>=a!=$)=*>|@;
z5a%jZxnhahRG#Jx9#)_pLv6!)77b%x&*Ws)yr$ws4T7X1VkCcNVk8vT?2IbgK>-s9
zY<WTAh&3qg1%WEV%|L1fB-Op18!HX~Hg8A(=o_q*fpcFB0M!G+RQnW)AXo&9IuDD3
z3&D)zwEEH`k|YI-a7Yw2KLONQhCNcMw-C#W66SAUM!1yW#!*5=gj7@<jKHP_D~m*;
zt`;?<qFzW6OzgYTaYG6)a0UWanSw->b0;TB3YA49B4Ds?Br+D%v($R2FB3wztjh@L
zQdu~TgcX4X;126HvWO)_oo#iO3DQ-P0KQ7jJeGuUfHT&>h=x2{LqV`8A|A1tTbP7&
zD=K7>UM&QzZva|=1Wl%h*%0IkBvn*6jfx`_PohNm2_UIj9P=3|Hf>$dWDvrV;2Lg{
ztC^@+Ne}^p^&}zOE4wo-+!g+{IKs7dD%IYtc6Ac@6h;6M%{@571i~R6v<!mL8N$rM
z6AYS4ppk)+^LMACVrxOO6yhiTT^s_=AhOvr<~5jsqzaks!POuZ@nr-Qh$x=4t0Ww9
zTX-lJvAM<8u7VaWJF{r7s{qCiiDl65ZQvY2G7kIz6PUXooe+B_&LTYMq7gXEh*m!y
z4EzYtG~k*rj2;{z>NujRSw=8GAS%1r5L#sfHaY+h@&Y4KjjExYci7)x^4Eb8l9YLp
zw_s~fnI56;GiCSSlMP^?IU7iU=3;<Y#bcNdG`u(@0>O(#f}}u)B|URsE*X9{Dv%+K
ze;BgIOONo<W*3bh`N|5W@3;vSacak9_m~82HcxN{p_}D#h^J-K@ot07Q;ROT%8lG0
zLrNe+N;OQh^yW0cXE2b19He7LI3giux%?b5?ku6lfaxBT?F<2?8V~^*V-lF|u}p~d
zGyY&jz91qGhg4jHg8ad<L3Uw9I4~pAp2aG6mN~o)U+HLK3ZTLRn)o^Rh?YyG?T`Ys
z=+vx44>t~H7Ylj?GPe2Fkg6uECs#=bMu52QW@B-vSOuzNWUTDkVg$IAnGqw(QzmC2
z6mM4ne#3wc6VOfqe0_84*)Ywm@c$Bm8unL+A_!(kVJ!|BC%JkiL=RY`sCGl_M--@d
z?Lh<-n|Ov`g0JJjut?48dxpR=DX!&$!cv1moMr6)n-JH)EW-O=3H|?TxBlPDuzz)e
zKwo1A$(z&m9uAYF5!%}kbZ7g3B?ACjRiPp~P#64QVFAld1~v680*TlF{S`q21`q`G
z-|iv~35T6Q|J5n}8yZl66(yfR>LXe(-oM|l|83mAyZ;IAq(BoFHvnaJ$J_F6RCviW
z{oP8iu*+q^#3QI-xI|=WdVV|{1}j7&XwTb^Xno-ZQxO=Q1{A1^3UP2@UcV7lD*nhs
zBn@v$yyChL&HyRQj~iDID#EfL5U`X)xMrCz4k^T+ayB>xiO}RvVM5Sm7aAdG%L<K<
z)Y-m?a6$gUB=9vE2hJ_RArSn&NVqVp2=v@C91}tlR-^~!yh0YZ5N{+X8h=p|=yNGX
za2k*Uq);A61r#?430CsUe<L-uVt;8JHJ5JFF2KTB$9z_asNj63T}rerQ%h=O2-3rP
zpoBt^PA0>oC*N4?;}nE$sWC`?#zEK-)Oith*9B741`O^rH%=;0bhFt9@6@t`WdSuQ
zK#nB`AW)%n5`dD5`U(~4MG|rbG!XWPI)mmMZC<;o0B~JJU6TTMLK8C{DMrm3?t9+h
z+ndM>@;OZ(ung#biY+E!mjp;O0M4Hu8!|Stv~h5D_wo%44g+bQUDQA@0PtN_QCFA2
zu(Gj3I5@euFY;W1@{;`f0HmU#^7je-3;%OC3RC>g;pnWuJP_x8#AkaiDkMBKAUrTU
zBs4t4_o;(jthSB4<Kys<1Z_J9hZx_G;6NG4*by|1vn6Ie)PhEru#=I+9<`!>n=It}
zdY!k9UrS=L@};y3-81Js^V`1!i#mrz%*!(h*gkVy;T+g`A}fze6Fv><_{C19x_Mwo
z6&U>Un{LmV*5lsqX4R`K+}={MG6cwE)3DZU!+PLu)mGgf$e>is4&=H54Sd3=KJK-e
zeN$5sHQvwzJGZRAwCGFt%DJ&7LG-jDVlFTjzO~5x+I(8$Yo|^3qZP-&#%AaJfj~OX
z@q6crc-kHL!r+fTze5;1W*L9?&tphc43`_(-jFw--_hJ_kzF&reJD2lbYC~!=550g
zPh2(RM)ULGWdU=M?><Mbi{&+@S_Qs_#5i<i_Q`vW(Rm1$(_{!cQSxlmu_pK@UBz{m
z^jWs2^VP(ZxYUz5atxMoXEsg;&X!8Ajv$(<8_BLcoYK!)O*Wk$|9z({K*Oo?31D63
zh9i>@F7xE2F?!@Wc2DfX;oS!;+P(eI*X6eQ)vnGz1p}v}v3xG~_xF2EHlDX%I&NT9
zJM1ZUm@W2kOxGpa9Q4$F<Uc4sQNCNrqXSi7SncfJs!+Q4p=&8P)cA^st!kO%xd2$L
zqVdgD=aq7bJx^0Hjw#2)rI!%EdgH1p8DP`)98i%p7gd}uyX`4+`x{-^wpl`4tY_2l
zcZGC@kXZYKq?(*`FLlKgj<l~Xiqrda<GpHC4lh^RmRLSM6I)Y;E-Yfjj)_h35<&Rm
ztNN{|`_Uh$e)8g*{JN=ZH1SCfa|ePniYj(0^cZTZur0&x9uAGm^RrWPwG=sJ+Wb@i
zMOA7T(UvHXCLStxIW79hcat9j-X6q}uuT<z^cJJ79m4jifC6%%@ei`0=z>_(m=$EY
zY4hoLwn*QOTxVPJOTB}O6%emWTvb#hzucZ9urcNX%ksKnx5{p>C?sICkGm-uCNdiJ
zdtUWs6Co4Zm*UarHT3`>r+HI~s+Kg>YfC)Wc+7vVX#^W#pL*OAo?c_vp3A{+g}v0M
z&Czk=iOYbuTMBP&VYxbSpy&2v1C6=fZzE!TmcyU(UdN7y8|O+gO(j-LRg(urSB6is
z+-MbtbBge_-E`kQGBnQ(YNqHTWmZ2G-&3fAh=u~z&Dsh8Ia9O#7$pFyk@}2VP%ruT
zl;P*Ja(0y#uj9eI{S}!z_N58qR87SY;X5qZu#c+yV}4T|#^goyaz5$8O>={m?4EPN
z!{hSoi*w#|FDyQJFf*(kapn6BpZ_$DE=I4sW}hECeaiSsehCH7ZmqR^zqvj9>{fis
zQATi_K3Qvqb}b1YFm~+94w&H4((_+JYmzTaj4@;y7rfk~8u~8UmWDQHO2372%^*=#
zq_6HYB3H957wx_rCHE_-cAHkm^MMa#<aLZ#$>;-9s8A}ML!5xevndma$j#uw1qo5E
z7%s`XwhmD@p}2OM#1huv_oGJ54M1oH9h6Q*<>yUy12X?G<`E(~DJxq%JYMU=n+D@S
zJ>9IV)y;!p0d=fKT*Ip6yLH@(%D#U1&SL>};h=cgowce*7Rnc5=kD>|^0-<nGB3Ed
zJ#H+N;i8+QwHUO(vIM84)y`Tv8Lhgo@i<Z~|5|(MDOGd*#gr<WI48G)S!9n6(AumG
zn*6dUKt4<2Wt=s)UBB7&%w%Yz@R>hOSQo-@D5gA>WVXLL>i1!VPj5yvo8e_9x!?|O
z_mN3X;?2P<8{&BDlwP}8!fe4>=hqrG;I*6qT#Ju??Qzxt6U2}^uhNgUSJZ>pey@$0
zlPRuM#_y&Kdy0@`<Z*8|d1y06lT?D~M3h-2oH$2Bvz?k<%9*tkWIR|r=!%|;7+!z$
zgmXIXfx(k{YD&z48_gyDh;hav5j4hasM{Sv4rf0xuuMfvukXIgS65%-`^+8|xVV$f
z;Qh_OBniX8&P~@{VRKc{FTK^2D;2R?X2tK2V|mVM#i{d<(iI5p*A9~h!v0XwgyoPi
zgiR6ACC6(;y^jPHr^{>mt{Y`K|M+feBLA`H>6?<`K|8q+`L!RzFHM+)H@2_5yf)Y1
znCc5}d*!jOnYQKV+x$o=?^18f2`AOQ8J${8ZpW2#+XW9V6CLk=sA4t#_R~&A49~~0
zFs&CyRcOt4(AX?{L;@@}Z>o?0%K<Pz#Ug8txWy3a&AI(njCLzzCbzh#EA3d9*)9D#
zRi_IB`|Y@gTKCa3J0v%!1sgAhl$iJhWZufs!l;pu?ks7jyP<<~<hYGYUuIrl)v*&|
zZ<^JlvNc@!ctQ8o8)>od5ji}TK9c)^W{4*db9i%@%BA4U_5gqZ*+v1VaLtW0<2m(G
zR^2OxleklTpN#+9GbM5+tV&!P*nj+jV^&pMKlV>j%6H|Re0uo=qk-cJKH@|A=EN>(
z3`2p$%MF+pLxM)(w;+o1_n-4}p5*5D83~zg)x{7-{9}_Dy8M>=vz<OVIsw!D7D9j!
z(@7<V$|E`(KK$1Eck_)5a>;AiR2<NDyUE)VZ6&=nt#aehZrwg|i$t5rDrIz*gVKl4
zlE*XVKMuV^TDLGoWQShydqXI9cAc^M4m<Du?U};C!KwpI`u6CY=<2#3$V>}q&o$po
zx>4Q0(`FBvM@O9rPZ@sK(hMGj&hr@W)C|mja8U=;<(ro|Nk-w$nXAaXW3fwz9252R
z-FD62DxYB$gBD*Nou<)zhetpIn-r@~Z+!1uSDdW8H!L&lz6$L-+50p;eA&x~xj18w
ze{V43!gT}-vkEFptX{@aR_DAjPtHd>I#nCMLB6F+8c;!JCWyCs*G%c8!Gp|4Hj0Xx
zLM(n(e@Ewg%X7;fhjx5I^+8b`i+WkYd=S-#FRL5dsd>s^p`;r;<l*ZX(djm}OXQ6o
zE*H&TSfS<95l^3={OR}mac5c7IY24{0}*l+VtQo-mTsiZZu3L&_jGQ1PQ^Y%PP<uu
zI54t`SY2)(W8dl=H$}cry|wzrEoe+;&t-#quXtarrn%_x@?+i9p+2T1QY7s)*^j>R
zlns^L!tVulA+^gG{*$n_zT~0+!EjhjQQHqR%J=(jxN)Nwl!zINCrY?9^F>?xX3YhA
z@7pp1GOBWQ`fcQiKv<l*<!-1hi?Bz8sjs}xz{v+0$lL;<rAfZr(Jy8eHZYXpQTIq-
zc(td$&Sy1NLAh%W(Rkv95cRz6A}t=g$q)Fp$z2~M6;b&_X0vgKt!E!<*Hk_}jZCG&
zzCgW87{wnENef)s&39k22vbv#`ff6|nXS#no6#>kwpCxqQ`W%^lRgVfyXfZVUNC#(
z^i*n1@}T*GfrOgTliFlGe|f<DX7fg`fft1%89HpI_ujAL_u*TO(at6l4QzsyX%|l8
zD~mbNnLgk5*K9mWYq0=eAkDQDb$Z-JlKllf`sY}If!0qs$L-Hrn>MWHQ|;cbgLg<l
za(?_>O<ym+l3%5($s}0i&OLkM7SeZ99it*_Lh`U_tikw?zE-k_Hz$Zm6^}e$w+672
zFoiI&>;G2%MAdWqe&9EocOS&+Jl>jy7VQW}4IsE&tGS{txJ`OgY|o0vyc>&S*S{uk
z#YFIOjOzo-N+t~o05!-ZEpecnIO(|~t-b5k(77EZfHH)&#BEwDUkw@T-;3N_h=|7Z
zHZl4&aX<cgDl1k-*Ax|dvnBXua}-tkYQGFz_idl_xZg`5)biJ=*>V@|*kH$_ZME}G
z1fpNvpSFk39~t9aAumLyHLstBHst$~^(fjENH<+3-^-BuJ}xs6P?Sve47gElNKJhV
zF;MapXutCgsx+#&&WA3y-^Oj)@+`#+KiHdco7;cs=F;{V)wOI#Qq}|hqBd(qE&iw+
z4N&%}%{2UkZneGE$5b*ql=jD89xt>>U!`6ZObtxyk}N3{^dD4v;bf8_ITAe;&~eCl
zxuQ;jl&9;c6Jn@6!q>W#Aqa%dW6n`uqBl>i)+@OYCMRt>=pTEO$h~7;aks@uH(NPw
z(c{F;OePzmNy1W|Cq^N}7$`Wd+7s2yJn~icpk2Z}Sor;sVXYx5w|?_QoYiEnjg*^X
z{N+ezxfK$3KJgOfxusr<O!{ydu?#u<6WW@N*?O)Un(PuwKYG{!wk$)E^m>KyC#HC_
z7Qj+*RF0WA6&Yw-(OROv`n)h!+{zdFp>rxE%QJEUBb!5ZWwu_iI^o5QSS*#Ujbe+*
zD0votDeGA4ROB_kd>?f0I_t}F%TZ{EwA9j4l5eH>^Y|Bo!3-M5OxhX)Y=kDW!>4~Z
zHCR&U*#k5mnA=)e;xuY!^p!Sc{se394?ZW=Q3|zbvG<1xZahgJ_M2H_yim`WQ4%zB
z^8<Zm$*(>`tM>{82nDvUA_CuLo4!RQS4*-~C)_s9(!Eah(4ON)CA#3@IIkFH#Q+7U
z%XX4^PK61$@CY7#P}94)31PV9q_s3;|IF~mrut8{?mEWj0X(PMb8@kto1Qvln07ln
zhwCond73LKCOo*su2^^J@@a{l`~J4Bi|A`pyr^{`Za(L$OH<(YbpeH=?Q;O1?41uc
z!2_37L51ZazeKQ?M;rP*mqw@EhoZ*)sewpH1AL2S)yn&`cDP763$6$C+tMj9FKO0(
z^k=*5>#AzHwyP`tHQ$s2-YLjXU(8!8YCFv7xj$3at9P&V$B#x`=SQr7j{FSvR03F5
zosbc4;c{%;V6^9H^f@Y6mnpj%Z`n9f96in*)lty7ksDe6gg)!J2CGlgSdw|tal9cS
zimSdB?v;~i9`w<7Zm*22xOv;M({^a0mmxcTW8q4bV}_R8QWOAWHfMpS=cZ3Y65tJm
zfoBRjEiKfFf%&vTAl5@VQt;$69OEi~aum3LOyc4C<jl}J?D<K87a!ZY&^q6TRjopo
zk6>IF8<*zho;6D`{?hYhyFDQd)gp2Q+&#LNVI>9$_$0GDA0)oE?aA`<dl>Jl7}JVR
zdG&hfp8SeGHp&Nkur!q)rBSK(Nn7VORa^jo5ilTRps(Ke`tamSy~`q&nJT66r|j3P
z(ZCz`(7d;$hx93`6|a9}ZQpw-XeuGZ+FeUxsXHIjflQ)Ke)+@fa{Z_*bt9o_h~$%L
zv*h&ef+g9LYWajMI=&|d`A<`q2pxL<wLY)goDFx_6oJquZg|-+(y<nd2pvH7LgU`N
zKYHh=!ZjyVo#>Y$*()BES90u6b|-kgp2dBN`TGH_!o|+?P0^)$V*Q%Y>hk(&Bv3c^
zg^u`yVuyCt3OQ=r^*7|5E+KXytQ31&q?dz@UDkSl`4C2PS_X=5J;4%+#FsTHPaE?J
z+{J*!GOn6&AQQgZxTut?QrYM3)N*=wx|XWz>ZW;#*{s2u5z(<@Z9e5C7TuLK(|x@1
zzT|_`2PQvTh@PVdqsDbZA~G#SQ=<&fUtWUkvEwM$7mb*HU58xnZV;p89Av&5JKQlg
z|N3p1C|ApS8g(s-TG`uyg7}wqX_P=~|E3Bl;3Z3LAgZ`}eS6vTV*OXNTk+jAYM0FZ
zn}ea6fk#?D{MlWlG<?jrLMM3J^<1CnKa!KG*UR<$qSWcHWqaS%j5;J@j4<W2_S)1(
z>iMk2$-3glk46u$MPHoErzS!MbhKpLyx@^pQ&ZIM*&)@oA)Xz;-#;zL@B04dPYW{e
zISlm>xfl@`dOz4dC^#fEAn-wOP;l4-@b&&<@I5%l|76YrtT``kClJU1z6Q~E#3}zl
zeo^r;^n9hkdmWAkYL#_LiCcTV>VAqrMM^#cMY*t^2ciREbjoE_u6H1MC9-7raC2qi
zQlrl8r+5|K-?ijg&v%W-A5oqBDJX~{1~?VGuV8l6-%1*E`7J$`bfYag-0W6;)pI4?
zX1~kKm+rCxC;cD`xf{Fm!+;859aPrcElxsP%{Bx(LH=p5lZ3C32NjVz_CtA#o_TL+
zZG~C-C3<!gwk#h|`OPMRQYoZ%&}sdJ#4njojMfjQkM`kTKEJYKtPTqfrr58c>-Jx=
z;((2)a7R(6#(4QHz9G7be^dXnVN4R*yeUKlg$4(ZvjL=hmHr*X$^h}xOR~>PI<T7u
z=k+d~BC0F04;2sS+cnMXR=#U4Jzow*(vYA<Fl^?vMjMCm=RUJJGU&<6OLf85>7}^Q
zF6F;H8gu9ft(P6Df22OXhJD4Z#l%SAJe)c3rHh-Z{b5lj<H&J_?Ku$|u-La~B8T#I
zv?=KUxw7S3+?bb6@E=|pS+1V)<4-lZf~jA-W7#$mqrXEJ`j|yCSk1FolZjQ&5{CNk
zTdDV?n(<>yrrbK~S0za{MJxtcEx6eFIrf}GZ<jT2nQ5SB3k~E@F}4aDT|DO-j(o0^
zDkO_2J`XyWptSCH11)3wCIFl(O`RiVyAjogb8E@mY1tB<ghq{ACAk&rOh5ihg8Kcn
z=jg+se4+c1ZOl@LaVu9m)^ohYu%ap{<L+zCG&T7OX{RMQ`?i3AeuXwxBe98cNjp)~
z^p*=NjyLZ+Oa;2oQrrsgQF%BcQ1iO(LDOJ}9B|{EMFc4z9Bd)2@+iMvyZ3$c+a32v
zJ!Uct<V8&s;UN7&YrLMD(Rcs)-28gFN68P3oZ}hnFM8U-_w8;C4dVUf6zTAO&#_ls
zJ1=&!q$sblMf4jiMiP%TPQJVpp{?B9u)i5v<{NorEEFE{<<l#jwWKT>fT#H?7>YY)
z*fdK`@WXX2M@zlemarW+sbtqy@?H!wo%GAzOXN$N@0K-QB``hZeJ+IY+H$Ysu&oZf
zLchi=x6c<>)(G!rc~bYKUoT_VgsEiHWk9L*!!xwQAz}XkPVhj1(nK*Tt1Qjuqd(56
z+qm3lv2y>5<!@S`^$AxQd9KR7QW!9WMb8uGPm9D;Q*T&&hO95w#6dUP-}4Yt8$Gl<
zbpoCyf6n*dr+EB0{@yC{YtDrm5^?k4!Lb=B0xJQPkb3Ld>(r^&N-<x118C%33&ieh
zC#kA7)u*JP=MXp5%q03U6V}a?BwG(U7mt6TGPg&8lRoWQa$wn`;#`eN_%p8|q9TTD
z#<X408%B5=|84<|qVv!XGO(^3wY$%wM0h>dS#!R#_I*^))1O`PRVLQ+uGdl^i^7v)
z<qh)zJ4J<0vP^3Rcdcn<yDlNIA}ZTm&yViD-ZWAbya)f7Qm$xTVx?%ZVs-7L;M^$~
znin&;z{OM6r-~!)nW5)7#UC~;SX`jud5P%NnC|9Nio1~=K1@|MwD<sG-gAwyF(u}%
z2UGe+l4ag&u9NW#JrN%7Tb(eb64lZ-p7sZx01;1{o<=6;*Iw~YAE=NWE}gB}k`EjG
z5H)B?jtV5cj*T@}3;MktYi)1CEzb_*fGLkOj3MA*EntxAtW;jVFs_oSe9bfEF(;kv
zr77Qr@wSk8qpR5!3S6V-cf>gbM`T(D-|ok~D1FzC@NrYrc~$dO`bJ7e%Kmx3SBorS
zx|B*?y>mNyAJxYAA0~DRgbWTFdF|80q8sgO@`cpRh#1!26jy=JIk#LOjA7|xP8i8l
z%+x)i8)Hwh=yjBBmky`re9bF4-zg{l%%|d(jyayPu&uq{cPZ7CyqjPAN*Z^dmcCqY
zXBSr%HEc7aqW%*5ElBT+kosY9>y4i;obs|okKGyHL{%<N4H~@XzPtR%+WM7)N`lFs
z+|fZ5svI!skmQ!`THF8(bZ3WLTaMG1#o8`zU(e1%wNK91Wt_OrW@g2d)DF=G&h+}^
zy`uU(lDK`xFSg$=0g_2TeRBPZHF`@1sa{gJ8}L(QTVnH};N3zWvzj<X4?dE_Y2O&i
zC#r90e=<9E`5(mVe|UqEx%+;x^}hb!bms<?dg<4a0Aa<V^IzV#IO-bSgtA3;R%Cpa
zmtuH$Sdki~!Tdm(MPY%XTx08obp_+5!yji8jz<@>Z^=&C_-Q+d8Gg8rmi--=@{4{_
zS5)WfxJ_TS*~<BcbI($3-8R0ouJk@`L<lJ`AA6d|`4q<b^ewpjWZ(l9=dx5u0M@Dm
z5_Bw&`GqR<&)Pqto@-dICA;REIk_d`BOkx=ZrWUVSDJ3D3=!tI#8mqwYWAUcC7psu
zv3hbwefZ5&`piI631=+Qj@vg=WzS{eG8G}pZaz~|d*}n^>+dO~&y|+cBfPO<%Pm3f
z)fe)tw7+jInNk4OW!mQfXQo%oqarADnr|m#vPJoYv>Z7LN^RF6+t#@Um-z82?8_qC
zoi5bRtlD1qnD%haCr~v>TjwF^?fnwlJT(*9R>#RTuFdy&ZZ%(&%Wf)N3{ae8c*-o-
zWs1|ul%$*v3rO~rJ>c0lHpcnsT;GkQn4<V|ed*wA?Puh<s=HYiY*Yw#(dd><{QyrV
zdJd%tVz+PKs7z-3roeD%N#nL|!lMq#fo+Ygt7{YN4{%KmcD@fC(^(oq>kU#;2ep&G
z81?QVE-E;w+;Mh>K)j!f>50{Q+NE=BCzq94vJ!ILlX~#HCNw)UPrN2lEZ%0?yEZnS
zQUE7SbP#}L*`rt#$W<ZqxzCG{N$uu6P}%(biA;apS*=Vh>v_)Z#D^q4`E>t@T$`J`
z+3qHneyhE)qztzR^HKG-y4pQ&=5*&3QP$r-nknWZY8IYoA^FFy5?UIoEzF15nZsvj
z!t$y<-J`{~E?~2G?`LcXU37|N0Nv{iiOQqnRUXPPyD|HDnnqV2@6h9-r^fUyW5ia1
z1>diPk8UKr3r@E+=o`Q74&<x5+&M%PYyJ+|qF3Ou7Ca&)*(O&@8zTJH!RNSE$<ez#
zIr+6qzNFzt`sy{COc^s*D5L7vaCyJuTb4BNR~cb?K_Zs3UjXa=O(6h6z0~F)Mgj)%
zD(vw4jHMr+oxR85lQLI~e!nuS)v{_}YoD-Z>WKIRy8!ifa-=8^`Stnw1oq<Z(Pm@s
zxC&dZQNcg)0W(`cG|eCPq1(pJ37-tM(|^R&<2EgGzxz(2TOi`oZ|Yb#FF#+nBEc@C
zKRWUyYa3`S<C5?MC_4U>;8g)3MfSMnH!XuN*>ax?5*Av|?;OUXclpMLw6LMGu%K+)
zZr^-v_qEr*Z;k1!$sc^Yk10<7>IQwckTK96zGzbGQoV5R`1ONQV{|gzw2M5&PhJgA
zLQ#VLdL$Fy1k>c73#o|y{mxbt5L)JDMgsNCpJAgBLZQoth5hvfBcnT0$X-5XvS0kl
zeN-)ekJ&x*9Z}kQTOyDiGdHT70<n4WHQ&6jGl>CJGJMX8wl6ww758+gUkmHAYmVX{
zYKSi%8P&K&ald}DgHtcn<_9J!i}uCm%=l4qb(Xv5g=e?l$pSp@=EEof;iGAQ3P8tp
z+jV?7^a$QGfAf6TxrtTtc~$ef8&SL6BF(bST|Ky0mu5tnaDsIyp{f>xCT#`x2?nct
zWMg&&Dfuu9#FeX~E{qD-hKre3_Acuc;;*$mz~HTyK5W318{S#(HX1KJu9)yg=O~;H
zP#*PVo#(po(*T}NtoAP;;{-DZR~Shc`43fgz(%FA0e!C}z|Vr`OG;F`RF5T#-hN=4
zPq};vFO9aq<UVfcefL-M51ku58gIAlqlfU!2hhSrL6pDFRvOt*qf;A;PN~suj3OWQ
zyOvuKLq($Nl2P)n7XCVMPPNKL?FHL&mt6CR(lbV*vDQm1WI#?o7g&?Cw(^qD^2HL-
z^A8>ztT7ON<+CSrEy@JwDc(Mzs~DInepfhF&k^jY(DfqD?x+AT->&b*s9s&QW_`?s
zn=CBPzc|M?y%ue}yny7}Q;?~<H<YnBBG|a*Pk9%A5u$+8UhkH|@WpMTqTO%hQ1@m&
zt#%3wq6Knj+|@~-I?R6%3?#Yc4Ht&Q8kIgxX)3pT$-D-ZB~P9&ezvlH9G#@qE@%4I
z{@%r&dIrD7@|i_PctYDsT-oG+XRWLN%a7`cwvUY4$`sGcR@bBM7x#XgOO^qu+}#Ts
zFqwYa`Az!oBYj0TmN}VDfAhtfZiM{qZ~YbS0$6XjK}l31mt4<5RS3@W#I1MngV9u!
z7gJB1KjXK_+I(+xx7PXj=iTY(U5nM7;aNp!)jhZX2}!uO8>;Vm^YT^b@xv@5QkD09
zL)&`OOMY2ThXfwAj&Y>k=`eXTKqKya?cqBW11>ZE0a_1=%h!HM{n~mQe1zm$vZx1m
z`n4s&6T1=su#~c)=jW<keLl@=k_(*4gbz$^^z2WrCe%&{YzVxj_L&Wp*rNzm^xO))
zl9{``9L8Rpjr26iUp%Ls^0xNWm4g|bY|~pJVJklPOyDH$6N%*$jE!vLOX-~qxrF;~
z$<%q(OL}6~Xp}9a9t1a`X@FLjtXfKD4c?!+E<kQl_V8DqgOT~+>&F%L)f}>PbAGT>
z$2%NRelKzU*-q_HuF{kCIWHHvkSyfKqGqeCKR32UxawD&o00_>)9I_)&y@*q2+c?{
m?(LDhLTSqomI?x0MX2cS{gT*f?V$X=d!JZwHQMGf@V@}TXM5BD
new file mode 100644
--- /dev/null
+++ b/dom/tests/browser/perfmetrics/sound.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<html>
+<head>
+<title>Dummy test page</title>
+<meta http-equiv="Content-Type" content="text/html;charset=utf-8"></meta>
+</head>
+<body>
+<p>Page with a sound</p>
+<audio controls autoplay>
+  <source src="hello.ogg" type="audio/ogg">
+</audio>
+</script>
+</body>
+</html>
--- a/dom/webidl/WorkerGlobalScope.webidl
+++ b/dom/webidl/WorkerGlobalScope.webidl
@@ -35,15 +35,15 @@ WorkerGlobalScope implements WindowOrWor
 // Not implemented yet: bug 1072107.
 // WorkerGlobalScope implements FontFaceSource;
 
 // Mozilla extensions
 partial interface WorkerGlobalScope {
 
   void dump(optional DOMString str);
 
-  // XXXbz no spec for this yet, because the webperf WG is a bit dysfunctional
-  [Constant, Cached]
+  // https://w3c.github.io/hr-time/#the-performance-attribute
+  [Constant, Cached, Replaceable]
   readonly attribute Performance performance;
 
   [Func="WorkerGlobalScope::IsInAutomation", Throws]
   object getJSTestingFunctions();
 };
--- a/dom/workers/WorkerDebugger.cpp
+++ b/dom/workers/WorkerDebugger.cpp
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WorkerDebugger.h"
 
 #include "mozilla/dom/MessageEvent.h"
 #include "mozilla/dom/MessageEventBinding.h"
+#include "mozilla/PerformanceUtils.h"
 #include "nsProxyRelease.h"
 #include "nsQueryObject.h"
 #include "nsThreadUtils.h"
 #include "ScriptLoader.h"
 #include "WorkerCommon.h"
 #include "WorkerError.h"
 #include "WorkerPrivate.h"
 #include "WorkerRunnable.h"
@@ -469,67 +470,110 @@ WorkerDebugger::ReportErrorToDebuggerOnM
   }
 
   WorkerErrorReport report;
   report.mMessage = aMessage;
   report.mFilename = aFilename;
   WorkerErrorReport::LogErrorToConsole(report, 0);
 }
 
-PerformanceInfo
+RefPtr<PerformanceInfoPromise>
 WorkerDebugger::ReportPerformanceInfo()
 {
   AssertIsOnMainThread();
+  nsCOMPtr<nsPIDOMWindowOuter> top;
+  RefPtr<WorkerDebugger> self = this;
 
 #if defined(XP_WIN)
   uint32_t pid = GetCurrentProcessId();
 #else
   uint32_t pid = getpid();
 #endif
-  bool isTopLevel= false;
+  bool isTopLevel = false;
   uint64_t windowID = mWorkerPrivate->WindowID();
+  PerformanceMemoryInfo memoryInfo;
 
   // Walk up to our containing page and its window
   WorkerPrivate* wp = mWorkerPrivate;
   while (wp->GetParent()) {
     wp = wp->GetParent();
   }
   nsPIDOMWindowInner* win = wp->GetWindow();
   if (win) {
     nsPIDOMWindowOuter* outer = win->GetOuterWindow();
     if (outer) {
-      nsCOMPtr<nsPIDOMWindowOuter> top = outer->GetTop();
+      top = outer->GetTop();
       if (top) {
         windowID = top->WindowID();
         isTopLevel = outer->IsTopLevelWindow();
       }
     }
   }
 
   // getting the worker URL
   RefPtr<nsIURI> scriptURI = mWorkerPrivate->GetResolvedScriptURI();
   nsCString url = scriptURI->GetSpecOrDefault();
 
-  // Workers only produce metrics for a single category - DispatchCategory::Worker.
-  // We still return an array of CategoryDispatch so the PerformanceInfo
-  // struct is common to all performance counters throughout Firefox.
+  // Workers only produce metrics for a single category -
+  // DispatchCategory::Worker. We still return an array of CategoryDispatch so
+  // the PerformanceInfo struct is common to all performance counters throughout
+  // Firefox.
   FallibleTArray<CategoryDispatch> items;
   uint64_t duration = 0;
   uint16_t count = 0;
   uint64_t perfId = 0;
 
   RefPtr<PerformanceCounter> perf = mWorkerPrivate->GetPerformanceCounter();
   if (perf) {
     perfId = perf->GetID();
-    count =  perf->GetTotalDispatchCount();
+    count = perf->GetTotalDispatchCount();
     duration = perf->GetExecutionDuration();
-    CategoryDispatch item = CategoryDispatch(DispatchCategory::Worker.GetValue(), count);
+    CategoryDispatch item =
+      CategoryDispatch(DispatchCategory::Worker.GetValue(), count);
     if (!items.AppendElement(item, fallible)) {
       NS_ERROR("Could not complete the operation");
-      return PerformanceInfo(url, pid, windowID, duration, perfId, true, isTopLevel, items);
     }
   }
 
-  return PerformanceInfo(url, pid, windowID, duration, perfId, true, isTopLevel, items);
+  if (!isTopLevel) {
+    return PerformanceInfoPromise::CreateAndResolve(PerformanceInfo(url,
+                                                                    pid,
+                                                                    windowID,
+                                                                    duration,
+                                                                    perfId,
+                                                                    true,
+                                                                    isTopLevel,
+                                                                    memoryInfo,
+                                                                    items),
+                                                    __func__);
+  }
+
+  // We need to keep a ref on workerPrivate, passed to the promise,
+  // to make sure it's still aloive when collecting the info.
+  RefPtr<WorkerPrivate> workerRef = mWorkerPrivate;
+  RefPtr<AbstractThread> mainThread =
+    SystemGroup::AbstractMainThreadFor(TaskCategory::Performance);
+
+  return CollectMemoryInfo(top, mainThread)
+    ->Then(mainThread,
+           __func__,
+           [workerRef, url, pid, perfId, windowID, duration, isTopLevel, items](
+             const PerformanceMemoryInfo& aMemoryInfo) {
+             return PerformanceInfoPromise::CreateAndResolve(
+               PerformanceInfo(url,
+                               pid,
+                               windowID,
+                               duration,
+                               perfId,
+                               true,
+                               isTopLevel,
+                               aMemoryInfo,
+                               items),
+               __func__);
+           },
+           [workerRef]() {
+             return PerformanceInfoPromise::CreateAndReject(NS_ERROR_FAILURE,
+                                                            __func__);
+           });
 }
 
 } // dom namespace
 } // mozilla namespace
--- a/dom/workers/WorkerDebugger.h
+++ b/dom/workers/WorkerDebugger.h
@@ -2,16 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_workers_WorkerDebugger_h
 #define mozilla_dom_workers_WorkerDebugger_h
 
+#include "mozilla/PerformanceTypes.h"
 #include "mozilla/dom/DOMTypes.h"
 #include "mozilla/dom/WorkerCommon.h"
 #include "nsIWorkerDebugger.h"
 
 namespace mozilla {
 namespace dom {
 
 class WorkerPrivate;
@@ -43,17 +44,17 @@ public:
   void
   ReportErrorToDebugger(const nsAString& aFilename, uint32_t aLineno,
                         const nsAString& aMessage);
 
   /*
    * Sends back a PerformanceInfo struct from the counters
    * in mWorkerPrivate. Counters are reset to zero after this call.
    */
-  PerformanceInfo
+  RefPtr<PerformanceInfoPromise>
   ReportPerformanceInfo();
 
 private:
   virtual
   ~WorkerDebugger();
 
   void
   PostMessageToDebuggerOnMainThread(const nsAString& aMessage);
--- a/gfx/thebes/gfxContext.h
+++ b/gfx/thebes/gfxContext.h
@@ -144,20 +144,22 @@ public:
     // path helpers
     /**
      * Draws a line from start to end.
      */
     void Line(const gfxPoint& start, const gfxPoint& end); // XXX snapToPixels option?
 
     /**
      * Draws the rectangle given by rect.
-     * @param snapToPixels ?
      */
-    void Rectangle(const gfxRect& rect, bool snapToPixels = false);
+    void Rectangle(const gfxRect& rect) { return Rectangle(rect, false); }
     void SnappedRectangle(const gfxRect& rect) { return Rectangle(rect, true); }
+private:
+    void Rectangle(const gfxRect& rect, bool snapToPixels);
+public:
 
     /**
      ** Transformation Matrix manipulation
      **/
 
     /**
      * Post-multiplies 'other' onto the current CTM, i.e. this
      * matrix's transformation will take place before the previously set
--- a/gfx/thebes/gfxTextRun.cpp
+++ b/gfx/thebes/gfxTextRun.cpp
@@ -557,20 +557,20 @@ struct MOZ_STACK_CLASS BufferAlphaColor 
     }
 
     ~BufferAlphaColor() {}
 
     void PushSolidColor(const gfxRect& aBounds, const Color& aAlphaColor, uint32_t appsPerDevUnit)
     {
         mContext->Save();
         mContext->NewPath();
-        mContext->Rectangle(gfxRect(aBounds.X() / appsPerDevUnit,
+        mContext->SnappedRectangle(gfxRect(aBounds.X() / appsPerDevUnit,
                     aBounds.Y() / appsPerDevUnit,
                     aBounds.Width() / appsPerDevUnit,
-                    aBounds.Height() / appsPerDevUnit), true);
+                    aBounds.Height() / appsPerDevUnit));
         mContext->Clip();
         mContext->SetColor(Color(aAlphaColor.r, aAlphaColor.g, aAlphaColor.b));
         mContext->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, aAlphaColor.a);
     }
 
     void PopAlpha()
     {
         // pop the text, using the color alpha as the opacity
--- a/js/public/Stream.h
+++ b/js/public/Stream.h
@@ -446,46 +446,47 @@ ReadableStreamEnqueue(JSContext* cx, Han
  *
  * Asserts that |stream| is a ReadableStream object or an unwrappable wrapper
  * for one.
  */
 extern JS_PUBLIC_API bool
 ReadableStreamError(JSContext* cx, HandleObject stream, HandleValue error);
 
 /**
- * Cancels the given ReadableStream reader's associated stream.
+ * C++ equivalent of `reader.cancel(reason)`
+ * (both <https://streams.spec.whatwg.org/#default-reader-cancel> and
+ * <https://streams.spec.whatwg.org/#byob-reader-cancel>).
  *
- * Throws a TypeError and returns false if the given reader isn't active.
- *
- * Asserts that |reader| is a ReadableStreamDefaultReader or
- * ReadableStreamBYOBReader object or an unwrappable wrapper for one.
+ * `reader` must be a stream reader created using `JS::ReadableStreamGetReader`
+ * or an unwrappable wrapper for one. (This function is meant to support using
+ * C++ to read from streams. It's not meant to allow C++ code to operate on
+ * readers created by scripts.)
  */
 extern JS_PUBLIC_API bool
 ReadableStreamReaderCancel(JSContext* cx, HandleObject reader, HandleValue reason);
 
 /**
- * Cancels the given ReadableStream reader's associated stream.
+ * C++ equivalent of `reader.releaseLock()`
+ * (both <https://streams.spec.whatwg.org/#default-reader-release-lock> and
+ * <https://streams.spec.whatwg.org/#byob-reader-release-lock>).
  *
- * Throws a TypeError and returns false if the given reader has pending
- * read or readInto (for default or byob readers, respectively) requests.
- *
- * Asserts that |reader| is a ReadableStreamDefaultReader or
- * ReadableStreamBYOBReader object or an unwrappable wrapper for one.
+ * `reader` must be a stream reader created using `JS::ReadableStreamGetReader`
+ * or an unwrappable wrapper for one.
  */
 extern JS_PUBLIC_API bool
 ReadableStreamReaderReleaseLock(JSContext* cx, HandleObject reader);
 
 /**
- * Requests a read from the reader's associated ReadableStream and returns the
- * resulting PromiseObject.
+ * C++ equivalent of the `reader.read()` method on default readers
+ * (<https://streams.spec.whatwg.org/#default-reader-read>).
  *
- * Returns a Promise that's resolved with the read result once available or
- * rejected immediately if the stream is errored or the operation failed.
+ * The result is a new Promise object, or null on OOM.
  *
- * Asserts that |reader| is a ReadableStreamDefaultReader object or an
- * unwrappable wrapper for one.
+ * `reader` must be the result of calling `JS::ReadableStreamGetReader` with
+ * `ReadableStreamReaderMode::Default` mode, or an unwrappable wrapper for such
+ * a reader.
  */
 extern JS_PUBLIC_API JSObject*
 ReadableStreamDefaultReaderRead(JSContext* cx, HandleObject reader);
 
 } // namespace JS
 
 #endif // js_Realm_h
--- a/js/src/builtin/Stream.cpp
+++ b/js/src/builtin/Stream.cpp
@@ -737,17 +737,19 @@ ReadableStream_cancel(JSContext* cx, uns
     if (!cancelPromise) {
         return false;
     }
     args.rval().setObject(*cancelPromise);
     return true;
 }
 
 static MOZ_MUST_USE ReadableStreamDefaultReader*
-CreateReadableStreamDefaultReader(JSContext* cx, Handle<ReadableStream*> unwrappedStream);
+CreateReadableStreamDefaultReader(JSContext* cx,
+                                  Handle<ReadableStream*> unwrappedStream,
+                                  ForAuthorCodeBool forAuthorCode = ForAuthorCodeBool::No);
 
 /**
  * Streams spec, 3.2.5.3. getReader()
  */
 static bool
 ReadableStream_getReader(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
@@ -767,17 +769,17 @@ ReadableStream_getReader(JSContext* cx, 
     HandleValue optionsVal = args.get(0);
     if (!optionsVal.isUndefined()) {
         if (!GetProperty(cx, optionsVal, cx->names().mode, &modeVal)) {
             return false;
         }
     }
 
     if (modeVal.isUndefined()) {
-        reader = CreateReadableStreamDefaultReader(cx, unwrappedStream);
+        reader = CreateReadableStreamDefaultReader(cx, unwrappedStream, ForAuthorCodeBool::Yes);
     } else {
         // Step 3: Set mode to ? ToString(mode) (implicit).
         RootedString mode(cx, ToString<CanGC>(cx, modeVal));
         if (!mode) {
             return false;
         }
 
         // Step 4: If mode is "byob", return ? AcquireReadableStreamBYOBReader(this).
@@ -1287,18 +1289,22 @@ ReadableStreamTee(JSContext* cx,
 
 inline static MOZ_MUST_USE bool
 AppendToListAtSlot(JSContext* cx,
                    HandleNativeObject unwrappedContainer,
                    uint32_t slot,
                    HandleObject obj);
 
 /**
- * Streams spec, 3.4.1. ReadableStreamAddReadIntoRequest ( stream )
- * Streams spec, 3.4.2. ReadableStreamAddReadRequest ( stream )
+ * Streams spec, 3.4.1. ReadableStreamAddReadIntoRequest ( stream, forAuthorCode )
+ * Streams spec, 3.4.2. ReadableStreamAddReadRequest ( stream, forAuthorCode )
+ *
+ * Our implementation does not pass around forAuthorCode parameters in the same
+ * places as the standard, but the effect is the same. See the comment on
+ * `ReadableStreamReader::forAuthorCode()`.
  */
 static MOZ_MUST_USE JSObject*
 ReadableStreamAddReadOrReadIntoRequest(JSContext* cx, Handle<ReadableStream*> unwrappedStream)
 {
     // Step 1: Assert: ! IsReadableStreamBYOBReader(stream.[[reader]]) is true.
     // Skipped: handles both kinds of readers.
     Rooted<ReadableStreamReader*> unwrappedReader(cx, UnwrapReaderFromStream(cx, unwrappedStream));
     if (!unwrappedReader) {
@@ -1309,20 +1315,23 @@ ReadableStreamAddReadOrReadIntoRequest(J
     MOZ_ASSERT_IF(unwrappedReader->is<ReadableStreamDefaultReader>(), unwrappedStream->readable());
 
     // Step 3: Let promise be a new promise.
     RootedObject promise(cx, PromiseObject::createSkippingExecutor(cx));
     if (!promise) {
         return nullptr;
     }
 
-    // Step 4: Let read{Into}Request be Record {[[promise]]: promise}.
+    // Step 4: Let read{Into}Request be
+    //         Record {[[promise]]: promise, [[forAuthorCode]]: forAuthorCode}.
     // Step 5: Append read{Into}Request as the last element of
     //         stream.[[reader]].[[read{Into}Requests]].
-    // Since [[promise]] is the Record's only field, we store it directly.
+    // Since we don't need the [[forAuthorCode]] field (see the comment on
+    // `ReadableStreamReader::forAuthorCode()`), we elide the Record and store
+    // only the promise.
     if (!AppendToListAtSlot(cx, unwrappedReader, ReadableStreamReader::Slot_Requests, promise)) {
         return nullptr;
     }
 
     // Step 6: Return promise.
     return promise;
 }
 
@@ -1391,16 +1400,22 @@ ReadableStreamCancel(JSContext* cx, Hand
     RootedAtom funName(cx, cx->names().empty);
     RootedFunction returnUndefined(cx, NewNativeFunction(cx, ReturnUndefined, 0, funName));
     if (!returnUndefined) {
         return nullptr;
     }
     return JS::CallOriginalPromiseThen(cx, sourceCancelPromise, returnUndefined, nullptr);
 }
 
+static MOZ_MUST_USE JSObject*
+ReadableStreamCreateReadResult(JSContext* cx,
+                               HandleValue value,
+                               bool done,
+                               ForAuthorCodeBool forAuthorCode);
+
 /**
  * Streams spec, 3.4.4. ReadableStreamClose ( stream )
  */
 MOZ_MUST_USE bool
 ReadableStreamCloseInternal(JSContext* cx, Handle<ReadableStream*> unwrappedStream)
 {
     // Step 1: Assert: stream.[[state]] is "readable".
     MOZ_ASSERT(unwrappedStream->readable());
@@ -1416,32 +1431,36 @@ ReadableStreamCloseInternal(JSContext* c
     // Step 3: Let reader be stream.[[reader]].
     Rooted<ReadableStreamReader*> unwrappedReader(cx, UnwrapReaderFromStream(cx, unwrappedStream));
     if (!unwrappedReader) {
         return false;
     }
 
     // Step 5: If ! IsReadableStreamDefaultReader(reader) is true,
     if (unwrappedReader->is<ReadableStreamDefaultReader>()) {
+        ForAuthorCodeBool forAuthorCode = unwrappedReader->forAuthorCode();
+
         // Step a: Repeat for each readRequest that is an element of
         //         reader.[[readRequests]],
         RootedNativeObject unwrappedReadRequests(cx, unwrappedReader->requests());
         uint32_t len = unwrappedReadRequests->getDenseInitializedLength();
         RootedObject readRequest(cx);
         RootedObject resultObj(cx);
         RootedValue resultVal(cx);
         for (uint32_t i = 0; i < len; i++) {
             // Step i: Resolve readRequest.[[promise]] with
-            //         ! CreateIterResultObject(undefined, true).
+            //         ! ReadableStreamCreateReadResult(undefined, true,
+            //                                          readRequest.[[forAuthorCode]]).
             readRequest = &unwrappedReadRequests->getDenseElement(i).toObject();
             if (!cx->compartment()->wrap(cx, &readRequest)) {
                 return false;
             }
 
-            resultObj = CreateIterResultObject(cx, UndefinedHandleValue, true);
+            resultObj = ReadableStreamCreateReadResult(cx, UndefinedHandleValue, true,
+                                                       forAuthorCode);
             if (!resultObj) {
                 return false;
             }
             resultVal = ObjectValue(*resultObj);
             if (!ResolvePromise(cx, readRequest, resultVal)) {
                 return false;
             }
         }
@@ -1472,16 +1491,50 @@ ReadableStreamCloseInternal(JSContext* c
                                                     source,
                                                     unwrappedStream->embeddingFlags());
     }
 
     return true;
 }
 
 /**
+ * Streams spec, 3.4.5. ReadableStreamCreateReadResult ( value, done, forAuthorCode )
+ */
+static MOZ_MUST_USE JSObject*
+ReadableStreamCreateReadResult(JSContext* cx,
+                               HandleValue value,
+                               bool done,
+                               ForAuthorCodeBool forAuthorCode)
+{
+    // Step 1: Let prototype be null.
+    // Step 2: If forAuthorCode is true, set prototype to %ObjectPrototype%.
+    RootedObject templateObject(cx,
+        forAuthorCode == ForAuthorCodeBool::Yes
+        ? cx->realm()->getOrCreateIterResultTemplateObject(cx)
+        : cx->realm()->getOrCreateIterResultWithoutPrototypeTemplateObject(cx));
+
+    // Step 3: Assert: Type(done) is Boolean (implicit).
+
+    // Step 4: Let obj be ObjectCreate(prototype).
+    NativeObject* obj;
+    JS_TRY_VAR_OR_RETURN_NULL(cx, obj, NativeObject::createWithTemplate(cx, gc::DefaultHeap,
+                                                                        templateObject));
+
+    // Step 5: Perform CreateDataProperty(obj, "value", value).
+    obj->setSlot(Realm::IterResultObjectValueSlot, value);
+
+    // Step 6: Perform CreateDataProperty(obj, "done", done).
+    obj->setSlot(Realm::IterResultObjectDoneSlot,
+                 done ? TrueHandleValue : FalseHandleValue);
+
+    // Step 7: Return obj.
+    return obj;
+}
+
+/**
  * Streams spec, 3.4.6. ReadableStreamError ( stream, e )
  */
 MOZ_MUST_USE bool
 ReadableStreamErrorInternal(JSContext* cx, Handle<ReadableStream*> unwrappedStream, HandleValue e)
 {
     // Step 1: Assert: ! IsReadableStream(stream) is true (implicit).
 
     // Step 2: Assert: stream.[[state]] is "readable".
@@ -1605,19 +1658,20 @@ ReadableStreamFulfillReadOrReadIntoReque
     //         and so on).
     RootedNativeObject unwrappedReadIntoRequests(cx, unwrappedReader->requests());
     RootedObject readIntoRequest(cx, ShiftFromList<JSObject>(cx, unwrappedReadIntoRequests));
     MOZ_ASSERT(readIntoRequest);
     if (!cx->compartment()->wrap(cx, &readIntoRequest)) {
         return false;
     }
 
-    // Step 4: Resolve readIntoRequest.[[promise]] with
-    //         ! CreateIterResultObject(chunk, done).
-    RootedObject iterResult(cx, CreateIterResultObject(cx, chunk, done));
+    // Step 4: Resolve read{Into}Request.[[promise]] with
+    //         ! ReadableStreamCreateReadResult(chunk, done, readIntoRequest.[[forAuthorCode]]).
+    RootedObject iterResult(cx,
+        ReadableStreamCreateReadResult(cx, chunk, done, unwrappedReader->forAuthorCode()));
     if (!iterResult) {
         return false;
     }
     RootedValue val(cx, ObjectValue(*iterResult));
     return ResolvePromise(cx, readIntoRequest, val);
 }
 
 /**
@@ -1672,40 +1726,43 @@ ReadableStreamHasDefaultReader(JSContext
 }
 
 
 /*** 3.5. Class ReadableStreamDefaultReader ******************************************************/
 
 static MOZ_MUST_USE bool
 ReadableStreamReaderGenericInitialize(JSContext* cx,
                                       Handle<ReadableStreamReader*> reader,
-                                      Handle<ReadableStream*> unwrappedStream);
+                                      Handle<ReadableStream*> unwrappedStream,
+                                      ForAuthorCodeBool forAuthorCode);
 
 /**
  * Stream spec, 3.5.3. new ReadableStreamDefaultReader ( stream )
  * Steps 2-4.
  */
 static MOZ_MUST_USE ReadableStreamDefaultReader*
-CreateReadableStreamDefaultReader(JSContext* cx, Handle<ReadableStream*> unwrappedStream)
+CreateReadableStreamDefaultReader(JSContext* cx,
+                                  Handle<ReadableStream*> unwrappedStream,
+                                  ForAuthorCodeBool forAuthorCode)
 {
     Rooted<ReadableStreamDefaultReader*> reader(cx,
         NewBuiltinClassInstance<ReadableStreamDefaultReader>(cx));
     if (!reader) {
         return nullptr;
     }
 
     // Step 2: If ! IsReadableStreamLocked(stream) is true, throw a TypeError
     //         exception.
     if (unwrappedStream->locked()) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_READABLESTREAM_LOCKED);
         return nullptr;
     }
 
     // Step 3: Perform ! ReadableStreamReaderGenericInitialize(this, stream).
-    if (!ReadableStreamReaderGenericInitialize(cx, reader, unwrappedStream)) {
+    if (!ReadableStreamReaderGenericInitialize(cx, reader, unwrappedStream, forAuthorCode)) {
         return nullptr;
     }
 
     // Step 4: Set this.[[readRequests]] to a new empty List.
     if (!SetNewList(cx, reader, ReadableStreamReader::Slot_Requests)) {
         return nullptr;
     }
 
@@ -1826,17 +1883,17 @@ ReadableStreamDefaultReader_read(JSConte
     // Step 2: If this.[[ownerReadableStream]] is undefined, return a promise
     //         rejected with a TypeError exception.
     if (!unwrappedReader->hasStream()) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                                   JSMSG_READABLESTREAMREADER_NOT_OWNED, "read");
         return ReturnPromiseRejectedWithPendingError(cx, args);
     }
 
-    // Step 3: Return ! ReadableStreamDefaultReaderRead(this).
+    // Step 3: Return ! ReadableStreamDefaultReaderRead(this, true).
     JSObject* readPromise = ::ReadableStreamDefaultReaderRead(cx, unwrappedReader);
     if (!readPromise) {
         return false;
     }
     args.rval().setObject(*readPromise);
     return true;
 }
 
@@ -1932,18 +1989,20 @@ ReadableStreamReaderGenericCancel(JSCont
     // Step 3: Return ! ReadableStreamCancel(stream, reason).
     return ::ReadableStreamCancel(cx, unwrappedStream, reason);
 }
 
 /**
  * Streams spec, 3.7.4. ReadableStreamReaderGenericInitialize ( reader, stream )
  */
 static MOZ_MUST_USE bool
-ReadableStreamReaderGenericInitialize(JSContext* cx, Handle<ReadableStreamReader*> reader,
-                                      Handle<ReadableStream*> unwrappedStream)
+ReadableStreamReaderGenericInitialize(JSContext* cx,
+                                      Handle<ReadableStreamReader*> reader,
+                                      Handle<ReadableStream*> unwrappedStream,
+                                      ForAuthorCodeBool forAuthorCode)
 {
     cx->check(reader);
 
     // Step 1: Set reader.[[ownerReadableStream]] to stream.
     {
         RootedObject readerCompartmentStream(cx, unwrappedStream);
         if (!cx->compartment()->wrap(cx, &readerCompartmentStream)) {
             return false;
@@ -1986,16 +2045,21 @@ ReadableStreamReaderGenericInitialize(JS
         promise = PromiseObject::unforgeableReject(cx, storedError);
     }
 
     if (!promise) {
         return false;
     }
 
     reader->setClosedPromise(promise);
+
+    // Extra step not in the standard. See the comment on
+    // `ReadableStreamReader::forAuthorCode()`.
+    reader->setForAuthorCode(forAuthorCode);
+
     return true;
 }
 
 /**
  * Streams spec, 3.7.5. ReadableStreamReaderGenericRelease ( reader )
  */
 static MOZ_MUST_USE bool
 ReadableStreamReaderGenericRelease(JSContext* cx, Handle<ReadableStreamReader*> unwrappedReader)
@@ -2061,57 +2125,61 @@ ReadableStreamReaderGenericRelease(JSCon
     return true;
 }
 
 static MOZ_MUST_USE JSObject*
 ReadableStreamControllerPullSteps(JSContext* cx,
                                   Handle<ReadableStreamController*> unwrappedController);
 
 /**
- * Streams spec, 3.7.7. ReadableStreamDefaultReaderRead ( reader )
+ * Streams spec, 3.7.7. ReadableStreamDefaultReaderRead ( reader [, forAuthorCode ] )
  */
 static MOZ_MUST_USE JSObject*
 ReadableStreamDefaultReaderRead(JSContext* cx,
                                 Handle<ReadableStreamDefaultReader*> unwrappedReader)
 {
-    // Step 1: Let stream be reader.[[ownerReadableStream]].
-    // Step 2: Assert: stream is not undefined.
+    // Step 1: If forAuthorCode was not passed, set it to false (implicit).
+
+    // Step 2: Let stream be reader.[[ownerReadableStream]].
+    // Step 3: Assert: stream is not undefined.
     Rooted<ReadableStream*> unwrappedStream(cx, UnwrapStreamFromReader(cx, unwrappedReader));
     if (!unwrappedStream) {
         return nullptr;
     }
 
-    // Step 3: Set stream.[[disturbed]] to true.
+    // Step 4: Set stream.[[disturbed]] to true.
     unwrappedStream->setDisturbed();
 
-    // Step 4: If stream.[[state]] is "closed", return a new promise resolved with
-    //         ! CreateIterResultObject(undefined, true).
+    // Step 5: If stream.[[state]] is "closed", return a new promise resolved with
+    //         ! ReadableStreamCreateReadResult(undefined, true, forAuthorCode).
     if (unwrappedStream->closed()) {
-        RootedObject iterResult(cx, CreateIterResultObject(cx, UndefinedHandleValue, true));
+        RootedObject iterResult(cx,
+            ReadableStreamCreateReadResult(cx, UndefinedHandleValue, true,
+                                           unwrappedReader->forAuthorCode()));
         if (!iterResult) {
             return nullptr;
         }
         RootedValue iterResultVal(cx, ObjectValue(*iterResult));
         return PromiseObject::unforgeableResolve(cx, iterResultVal);
     }
 
-    // Step 5: If stream.[[state]] is "errored", return a new promise rejected with
+    // Step 6: If stream.[[state]] is "errored", return a new promise rejected with
     //         stream.[[storedError]].
     if (unwrappedStream->errored()) {
         RootedValue storedError(cx, unwrappedStream->storedError());
         if (!cx->compartment()->wrap(cx, &storedError)) {
             return nullptr;
         }
         return PromiseObject::unforgeableReject(cx, storedError);
     }
 
-    // Step 6: Assert: stream.[[state]] is "readable".
+    // Step 7: Assert: stream.[[state]] is "readable".
     MOZ_ASSERT(unwrappedStream->readable());
 
-    // Step 7: Return ! stream.[[readableStreamController]].[[PullSteps]]().
+    // Step 8: Return ! stream.[[readableStreamController]].[[PullSteps]]().
     Rooted<ReadableStreamController*> unwrappedController(cx, unwrappedStream->controller());
     return ReadableStreamControllerPullSteps(cx, unwrappedController);
 }
 
 
 /*** 3.8. Class ReadableStreamDefaultController **************************************************/
 
 inline static MOZ_MUST_USE bool
@@ -2575,17 +2643,17 @@ ReadableStreamControllerCancelSteps(JSCo
 }
 
 inline static MOZ_MUST_USE bool
 DequeueValue(JSContext* cx,
              Handle<ReadableStreamController*> unwrappedContainer,
              MutableHandleValue chunk);
 
 /**
- * Streams spec, 3.8.5.2. ReadableStreamDefaultController [[PullSteps]]()
+ * Streams spec, 3.8.5.2. ReadableStreamDefaultController [[PullSteps]]( forAuthorCode )
  */
 static JSObject*
 ReadableStreamDefaultControllerPullSteps(JSContext* cx,
                                          Handle<ReadableStreamDefaultController*> unwrappedController)
 {
     // Step 1: Let stream be this.[[controlledReadableStream]].
     Rooted<ReadableStream*> unwrappedStream(cx, unwrappedController->stream());
 
@@ -2615,27 +2683,34 @@ ReadableStreamDefaultControllerPullSteps
 
         // Step c: Otherwise, perform ! ReadableStreamDefaultControllerCallPullIfNeeded(this).
         else {
             if (!ReadableStreamControllerCallPullIfNeeded(cx, unwrappedController)) {
                 return nullptr;
             }
         }
 
-        // Step d: Return a promise resolved with ! CreateIterResultObject(chunk, false).
+        // Step d: Return a promise resolved with
+        //         ! ReadableStreamCreateReadResult(chunk, false, forAuthorCode).
         cx->check(chunk);
-        RootedObject iterResultObj(cx, CreateIterResultObject(cx, chunk, false));
-        if (!iterResultObj) {
+        ReadableStreamReader* unwrappedReader = UnwrapReaderFromStream(cx, unwrappedStream);
+        if (!unwrappedReader) {
             return nullptr;
         }
-        RootedValue iterResult(cx, ObjectValue(*iterResultObj));
-        return PromiseObject::unforgeableResolve(cx, iterResult);
-    }
-
-    // Step 3: Let pendingPromise be ! ReadableStreamAddReadRequest(stream).
+        RootedObject readResultObj(cx,
+            ReadableStreamCreateReadResult(cx, chunk, false, unwrappedReader->forAuthorCode()));
+        if (!readResultObj) {
+            return nullptr;
+        }
+        RootedValue readResult(cx, ObjectValue(*readResultObj));
+        return PromiseObject::unforgeableResolve(cx, readResult);
+    }
+
+    // Step 3: Let pendingPromise be
+    //         ! ReadableStreamAddReadRequest(stream, forAuthorCode).
     RootedObject pendingPromise(cx, ReadableStreamAddReadOrReadIntoRequest(cx, unwrappedStream));
     if (!pendingPromise) {
         return nullptr;
     }
 
     // Step 4: Perform ! ReadableStreamDefaultControllerCallPullIfNeeded(this).
     if (!ReadableStreamControllerCallPullIfNeeded(cx, unwrappedController)) {
         return nullptr;
@@ -3292,17 +3367,17 @@ CLASS_SPEC(ReadableByteStreamController,
 // Streams spec, 3.10.5.1. [[CancelSteps]] ()
 // Unified with 3.8.5.1 above.
 
 static MOZ_MUST_USE bool
 ReadableByteStreamControllerHandleQueueDrain(JSContext* cx,
                                              Handle<ReadableStreamController*> unwrappedController);
 
 /**
- * Streams spec, 3.10.5.2. [[PullSteps]] ()
+ * Streams spec, 3.10.5.2. [[PullSteps]] ( forAuthorCode )
  */
 static MOZ_MUST_USE JSObject*
 ReadableByteStreamControllerPullSteps(JSContext* cx,
                                       Handle<ReadableByteStreamController*> unwrappedController)
 {
     // Step 1: Let stream be this.[[controlledReadableStream]].
     Rooted<ReadableStream*> unwrappedStream(cx, unwrappedController->stream());
 
@@ -3381,23 +3456,29 @@ ReadableByteStreamControllerPullSteps(JS
         unwrappedController->setQueueTotalSize(queueTotalSize);
 
         // Step 3.e: Perform ! ReadableByteStreamControllerHandleQueueDrain(this).
         // (reordered)
         if (!ReadableByteStreamControllerHandleQueueDrain(cx, unwrappedController)) {
             return nullptr;
         }
 
-        // Step 3.g: Return a promise resolved with ! CreateIterResultObject(view, false).
+        // Step 3.g: Return a promise resolved with
+        //           ! ReadableStreamCreateReadResult(view, false, forAuthorCode).
         val.setObject(*view);
-        RootedObject iterResult(cx, CreateIterResultObject(cx, val, false));
-        if (!iterResult) {
+        ReadableStreamReader* unwrappedReader = UnwrapReaderFromStream(cx, unwrappedStream);
+        if (!unwrappedReader) {
             return nullptr;
         }
-        val.setObject(*iterResult);
+        RootedObject readResult(cx,
+            ReadableStreamCreateReadResult(cx, val, false, unwrappedReader->forAuthorCode()));
+        if (!readResult) {
+            return nullptr;
+        }
+        val.setObject(*readResult);
 
         return PromiseObject::unforgeableResolve(cx, val);
     }
 
     // Step 4: Let autoAllocateChunkSize be this.[[autoAllocateChunkSize]].
     val = unwrappedController->autoAllocateChunkSize();
 
     // Step 5: If autoAllocateChunkSize is not undefined,
@@ -3435,17 +3516,17 @@ ReadableByteStreamControllerPullSteps(JS
                                 unwrappedController,
                                 ReadableByteStreamController::Slot_PendingPullIntos,
                                 pullIntoDescriptor))
         {
             return nullptr;
         }
     }
 
-    // Step 6: Let promise be ! ReadableStreamAddReadRequest(stream).
+    // Step 6: Let promise be ! ReadableStreamAddReadRequest(stream, forAuthorCode).
     RootedObject promise(cx, ReadableStreamAddReadOrReadIntoRequest(cx, unwrappedStream));
     if (!promise) {
         return nullptr;
     }
 
     // Step 7: Perform ! ReadableByteStreamControllerCallPullIfNeeded(this).
     if (!ReadableStreamControllerCallPullIfNeeded(cx, unwrappedController)) {
         return nullptr;
@@ -3453,19 +3534,19 @@ ReadableByteStreamControllerPullSteps(JS
 
     // Step 8: Return promise.
     return promise;
 }
 
 /**
  * Unified implementation of ReadableStream controllers' [[PullSteps]] internal
  * methods.
- * Streams spec, 3.8.5.2. [[PullSteps]] ()
+ * Streams spec, 3.8.5.2. [[PullSteps]] ( forAuthorCode )
  * and
- * Streams spec, 3.10.5.2. [[PullSteps]] ()
+ * Streams spec, 3.10.5.2. [[PullSteps]] ( forAuthorCode )
  */
 static MOZ_MUST_USE JSObject*
 ReadableStreamControllerPullSteps(JSContext* cx, Handle<ReadableStreamController*> unwrappedController)
 {
     if (unwrappedController->is<ReadableStreamDefaultController>()) {
         Rooted<ReadableStreamDefaultController*> unwrappedDefaultController(cx,
             &unwrappedController->as<ReadableStreamDefaultController>());
         return ReadableStreamDefaultControllerPullSteps(cx, unwrappedDefaultController);
@@ -4512,30 +4593,35 @@ JS::ReadableStreamReaderCancel(JSContext
     CHECK_THREAD(cx);
     cx->check(reason);
 
     Rooted<ReadableStreamReader*> unwrappedReader(cx,
         APIToUnwrapped<ReadableStreamReader>(cx, readerObj));
     if (!unwrappedReader) {
         return false;
     }
+    MOZ_ASSERT(unwrappedReader->forAuthorCode() == ForAuthorCodeBool::No,
+               "C++ code should not touch readers created by scripts");
 
     return ReadableStreamReaderGenericCancel(cx, unwrappedReader, reason);
 }
 
 JS_PUBLIC_API bool
 JS::ReadableStreamReaderReleaseLock(JSContext* cx, HandleObject readerObj)
 {
     AssertHeapIsIdle();
     CHECK_THREAD(cx);
 
-    Rooted<ReadableStreamReader*> unwrappedReader(cx, APIToUnwrapped<ReadableStreamReader>(cx, readerObj));
+    Rooted<ReadableStreamReader*> unwrappedReader(cx,
+        APIToUnwrapped<ReadableStreamReader>(cx, readerObj));
     if (!unwrappedReader) {
         return false;
     }
+    MOZ_ASSERT(unwrappedReader->forAuthorCode() == ForAuthorCodeBool::No,
+               "C++ code should not touch readers created by scripts");
 
 #ifdef DEBUG
     Rooted<ReadableStream*> unwrappedStream(cx, UnwrapStreamFromReader(cx, unwrappedReader));
     if (!unwrappedStream) {
         return false;
     }
     MOZ_ASSERT(ReadableStreamGetNumReadRequests(unwrappedStream) == 0);
 #endif // DEBUG
@@ -4549,11 +4635,13 @@ JS::ReadableStreamDefaultReaderRead(JSCo
     AssertHeapIsIdle();
     CHECK_THREAD(cx);
 
     Rooted<ReadableStreamDefaultReader*> unwrappedReader(cx);
     unwrappedReader = APIToUnwrapped<ReadableStreamDefaultReader>(cx, readerObj);
     if (!unwrappedReader) {
         return nullptr;
     }
+    MOZ_ASSERT(unwrappedReader->forAuthorCode() == ForAuthorCodeBool::No,
+               "C++ code should not touch readers created by scripts");
 
     return ::ReadableStreamDefaultReaderRead(cx, unwrappedReader);
 }
--- a/js/src/builtin/Stream.h
+++ b/js/src/builtin/Stream.h
@@ -110,16 +110,23 @@ class ReadableStream : public NativeObje
   public:
     static bool constructor(JSContext* cx, unsigned argc, Value* vp);
     static const ClassSpec classSpec_;
     static const Class class_;
     static const ClassSpec protoClassSpec_;
     static const Class protoClass_;
 };
 
+/**
+ * Tells whether or not read() result objects inherit from Object.prototype.
+ * Generally, they should do so only if the reader was created by author code.
+ * See <https://streams.spec.whatwg.org/#readable-stream-create-read-result>.
+ */
+enum class ForAuthorCodeBool { No, Yes };
+
 class ReadableStreamReader : public NativeObject
 {
   public:
     /**
      * Memory layout of Stream Reader instances.
      *
      * See https://streams.spec.whatwg.org/#default-reader-internal-slots and
      * https://streams.spec.whatwg.org/#byob-reader-internal-slots for details.
@@ -138,24 +145,47 @@ class ReadableStreamReader : public Nati
      *
      * Requests is guaranteed to be in the same compartment as the Reader, but
      * can contain wrapped request objects from other globals.
      */
     enum Slots {
         Slot_Stream,
         Slot_Requests,
         Slot_ClosedPromise,
+        Slot_ForAuthorCode,
         SlotCount,
     };
 
     bool hasStream() const { return !getFixedSlot(Slot_Stream).isUndefined(); }
     void setStream(JSObject* stream) { setFixedSlot(Slot_Stream, ObjectValue(*stream)); }
     void clearStream() { setFixedSlot(Slot_Stream, UndefinedValue()); }
     bool isClosed() { return !hasStream(); }
 
+    /**
+     * Tells whether this reader was created by author code.
+     *
+     * This returns Yes for readers created using `stream.getReader()`, and No
+     * for readers created for the internal use of algorithms like
+     * `stream.tee()` and `new Response(stream)`.
+     *
+     * The standard does not have this field. Instead, eight algorithms take a
+     * forAuthorCode parameter, and a [[forAuthorCode]] field is part of each
+     * read request. But the behavior is always equivalent to treating readers
+     * created by author code as having a bit set on them. We implement it that
+     * way for simplicity.
+     */
+    ForAuthorCodeBool forAuthorCode() const {
+        return getFixedSlot(Slot_ForAuthorCode).toBoolean()
+               ? ForAuthorCodeBool::Yes
+               : ForAuthorCodeBool::No;
+    }
+    void setForAuthorCode(ForAuthorCodeBool value) {
+        setFixedSlot(Slot_ForAuthorCode, BooleanValue(value == ForAuthorCodeBool::Yes));
+    }
+
     NativeObject* requests() const {
         return &getFixedSlot(Slot_Requests).toObject().as<NativeObject>();
     }
     void clearRequests() { setFixedSlot(Slot_Requests, UndefinedValue()); }
 
     JSObject* closedPromise() const { return &getFixedSlot(Slot_ClosedPromise).toObject(); }
     void setClosedPromise(JSObject* wrappedPromise) {
         setFixedSlot(Slot_ClosedPromise, ObjectValue(*wrappedPromise));
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -1630,8 +1630,14 @@ js::LogCtor(void* self, const char* type
 
 JS_FRIEND_API void
 js::LogDtor(void* self, const char* type, uint32_t sz)
 {
     if (LogCtorDtor fun = sLogDtor) {
         fun(self, type, sz);
     }
 }
+
+JS_FRIEND_API uint64_t
+js::GetGCHeapUsageForObjectZone(JSObject* obj)
+{
+    return obj->zone()->usage.gcBytes();
+}
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -2977,11 +2977,18 @@ extern JS_FRIEND_API void
 LogDtor(void* self, const char* type, uint32_t sz);
 
 #define JS_COUNT_CTOR(Class)                            \
     LogCtor((void*) this, #Class, sizeof(Class))
 
 #define JS_COUNT_DTOR(Class)                            \
     LogDtor((void*) this, #Class, sizeof(Class))
 
+/**
+ * This function only reports GC heap memory,
+ * and not malloc allocated memory associated with GC things.
+ */
+extern JS_FRIEND_API uint64_t
+GetGCHeapUsageForObjectZone(JSObject* obj);
+
 } /* namespace js */
 
 #endif /* jsfriendapi_h */
--- a/js/src/vm/Iteration.cpp
+++ b/js/src/vm/Iteration.cpp
@@ -1031,44 +1031,69 @@ NativeObject*
 Realm::getOrCreateIterResultTemplateObject(JSContext* cx)
 {
     MOZ_ASSERT(cx->realm() == this);
 
     if (iterResultTemplate_) {
         return iterResultTemplate_;
     }
 
+    NativeObject* templateObj = createIterResultTemplateObject(cx, WithObjectPrototype::Yes);
+    iterResultTemplate_.set(templateObj);
+    return iterResultTemplate_;
+}
+
+NativeObject*
+Realm::getOrCreateIterResultWithoutPrototypeTemplateObject(JSContext* cx)
+{
+    MOZ_ASSERT(cx->realm() == this);
+
+    if (iterResultWithoutPrototypeTemplate_) {
+        return iterResultWithoutPrototypeTemplate_;
+    }
+
+    NativeObject* templateObj = createIterResultTemplateObject(cx, WithObjectPrototype::No);
+    iterResultWithoutPrototypeTemplate_.set(templateObj);
+    return iterResultWithoutPrototypeTemplate_;
+}
+
+NativeObject*
+Realm::createIterResultTemplateObject(JSContext* cx, WithObjectPrototype withProto)
+{
     // Create template plain object
-    RootedNativeObject templateObject(cx, NewBuiltinClassInstance<PlainObject>(cx, TenuredObject));
+    RootedNativeObject templateObject(cx,
+        withProto == WithObjectPrototype::Yes
+        ? NewBuiltinClassInstance<PlainObject>(cx, TenuredObject)
+        : NewObjectWithNullTaggedProto<PlainObject>(cx));
     if (!templateObject) {
-        return iterResultTemplate_; // = nullptr
+        return nullptr;
     }
 
     // Create a new group for the template.
     Rooted<TaggedProto> proto(cx, templateObject->taggedProto());
     RootedObjectGroup group(cx, ObjectGroupRealm::makeGroup(cx, templateObject->realm(),
                                                             templateObject->getClass(),
                                                             proto));
     if (!group) {
-        return iterResultTemplate_; // = nullptr
+        return nullptr;
     }
     templateObject->setGroup(group);
 
     // Set dummy `value` property
     if (!NativeDefineDataProperty(cx, templateObject, cx->names().value, UndefinedHandleValue,
                                   JSPROP_ENUMERATE))
     {
-        return iterResultTemplate_; // = nullptr
+        return nullptr;
     }
 
     // Set dummy `done` property
     if (!NativeDefineDataProperty(cx, templateObject, cx->names().done, TrueHandleValue,
                                   JSPROP_ENUMERATE))
     {
-        return iterResultTemplate_; // = nullptr
+        return nullptr;
     }
 
     AutoSweepObjectGroup sweep(group);
     if (!group->unknownProperties(sweep)) {
         // Update `value` property typeset, since it can be any value.
         HeapTypeSet* types = group->maybeGetProperty(sweep, NameToId(cx->names().value));
         MOZ_ASSERT(types);
         {
@@ -1079,19 +1104,17 @@ Realm::getOrCreateIterResultTemplateObje
 
     // Make sure that the properties are in the right slots.
     DebugOnly<Shape*> shape = templateObject->lastProperty();
     MOZ_ASSERT(shape->previous()->slot() == Realm::IterResultObjectValueSlot &&
                shape->previous()->propidRef() == NameToId(cx->names().value));
     MOZ_ASSERT(shape->slot() == Realm::IterResultObjectDoneSlot &&
                shape->propidRef() == NameToId(cx->names().done));
 
-    iterResultTemplate_.set(templateObject);
-
-    return iterResultTemplate_;
+    return templateObject;
 }
 
 /*** Iterator objects ****************************************************************************/
 
 size_t
 PropertyIteratorObject::sizeOfMisc(mozilla::MallocSizeOf mallocSizeOf) const
 {
     return mallocSizeOf(getPrivate());
--- a/js/src/vm/Realm.cpp
+++ b/js/src/vm/Realm.cpp
@@ -525,16 +525,22 @@ Realm::sweepTemplateObjects()
 
     if (unmappedArgumentsTemplate_ && IsAboutToBeFinalized(&unmappedArgumentsTemplate_)) {
         unmappedArgumentsTemplate_.set(nullptr);
     }
 
     if (iterResultTemplate_ && IsAboutToBeFinalized(&iterResultTemplate_)) {
         iterResultTemplate_.set(nullptr);
     }
+
+    if (iterResultWithoutPrototypeTemplate_ &&
+        IsAboutToBeFinalized(&iterResultWithoutPrototypeTemplate_))
+    {
+        iterResultWithoutPrototypeTemplate_.set(nullptr);
+    }
 }
 
 void
 Realm::fixupAfterMovingGC()
 {
     purge();
     fixupGlobal();
     objectGroups_.fixupTablesAfterMovingGC();
--- a/js/src/vm/Realm.h
+++ b/js/src/vm/Realm.h
@@ -360,16 +360,17 @@ class JS::Realm : public JS::shadow::Rea
     // This pointer is controlled by the embedder. If it is non-null, and if
     // cx->enableAccessValidation is true, then we assert that *validAccessPtr
     // is true before running any code in this realm.
     bool* validAccessPtr_ = nullptr;
 
     js::ReadBarriered<js::ArgumentsObject*> mappedArgumentsTemplate_ { nullptr };
     js::ReadBarriered<js::ArgumentsObject*> unmappedArgumentsTemplate_ { nullptr };
     js::ReadBarriered<js::NativeObject*> iterResultTemplate_ { nullptr };
+    js::ReadBarriered<js::NativeObject*> iterResultWithoutPrototypeTemplate_ { nullptr };
 
     // There are two ways to enter a realm:
     //
     // (1) AutoRealm (and JSAutoRealm, JS::EnterRealm)
     // (2) When calling a cross-realm (but same-compartment) function in JIT
     //     code.
     //
     // This field only accounts for (1), to keep the JIT code as simple as
@@ -688,17 +689,23 @@ class JS::Realm : public JS::shadow::Rea
     // Used to approximate non-content code when reporting telemetry.
     bool isProbablySystemCode() const {
         return isSystem_;
     }
 
     static const size_t IterResultObjectValueSlot = 0;
     static const size_t IterResultObjectDoneSlot = 1;
     js::NativeObject* getOrCreateIterResultTemplateObject(JSContext* cx);
+    js::NativeObject* getOrCreateIterResultWithoutPrototypeTemplateObject(JSContext* cx);
 
+  private:
+    enum class WithObjectPrototype { No, Yes };
+    js::NativeObject* createIterResultTemplateObject(JSContext* cx, WithObjectPrototype withProto);
+
+  public:
     js::ArgumentsObject* getOrCreateArgumentsTemplateObject(JSContext* cx, bool mapped);
     js::ArgumentsObject* maybeArgumentsTemplateObject(bool mapped) const;
 
     //
     // The Debugger observes execution on a frame-by-frame basis. The
     // invariants of Realm's debug mode bits, JSScript::isDebuggee,
     // InterpreterFrame::isDebuggee, and BaselineFrame::isDebuggee are
     // enumerated below.
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -4707,17 +4707,17 @@ PresShell::RenderDocument(const nsRect& 
   nsAutoScriptBlocker blockScripts;
 
   // Set up the rectangle as the path in aThebesContext
   gfxRect r(0, 0,
             nsPresContext::AppUnitsToFloatCSSPixels(aRect.width),
             nsPresContext::AppUnitsToFloatCSSPixels(aRect.height));
   aThebesContext->NewPath();
 #ifdef MOZ_GFX_OPTIMIZE_MOBILE
-  aThebesContext->Rectangle(r, true);
+  aThebesContext->SnappedRectangle(r);
 #else
   aThebesContext->Rectangle(r);
 #endif
 
   nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
   if (!rootFrame) {
     // Nothing to paint, just fill the rect
     aThebesContext->SetColor(Color::FromABGR(aBackgroundColor));
--- a/layout/generic/nsFrame.h
+++ b/layout/generic/nsFrame.h
@@ -620,19 +620,22 @@ public:
   {
     // clip overflow:-moz-hidden-unscrollable, except for nsListControlFrame,
     // which is an nsHTMLScrollFrame.
     if (MOZ_UNLIKELY(aDisp->mOverflowX == NS_STYLE_OVERFLOW_CLIP &&
                      !aFrame->IsListControlFrame())) {
       return true;
     }
 
-    // contain: paint, which we should interpret as -moz-hidden-unscrollable
-    // by default.
-    if (aDisp->IsContainPaint()) {
+    // contain: paint, which we interpret as -moz-hidden-unscrollable
+    // Exception: for scrollframes, we don't need contain:paint to add any
+    // clipping, because the scrollable frame will already clip overflowing
+    // content, and because contain:paint should prevent all means of escaping
+    // that clipping (e.g. because it forms a fixed-pos containing block).
+    if (aDisp->IsContainPaint() && !aFrame->IsScrollFrame()) {
       return true;
     }
 
     // and overflow:hidden that we should interpret as -moz-hidden-unscrollable
     if (aDisp->mOverflowX == NS_STYLE_OVERFLOW_HIDDEN &&
         aDisp->mOverflowY == NS_STYLE_OVERFLOW_HIDDEN) {
       // REVIEW: these are the frame types that set up clipping.
       mozilla::LayoutFrameType type = aFrame->Type();
--- a/layout/painting/DisplayItemClip.cpp
+++ b/layout/painting/DisplayItemClip.cpp
@@ -96,17 +96,17 @@ DisplayItemClip::ApplyTo(gfxContext* aCo
   ApplyRoundedRectClipsTo(aContext, A2D, 0, mRoundedClipRects.Length());
 }
 
 void
 DisplayItemClip::ApplyRectTo(gfxContext* aContext, int32_t A2D) const
 {
   aContext->NewPath();
   gfxRect clip = nsLayoutUtils::RectToGfxRect(mClipRect, A2D);
-  aContext->Rectangle(clip, true);
+  aContext->SnappedRectangle(clip);
   aContext->Clip();
 }
 
 void
 DisplayItemClip::ApplyRoundedRectClipsTo(gfxContext* aContext,
                                          int32_t A2D,
                                          uint32_t aBegin,
                                          uint32_t aEnd) const
--- a/layout/painting/nsCSSRendering.cpp
+++ b/layout/painting/nsCSSRendering.cpp
@@ -2538,17 +2538,17 @@ SetupImageLayerClip(nsCSSRendering::Imag
   if (aClipState.mHasAdditionalBGClipArea) {
     gfxRect bgAreaGfx = nsLayoutUtils::RectToGfxRect(
       aClipState.mAdditionalBGClipArea, aAppUnitsPerPixel);
     bgAreaGfx.Round();
     gfxUtils::ConditionRect(bgAreaGfx);
 
     aAutoSR->EnsureSaved(aCtx);
     aCtx->NewPath();
-    aCtx->Rectangle(bgAreaGfx, true);
+    aCtx->SnappedRectangle(bgAreaGfx);
     aCtx->Clip();
   }
 
   if (aClipState.mHasRoundedCorners) {
     Rect bgAreaGfx = NSRectToRect(aClipState.mBGClipArea, aAppUnitsPerPixel);
     bgAreaGfx.Round();
 
     if (bgAreaGfx.IsEmpty()) {
@@ -2580,17 +2580,17 @@ DrawBackgroundColor(nsCSSRendering::Imag
   }
 
   DrawTarget* drawTarget = aCtx->GetDrawTarget();
 
   // We don't support custom clips and rounded corners, arguably a bug, but
   // table painting seems to depend on it.
   if (!aClipState.mHasRoundedCorners || aClipState.mCustomClip) {
     aCtx->NewPath();
-    aCtx->Rectangle(aClipState.mDirtyRectInDevPx, true);
+    aCtx->SnappedRectangle(aClipState.mDirtyRectInDevPx);
     aCtx->Fill();
     return;
   }
 
   Rect bgAreaGfx = NSRectToRect(aClipState.mBGClipArea, aAppUnitsPerPixel);
   bgAreaGfx.Round();
 
   if (bgAreaGfx.IsEmpty()) {
@@ -2601,26 +2601,26 @@ DrawBackgroundColor(nsCSSRendering::Imag
     aClipState.mDirtyRectInDevPx.SizeTo(gfxSize(0.0, 0.0));
     return;
   }
 
   aCtx->Save();
   gfxRect dirty = ThebesRect(bgAreaGfx).Intersect(aClipState.mDirtyRectInDevPx);
 
   aCtx->NewPath();
-  aCtx->Rectangle(dirty, true);
+  aCtx->SnappedRectangle(dirty);
   aCtx->Clip();
 
   if (aClipState.mHasAdditionalBGClipArea) {
     gfxRect bgAdditionalAreaGfx = nsLayoutUtils::RectToGfxRect(
       aClipState.mAdditionalBGClipArea, aAppUnitsPerPixel);
     bgAdditionalAreaGfx.Round();
     gfxUtils::ConditionRect(bgAdditionalAreaGfx);
     aCtx->NewPath();
-    aCtx->Rectangle(bgAdditionalAreaGfx, true);
+    aCtx->SnappedRectangle(bgAdditionalAreaGfx);
     aCtx->Clip();
   }
 
   RefPtr<Path> roundedRect =
     MakePathForRoundedRect(*drawTarget, bgAreaGfx, aClipState.mClippedRadii);
   aCtx->SetPath(roundedRect);
   aCtx->Fill();
   aCtx->Restore();
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -5115,25 +5115,25 @@ nsDisplayBackgroundColor::Paint(nsDispla
     mBackgroundStyle->StyleBackground()->mImage.mLayers[0].mClip;
   if (clip == StyleGeometryBox::Text) {
     if (!GenerateAndPushTextMask(mFrame, aCtx, mBackgroundRect, aBuilder)) {
       return;
     }
 
     ctx->SetColor(mColor);
     ctx->NewPath();
-    ctx->Rectangle(bounds, true);
+    ctx->SnappedRectangle(bounds);
     ctx->Fill();
     ctx->PopGroupAndBlend();
     return;
   }
 
   ctx->SetColor(mColor);
   ctx->NewPath();
-  ctx->Rectangle(bounds, true);
+  ctx->SnappedRectangle(bounds);
   ctx->Fill();
 #endif
 }
 
 nsRegion
 nsDisplayBackgroundColor::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
                                           bool* aSnap) const
 {
new file mode 100644
--- /dev/null
+++ b/layout/reftests/async-scrolling/contain-paint-scrollable-frame-1-ref.html
@@ -0,0 +1,8 @@
+<!DOCTYPE HTML>
+<html>
+<body>
+  <div style="width:400px; height:200px;
+              border:2px solid black; background: lime;">
+  </div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/async-scrolling/contain-paint-scrollable-frame-1.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<html reftest-async-scroll>
+<body>
+  <div style="width:400px; height:200px; overflow:hidden;
+              border:2px solid black; background: red;
+              contain:paint;"
+       reftest-displayport-x="0" reftest-displayport-y="0"
+       reftest-displayport-w="800" reftest-displayport-h="2000"
+       reftest-async-scroll-x="0" reftest-async-scroll-y="100">
+    <!-- This element is what we're hoping will fill the scrollport
+         when the reftest snapshot is taken: -->
+    <div style="background: lime; margin-top: 100px; height: 600px"></div>
+
+    <!-- This element is just to be sure the scrollframe's "contain:paint"
+         styling is actually having an effect. "contain:paint" should make the
+         scrollframe become a containing block for this fixed-pos element, and
+         then this element will position itself off the bottom of the
+         scrollframe and will be entirely clipped. If we fail to honor
+         "contain:paint" for some reason, then this element will instead use
+         the *viewport* as its containing block, and it'll show up in the
+         reftest snapshot and cause the reftest to fail. -->
+    <div style="position: fixed; top: 320px; width: 50px; height: 50px;
+                background: purple;"></div>
+  </div>
+</body>
+</html>
--- a/layout/reftests/async-scrolling/reftest.list
+++ b/layout/reftests/async-scrolling/reftest.list
@@ -8,16 +8,17 @@ skip-if(!asyncPan) == bg-fixed-child-cli
 fuzzy(0-1,0-246) fuzzy-if(skiaContent,0-2,0-170) fuzzy-if(browserIsRemote&&d2d,0-59,0-187) skip-if(!asyncPan) == bg-fixed-child-mask.html bg-fixed-child-mask-ref.html
 skip-if(!asyncPan) == bg-fixed-in-opacity.html bg-fixed-in-opacity-ref.html
 # Passing the test below without WebRender would require implementing CSS filters in the Gecko compositor.
 fails-if(!webrender) skip-if(!asyncPan) fuzzy-if(webrender&&gtkWidget,0-1,0-87) fuzzy-if(webrender&&!gtkWidget,0-1,0-8) == bg-fixed-in-css-filter.html bg-fixed-in-css-filter-ref.html # bug 1454794 for webrender fuzziness
 skip-if(!asyncPan) == bg-fixed-child-no-culling-1.html bg-fixed-child-no-culling-1-ref.html
 skip-if(!asyncPan) == bg-fixed-child-no-culling-2.html bg-fixed-child-no-culling-2-ref.html
 skip-if(!asyncPan) == bg-fixed-child-no-culling-3.html bg-fixed-child-no-culling-3-ref.html
 fuzzy-if(Android,0-2,0-4000) fuzzy-if(browserIsRemote&&cocoaWidget,0-2,0-179524) fuzzy-if(browserIsRemote&&winWidget,0-1,0-74590) fuzzy-if(gtkWidget&&layersGPUAccelerated,0-1,0-3528) skip-if(!asyncPan) == bg-fixed-transformed-image.html bg-fixed-transformed-image-ref.html
+test-pref(layout.css.contain.enabled,true) skip-if(!asyncPan) == contain-paint-scrollable-frame-1.html contain-paint-scrollable-frame-1-ref.html
 skip-if(!asyncPan) == element-1.html element-1-ref.html
 pref(layers.force-active,true) skip-if(!asyncPan) == iframe-1.html iframe-1-ref.html
 skip-if(!asyncPan) == nested-1.html nested-1-ref.html
 skip-if(!asyncPan) == nested-2.html nested-2-ref.html
 skip-if(!asyncPan) == position-fixed-1.html position-fixed-1-ref.html
 skip-if(!asyncPan) == position-fixed-2.html position-fixed-2-ref.html
 fuzzy-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu),0-1,0-3120) skip-if(!asyncPan) == position-fixed-body.html position-fixed-body-ref.html
 skip-if(!asyncPan) == position-fixed-cover-1.html position-fixed-cover-1-ref.html
--- a/layout/svg/nsSVGIntegrationUtils.cpp
+++ b/layout/svg/nsSVGIntegrationUtils.cpp
@@ -1048,17 +1048,17 @@ void PaintMaskAndClipPathInternal(const 
 
   if (gfxPrefs::DrawMaskLayer()) {
     gfxContextAutoSaveRestore saver(&context);
 
     context.NewPath();
     gfxRect drawingRect =
       nsLayoutUtils::RectToGfxRect(aParams.borderArea,
                                    frame->PresContext()->AppUnitsPerDevPixel());
-    context.Rectangle(drawingRect, true);
+    context.SnappedRectangle(drawingRect);
     Color overlayColor(0.0f, 0.0f, 0.0f, 0.8f);
     if (maskUsage.shouldGenerateMaskLayer) {
       overlayColor.r = 1.0f; // red represents css positioned mask.
     }
     if (maskUsage.shouldApplyClipPath ||
         maskUsage.shouldGenerateClipMaskLayer) {
       overlayColor.g = 1.0f; // green represents clip-path:<clip-source>.
     }
--- a/mfbt/Result.h
+++ b/mfbt/Result.h
@@ -216,16 +216,21 @@ struct UnusedZero<T&>
   static const bool value = true;
 };
 
 // A bit of help figuring out which of the above specializations to use.
 //
 // We begin by safely assuming types don't have a spare bit.
 template <typename T> struct HasFreeLSB { static const bool value = false; };
 
+// As an incomplete type, void* does not have a spare bit.
+template <> struct HasFreeLSB<void*> {
+  static const bool value = false;
+};
+
 // The lowest bit of a properly-aligned pointer is always zero if the pointee
 // type is greater than byte-aligned. That bit is free to use if it's masked
 // out of such pointers before they're dereferenced.
 template <typename T> struct HasFreeLSB<T*> {
   static const bool value = (alignof(T) & 1) == 0;
 };
 
 // We store references as pointers, so they have a free bit if a pointer would
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/SessionAccessibility.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/SessionAccessibility.java
@@ -106,26 +106,30 @@ public class SessionAccessibility {
 
         Log.e(LOGTAG, "Index " + index + " our of CLASSNAME bounds.");
         return "android.view.View"; // Fallback class is View
     }
 
     /* package */ final class NodeProvider extends AccessibilityNodeProvider {
         @Override
         public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualDescendantId) {
-            AccessibilityNodeInfo node;
+            AccessibilityNodeInfo node = null;
             if (mAttached) {
                 node = mSession.getSettings().getBoolean(GeckoSessionSettings.FULL_ACCESSIBILITY_TREE) ?
                         getNodeFromGecko(virtualDescendantId) : getNodeFromCache(virtualDescendantId);
                 if (node != null) {
                     node.setAccessibilityFocused(mAccessibilityFocusedNode == virtualDescendantId);
                     node.setFocused(mFocusedNode == virtualDescendantId);
                 }
-            } else {
-                node = AccessibilityNodeInfo.obtain(mView, virtualDescendantId);
+            }
+
+            if (node == null) {
+                Log.w(LOGTAG, "Failed to retrieve accessible node virtualDescendantId=" +
+                        virtualDescendantId + " mAttached=" + mAttached);
+                node = AccessibilityNodeInfo.obtain(mView, View.NO_ID);
                 if (Build.VERSION.SDK_INT < 17 || mView.getDisplay() != null) {
                     // When running junit tests we don't have a display
                     mView.onInitializeAccessibilityNodeInfo(node);
                 }
                 node.setClassName("android.webkit.WebView");
             }
 
             return node;
--- a/security/manager/ssl/nsNSSIOLayer.cpp
+++ b/security/manager/ssl/nsNSSIOLayer.cpp
@@ -2436,31 +2436,33 @@ done:
   if (mRV == SECFailure) {
     mErrorCodeToReport = error;
   }
 }
 
 static PRFileDesc*
 nsSSLIOLayerImportFD(PRFileDesc* fd,
                      nsNSSSocketInfo* infoObject,
-                     const char* host)
+                     const char* host,
+                     bool haveHTTPSProxy)
 {
   PRFileDesc* sslSock = SSL_ImportFD(nullptr, fd);
   if (!sslSock) {
     MOZ_ASSERT_UNREACHABLE("NSS: Error importing socket");
     return nullptr;
   }
   SSL_SetPKCS11PinArg(sslSock, (nsIInterfaceRequestor*) infoObject);
   SSL_HandshakeCallback(sslSock, HandshakeCallback, infoObject);
   SSL_SetCanFalseStartCallback(sslSock, CanFalseStartCallback, infoObject);
 
   // Disable this hook if we connect anonymously. See bug 466080.
   uint32_t flags = 0;
   infoObject->GetProviderFlags(&flags);
-  if (flags & nsISocketProvider::ANONYMOUS_CONNECT) {
+  // Provide the client cert to HTTPS proxy no matter if it is anonymous.
+  if (flags & nsISocketProvider::ANONYMOUS_CONNECT && !haveHTTPSProxy) {
       SSL_GetClientAuthDataHook(sslSock, nullptr, infoObject);
   } else {
       SSL_GetClientAuthDataHook(sslSock,
                             (SSLGetClientAuthData) nsNSS_SSLGetClientAuthData,
                             infoObject);
   }
   if (flags & nsISocketProvider::MITM_OK) {
     MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
@@ -2696,36 +2698,41 @@ nsSSLIOLayerAddToSocket(int32_t family,
   infoObject->SetHostName(host);
   infoObject->SetPort(port);
   infoObject->SetOriginAttributes(originAttributes);
   if (allocatedState) {
     infoObject->SetSharedOwningReference(allocatedState);
   }
 
   bool haveProxy = false;
+  bool haveHTTPSProxy = false;
   if (proxy) {
-    nsCString proxyHost;
+    nsAutoCString proxyHost;
     proxy->GetHost(proxyHost);
     haveProxy = !proxyHost.IsEmpty();
+    nsAutoCString type;
+    haveHTTPSProxy = haveProxy &&
+                     NS_SUCCEEDED(proxy->GetType(type)) &&
+                     type.EqualsLiteral("https");
   }
 
   // A plaintext observer shim is inserted so we can observe some protocol
   // details without modifying nss
   plaintextLayer = PR_CreateIOLayerStub(nsSSLIOLayerHelpers::nsSSLPlaintextLayerIdentity,
                                         &nsSSLIOLayerHelpers::nsSSLPlaintextLayerMethods);
   if (plaintextLayer) {
     plaintextLayer->secret = (PRFilePrivate*) infoObject;
     stat = PR_PushIOLayer(fd, PR_TOP_IO_LAYER, plaintextLayer);
     if (stat == PR_FAILURE) {
       plaintextLayer->dtor(plaintextLayer);
       plaintextLayer = nullptr;
     }
   }
 
-  PRFileDesc* sslSock = nsSSLIOLayerImportFD(fd, infoObject, host);
+  PRFileDesc* sslSock = nsSSLIOLayerImportFD(fd, infoObject, host, haveHTTPSProxy);
   if (!sslSock) {
     MOZ_ASSERT_UNREACHABLE("NSS: Error importing socket");
     goto loser;
   }
 
   infoObject->SetFileDescPtr(sslSock);
 
   rv = nsSSLIOLayerSetOptions(sslSock, forSTARTTLS, haveProxy, host, port,
--- a/taskcluster/ci/packages/kind.yml
+++ b/taskcluster/ci/packages/kind.yml
@@ -145,35 +145,22 @@ jobs:
       dsc:
         url: http://snapshot.debian.org/archive/debian/20160317T100542Z/pool/main/g/git/git_2.8.0%7Erc3-1.dsc
         sha256: 6e81a318fb4eb5cca0333b7b6ff0c70dd0097e9fe711b159d5eac4b9f47c6c27
 
   deb7-valgrind:
     description: "Valgrind for Debian Wheezy"
     treeherder:
       symbol: Deb7(valgrind)
-    worker:
-      env:
-        VERSION: 3.14.0.git20180806
-        COMMIT: 2eb2df759f51b15702934dee108f4c20c3db5fef
     run:
       using: debian-package
       dsc:
-        url: http://snapshot.debian.org/archive/debian/20170725T040438Z/pool/main/v/valgrind/valgrind_3.13.0-1.dsc
-        sha256: ab84e017d1660efd30e9e0593a4c8b976aeda013cefb8c416dd284cc7222c11c
-      packages:
-        - deb7-git
+        url: http://snapshot.debian.org/archive/debian/20181115T045552Z/pool/main/v/valgrind/valgrind_3.14.0-1.dsc
+        sha256: 6709e2fe4e8251ee32f3cfbf2c6ee106a5cfa3e8dc672cf1dd5f2b26e72b64ee
       patch: valgrind-wheezy.diff
-      pre-build-command: >-
-        git clone -n git://sourceware.org/git/valgrind.git ../valgrind-git &&
-        git -C ../valgrind-git archive --format=tar --prefix=valgrind-$VERSION/ $COMMIT | bzip2 > ../valgrind_$VERSION.orig.tar.bz2 &&
-        tar -C .. -jxf ../valgrind_$VERSION.orig.tar.bz2 &&
-        cp -r debian ../valgrind-$VERSION &&
-        cd ../valgrind-$VERSION &&
-        debchange -v 1:$VERSION-1.deb7moz1 --distribution wheezy "Mozilla backport of git master as of 2018-08-06." < /dev/null
 
   deb7-dh-python:
     description: "dh-python for Debian wheezy"
     treeherder:
       symbol: Deb7(dh-python)
     run:
       using: debian-package
       dsc:
--- a/taskcluster/ci/test/raptor.yml
+++ b/taskcluster/ci/test/raptor.yml
@@ -80,16 +80,120 @@ raptor-tp6-2-chrome:
             linux64.*: 3
             default: 2
     max-run-time: 1800
     mozharness:
         extra-options:
             - --test=raptor-tp6-2
             - --app=chrome
 
+raptor-tp6-3-firefox:
+    description: "Raptor tp6-3 on Firefox"
+    try-name: raptor-tp6-3-firefox
+    treeherder-symbol: Rap(tp6-3)
+    run-on-projects: ['try', 'mozilla-central']
+    tier: 2
+    max-run-time: 1800
+    mozharness:
+        extra-options:
+            - --test=raptor-tp6-3
+
+raptor-tp6-3-chrome:
+    description: "Raptor tp6-3 on Chrome"
+    try-name: raptor-tp6-3-chrome
+    treeherder-symbol: Rap-C(tp6-3)
+    run-on-projects: ['try', 'mozilla-central']
+    tier:
+        by-test-platform:
+            linux64.*: 3
+            default: 2
+    max-run-time: 2400
+    mozharness:
+        extra-options:
+            - --test=raptor-tp6-3
+            - --app=chrome
+
+raptor-tp6-4-firefox:
+    description: "Raptor tp6-4 on Firefox"
+    try-name: raptor-tp6-4-firefox
+    treeherder-symbol: Rap(tp6-4)
+    run-on-projects: ['try', 'mozilla-central']
+    tier: 2
+    max-run-time: 1200
+    mozharness:
+        extra-options:
+            - --test=raptor-tp6-4
+
+raptor-tp6-4-chrome:
+    description: "Raptor tp6-4 on Chrome"
+    try-name: raptor-tp6-4-chrome
+    treeherder-symbol: Rap-C(tp6-4)
+    run-on-projects: ['try', 'mozilla-central']
+    tier:
+        by-test-platform:
+            linux64.*: 3
+            default: 2
+    max-run-time: 1800
+    mozharness:
+        extra-options:
+            - --test=raptor-tp6-4
+            - --app=chrome
+
+raptor-tp6-5-firefox:
+    description: "Raptor tp6-5 on Firefox"
+    try-name: raptor-tp6-5-firefox
+    treeherder-symbol: Rap(tp6-5)
+    run-on-projects: ['try', 'mozilla-central']
+    tier: 2
+    max-run-time: 1200
+    mozharness:
+        extra-options:
+            - --test=raptor-tp6-5
+
+raptor-tp6-5-chrome:
+    description: "Raptor tp6-5 on Chrome"
+    try-name: raptor-tp6-5-chrome
+    treeherder-symbol: Rap-C(tp6-5)
+    run-on-projects: ['try', 'mozilla-central']
+    tier:
+        by-test-platform:
+            linux64.*: 3
+            default: 2
+    max-run-time: 1800
+    mozharness:
+        extra-options:
+            - --test=raptor-tp6-5
+            - --app=chrome
+
+raptor-tp6-6-firefox:
+    description: "Raptor tp6-6 on Firefox"
+    try-name: raptor-tp6-6-firefox
+    treeherder-symbol: Rap(tp6-6)
+    run-on-projects: ['try', 'mozilla-central']
+    tier: 2
+    max-run-time: 1200
+    mozharness:
+        extra-options:
+            - --test=raptor-tp6-6
+
+raptor-tp6-6-chrome:
+    description: "Raptor tp6-6 on Chrome"
+    try-name: raptor-tp6-6-chrome
+    treeherder-symbol: Rap-C(tp6-6)
+    run-on-projects: ['try', 'mozilla-central']
+    tier:
+        by-test-platform:
+            linux64.*: 3
+            default: 2
+    max-run-time: 1800
+    mozharness:
+        extra-options:
+            - --test=raptor-tp6-6
+            - --app=chrome
+
 raptor-speedometer-firefox:
     description: "Raptor Speedometer on Firefox"
     try-name: raptor-speedometer-firefox
     treeherder-symbol: Rap(sp)
     run-on-projects:
         by-test-platform:
             windows10-64-ux: ['try', 'mozilla-central']
             default: built-projects
--- a/taskcluster/ci/test/test-sets.yml
+++ b/taskcluster/ci/test/test-sets.yml
@@ -74,27 +74,35 @@ talos:
     - talos-tp6-stylo-threads
     - talos-tps
     # - talos-h1 Bug 1487031 - Disabled for not finding actionable regressions
     # - talos-h2 Bug 1487031 - Disabled for not finding actionable regressions
 
 raptor-firefox:
     - raptor-tp6-1-firefox
     - raptor-tp6-2-firefox
+    - raptor-tp6-3-firefox
+    - raptor-tp6-4-firefox
+    - raptor-tp6-5-firefox
+    - raptor-tp6-6-firefox
     - raptor-speedometer-firefox
     - raptor-stylebench-firefox
     - raptor-motionmark-htmlsuite-firefox
     - raptor-motionmark-animometer-firefox
     - raptor-webaudio-firefox
     - raptor-sunspider-firefox
     - raptor-wasm-godot-firefox
 
 raptor-chrome:
     - raptor-tp6-1-chrome
     - raptor-tp6-2-chrome
+    - raptor-tp6-3-chrome
+    - raptor-tp6-4-chrome
+    - raptor-tp6-5-chrome
+    - raptor-tp6-6-chrome
     - raptor-speedometer-chrome
     - raptor-stylebench-chrome
     - raptor-motionmark-htmlsuite-chrome
     - raptor-motionmark-animometer-chrome
     - raptor-webaudio-chrome
     - raptor-sunspider-chrome
     - raptor-wasm-godot-chrome
 
--- a/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm
+++ b/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm
@@ -1151,16 +1151,73 @@ var BrowserTestUtils = {
 
       mm.sendAsyncMessage("Test:SynthesizeMouse",
                           {target, targetFn, x: offsetX, y: offsetY, event},
                           {object: cpowObject});
     });
   },
 
   /**
+   *  Versions of EventUtils.jsm synthesizeTouch functions that synthesize a
+   *  touch event in a child process and return promises that resolve when the
+   *  event has fired and completed. Instead of a window, a browser is required
+   *  to be passed to this function.
+   *
+   * @param target
+   *        One of the following:
+   *        - a selector string that identifies the element to target. The syntax is as
+   *          for querySelector.
+   *        - An array of selector strings. Each selector after the first
+   *          selects for an element in the iframe specified by the previous
+   *          selector.
+   *        - a CPOW element (for easier test-conversion).
+   *        - a function to be run in the content process that returns the element to
+   *        target
+   *        - null, in which case the offset is from the content document's edge.
+   * @param {integer} offsetX
+   *        x offset from target's left bounding edge
+   * @param {integer} offsetY
+   *        y offset from target's top bounding edge
+   * @param {Object} event object
+   *        Additional arguments, similar to the EventUtils.jsm version
+   * @param {Browser} browser
+   *        Browser element, must not be null
+   *
+   * @returns {Promise}
+   * @resolves True if the touch event was cancelled.
+   */
+  synthesizeTouch(target, offsetX, offsetY, event, browser) {
+    return new Promise((resolve, reject) => {
+      let mm = browser.messageManager;
+      mm.addMessageListener("Test:SynthesizeTouchDone", function touchMsg(message) {
+        mm.removeMessageListener("Test:SynthesizeTouchDone", touchMsg);
+        if (message.data.hasOwnProperty("defaultPrevented")) {
+          resolve(message.data.defaultPrevented);
+        } else {
+          reject(new Error(message.data.error));
+        }
+      });
+
+      let cpowObject = null;
+      let targetFn = null;
+      if (typeof target == "function") {
+        targetFn = target.toString();
+        target = null;
+      } else if (typeof target != "string" && !Array.isArray(target)) {
+        cpowObject = target;
+        target = null;
+      }
+
+      mm.sendAsyncMessage("Test:SynthesizeTouch",
+                          {target, targetFn, x: offsetX, y: offsetY, event},
+                          {object: cpowObject});
+    });
+  },
+
+  /**
    * Wait for a message to be fired from a particular message manager
    *
    * @param {nsIMessageManager} messageManager
    *                            The message manager that should be used.
    * @param {String}            message
    *                            The message we're waiting for.
    * @param {Function}          checkFn (optional)
    *                            Optional function to invoke to check the message.
--- a/testing/mochitest/tests/Harness_sanity/mochitest.ini
+++ b/testing/mochitest/tests/Harness_sanity/mochitest.ini
@@ -5,20 +5,19 @@ skip-if = true #depends on fix for bug 1
 [test_importInMainProcess.html]
 skip-if = verify
 support-files = importtesting_chromescript.js
 [test_sanity.html]
 [test_sanityException.html]
 [test_sanityException2.html]
 [test_sanityParams.html]
 [test_sanityRegisteredServiceWorker.html]
-skip-if = serviceworker_e10s
 support-files = empty.js
 [test_sanityRegisteredServiceWorker2.html]
-skip-if = verify || serviceworker_e10s
+skip-if = verify
 support-files = empty.js
 [test_sanityWindowSnapshot.html]
 [test_SpecialPowersExtension.html]
 [test_SpecialPowersExtension2.html]
 support-files = file_SpecialPowersFrame1.html
 [test_SpecialPowersPushPermissions.html]
 support-files =
     specialPowers_framescript.js
--- a/testing/mochitest/tests/SimpleTest/AsyncUtilsContent.js
+++ b/testing/mochitest/tests/SimpleTest/AsyncUtilsContent.js
@@ -86,16 +86,60 @@ addMessageListener("Test:SynthesizeMouse
   if (data.event && data.event.wheel) {
     EventUtils.synthesizeWheelAtPoint(left, top, data.event, content);
   } else {
     result = EventUtils.synthesizeMouseAtPoint(left, top, data.event, content);
   }
   sendAsyncMessage("Test:SynthesizeMouseDone", { defaultPrevented: result });
 });
 
+addMessageListener("Test:SynthesizeTouch", (message) => {
+  let data = message.data;
+  let target = data.target;
+  if (typeof target == "string") {
+    target = content.document.querySelector(target);
+  }
+  else if (Array.isArray(target)) {
+    let elem = {contentDocument: content.document};
+    for (let sel of target) {
+      elem = elem.contentDocument.querySelector(sel);
+    }
+    target = elem;
+  }
+  else if (typeof data.targetFn == "string") {
+    let runnablestr = `
+      (() => {
+        return (${data.targetFn});
+      })();`
+    target = eval(runnablestr)();
+  }
+  else {
+    target = message.objects.object;
+  }
+
+  if (target) {
+    if (target.ownerDocument !== content.document) {
+      // Account for nodes found in iframes.
+      let cur = target;
+      do {
+        cur = cur.ownerGlobal.frameElement;
+      } while (cur && cur.ownerDocument !== content.document);
+
+      // node must be in this document tree.
+      if (!cur) {
+        sendAsyncMessage("Test:SynthesizeTouchDone",
+                         { error: "target must be in the main document tree"});
+        return;
+      }
+    }
+  }
+  let result = EventUtils.synthesizeTouch(target, data.x, data.y, data.event, content)
+  sendAsyncMessage("Test:SynthesizeTouchDone", { defaultPrevented: result });
+});
+
 addMessageListener("Test:SendChar", message => {
   let result = EventUtils.sendChar(message.data.char, content);
   sendAsyncMessage("Test:SendCharDone", { result, seq: message.data.seq });
 });
 
 addMessageListener("Test:SynthesizeKey", message => {
   EventUtils.synthesizeKey(message.data.key, message.data.event || {}, content);
   sendAsyncMessage("Test:SynthesizeKeyDone", { seq: message.data.seq });
--- a/testing/mochitest/tests/SimpleTest/EventUtils.js
+++ b/testing/mochitest/tests/SimpleTest/EventUtils.js
@@ -403,17 +403,17 @@ function synthesizeMouse(aTarget, aOffse
 {
   var rect = aTarget.getBoundingClientRect();
   return synthesizeMouseAtPoint(rect.left + aOffsetX, rect.top + aOffsetY,
        aEvent, aWindow);
 }
 function synthesizeTouch(aTarget, aOffsetX, aOffsetY, aEvent, aWindow)
 {
   var rect = aTarget.getBoundingClientRect();
-  synthesizeTouchAtPoint(rect.left + aOffsetX, rect.top + aOffsetY,
+  return synthesizeTouchAtPoint(rect.left + aOffsetX, rect.top + aOffsetY,
        aEvent, aWindow);
 }
 
 /*
  * Synthesize a mouse event at a particular point in aWindow.
  *
  * aEvent is an object which may contain the properties:
  *   shiftKey, ctrlKey, altKey, metaKey, accessKey, clickCount, button, type
@@ -475,33 +475,35 @@ function synthesizeMouseAtPoint(left, to
   }
 
   return defaultPrevented;
 }
 
 function synthesizeTouchAtPoint(left, top, aEvent, aWindow = window)
 {
   var utils = _getDOMWindowUtils(aWindow);
+  let defaultPrevented = false;
 
   if (utils) {
     var id = aEvent.id || utils.DEFAULT_TOUCH_POINTER_ID;
     var rx = aEvent.rx || 1;
     var ry = aEvent.ry || 1;
     var angle = aEvent.angle || 0;
     var force = aEvent.force || 1;
     var modifiers = _parseModifiers(aEvent, aWindow);
 
     if (("type" in aEvent) && aEvent.type) {
-      utils.sendTouchEvent(aEvent.type, [id], [left], [top], [rx], [ry], [angle], [force], 1, modifiers);
+      defaultPrevented = utils.sendTouchEvent(aEvent.type, [id], [left], [top], [rx], [ry], [angle], [force], 1, modifiers);
     }
     else {
       utils.sendTouchEvent("touchstart", [id], [left], [top], [rx], [ry], [angle], [force], 1, modifiers);
       utils.sendTouchEvent("touchend", [id], [left], [top], [rx], [ry], [angle], [force], 1, modifiers);
     }
   }
+  return defaultPrevented;
 }
 
 // Call synthesizeMouse with coordinates at the center of aTarget.
 function synthesizeMouseAtCenter(aTarget, aEvent, aWindow)
 {
   var rect = aTarget.getBoundingClientRect();
   return synthesizeMouse(aTarget, rect.width / 2, rect.height / 2, aEvent,
                          aWindow);
new file mode 100644
--- /dev/null
+++ b/testing/raptor/raptor/playback/mitmproxy-recordings-raptor-tp6-3.manifest
@@ -0,0 +1,9 @@
+[
+    {
+        "filename": "raptor-tp6-3.zip",
+        "size": 24512088,
+        "digest": "5b8080cf842a50fe2055127daa084fa9f133307965e4d8af246978150b8b3b11f1fb06cdf65ba69e13c875b77db14e7494cdb940376b264c3aefb5b53e22b892",
+        "algorithm": "sha512",
+        "unpack": true
+    }
+]
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/testing/raptor/raptor/playback/mitmproxy-recordings-raptor-tp6-4.manifest
@@ -0,0 +1,9 @@
+[
+    {
+        "filename": "raptor-tp6-4.zip",
+        "size": 1643188,
+        "digest": "0e9f2a23323f93f7a9839ab49d84555cce03bfab2196aa8670e604d6df390b22f0594527cecf7b2efd0449dd87507ec934bdcc74f1ebee68c22161f4104b6513",
+        "algorithm": "sha512",
+        "unpack": true
+    }
+]
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/testing/raptor/raptor/playback/mitmproxy-recordings-raptor-tp6-5.manifest
@@ -0,0 +1,9 @@
+[
+    {
+        "filename": "raptor-tp6-5.zip",
+        "size": 27670268,
+        "digest": "4a52cb6770062231f9f283ab42ac53634d91677146b4d91931f508de22bff262512775d0c93d80730287000907e438d467136768883691c3e20c1e6a8f475a03",
+        "algorithm": "sha512",
+        "unpack": true
+    }
+]
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/testing/raptor/raptor/playback/mitmproxy-recordings-raptor-tp6-6.manifest
@@ -0,0 +1,9 @@
+[
+    {
+        "filename": "raptor-tp6-6.zip",
+        "size": 2240008,
+        "digest": "21110ee297074413a72343bcb7d22ae88efce104de6e7eea5916e2dae004b29746ec6dd6155686cffed816e88d38ad1bbc04fe1253623ed8c0d4256a146d8b77",
+        "algorithm": "sha512",
+        "unpack": true
+    }
+]
\ No newline at end of file
--- a/testing/raptor/raptor/raptor.ini
+++ b/testing/raptor/raptor/raptor.ini
@@ -1,11 +1,15 @@
 # raptor pageload tests
 [include:tests/raptor-tp6-1.ini]
 [include:tests/raptor-tp6-2.ini]
+[include:tests/raptor-tp6-3.ini]
+[include:tests/raptor-tp6-4.ini]
+[include:tests/raptor-tp6-5.ini]
+[include:tests/raptor-tp6-6.ini]
 
 # raptor benchmark tests
 [include:tests/raptor-assorted-dom.ini]
 [include:tests/raptor-motionmark-animometer.ini]
 [include:tests/raptor-motionmark-htmlsuite.ini]
 [include:tests/raptor-speedometer.ini]
 [include:tests/raptor-stylebench.ini]
 [include:tests/raptor-sunspider.ini]
new file mode 100644
--- /dev/null
+++ b/testing/raptor/raptor/tests/raptor-tp6-3.ini
@@ -0,0 +1,57 @@
+# 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/.
+
+# raptor tp6-3
+
+[DEFAULT]
+type =  pageload
+playback = mitmproxy
+playback_binary_manifest = mitmproxy-rel-bin-{platform}.manifest
+python3_win_manifest = python3{x64}.manifest
+playback_pageset_manifest = mitmproxy-recordings-raptor-tp6-3.manifest
+page_cycles = 25
+unit = ms
+lower_is_better = true
+alert_threshold = 2.0
+# TTI/TTFI can take a while on some pages, and requires at least 5 seconds
+# beyond typical pageload time
+page_timeout = 30000
+gecko_profile_interval = 1
+gecko_profile_entries = 2000000
+
+[raptor-tp6-imdb-firefox]
+apps = firefox
+test_url = https://www.imdb.com/title/tt0084967/?ref_=nv_sr_2
+playback_recordings = imdb.mp
+measure = fnbpaint, dcf, ttfi
+
+[raptor-tp6-imgur-firefox]
+apps = firefox
+test_url = https://imgur.com/gallery/m5tYJL6
+playback_recordings = imgur.mp
+measure = fnbpaint, dcf, ttfi
+
+[raptor-tp6-wikia-firefox]
+apps = firefox
+test_url = http://fandom.wikia.com/articles/fallout-76-will-live-and-die-on-the-creativity-of-its-playerbase
+playback_recordings = wikia.mp
+measure = fnbpaint, dcf, ttfi
+
+[raptor-tp6-imdb-chrome]
+apps = chrome
+test_url = https://www.imdb.com/title/tt0084967/?ref_=nv_sr_2
+playback_recordings = imdb.mp
+measure = fcp
+
+[raptor-tp6-imgur-chrome]
+apps = chrome
+test_url = https://imgur.com/gallery/m5tYJL6
+playback_recordings = imgur.mp
+measure = fcp
+
+[raptor-tp6-wikia-chrome]
+apps = chrome
+test_url = http://fandom.wikia.com/articles/fallout-76-will-live-and-die-on-the-creativity-of-its-playerbase
+playback_recordings = wikia.mp
+measure = fcp
new file mode 100644
--- /dev/null
+++ b/testing/raptor/raptor/tests/raptor-tp6-4.ini
@@ -0,0 +1,45 @@
+# 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/.
+
+# raptor tp6-4
+
+[DEFAULT]
+type =  pageload
+playback = mitmproxy
+playback_binary_manifest = mitmproxy-rel-bin-{platform}.manifest
+python3_win_manifest = python3{x64}.manifest
+playback_pageset_manifest = mitmproxy-recordings-raptor-tp6-4.manifest
+page_cycles = 25
+unit = ms
+lower_is_better = true
+alert_threshold = 2.0
+# TTI/TTFI can take a while on some pages, and requires at least 5 seconds
+# beyond typical pageload time
+page_timeout = 30000
+gecko_profile_interval = 1
+gecko_profile_entries = 2000000
+
+[raptor-tp6-bing-firefox]
+apps = firefox
+test_url = https://www.bing.com/search?q=barack+obama
+playback_recordings = bing.mp
+measure = fnbpaint, dcf, ttfi
+
+[raptor-tp6-yandex-firefox]
+apps = firefox
+test_url = https://yandex.ru/search/?text=barack%20obama&lr=10115
+playback_recordings = yandex.mp
+measure = fnbpaint, dcf, ttfi
+
+[raptor-tp6-bing-chrome]
+apps = chrome
+test_url = https://www.bing.com/search?q=barack+obama
+playback_recordings = bing.mp
+measure = fcp
+
+[raptor-tp6-yandex-chrome]
+apps = chrome
+test_url = https://yandex.ru/search/?text=barack%20obama&lr=10115
+playback_recordings = yandex.mp
+measure = fcp
new file mode 100644
--- /dev/null
+++ b/testing/raptor/raptor/tests/raptor-tp6-5.ini
@@ -0,0 +1,46 @@
+# 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/.
+
+# raptor tp6-5
+
+[DEFAULT]
+type =  pageload
+playback = mitmproxy
+playback_binary_manifest = mitmproxy-rel-bin-{platform}.manifest
+python3_win_manifest = python3{x64}.manifest
+playback_pageset_manifest = mitmproxy-recordings-raptor-tp6-5.manifest
+page_cycles = 25
+unit = ms
+lower_is_better = true
+alert_threshold = 2.0
+# TTI/TTFI can take a while on some pages, and requires at least 5 seconds
+# beyond typical pageload time
+page_timeout = 30000
+gecko_profile_interval = 1
+gecko_profile_entries = 2000000
+
+[raptor-tp6-apple-firefox]
+apps = firefox
+test_url = https://www.apple.com/macbook-pro/
+playback_recordings = apple.mp
+measure = fnbpaint, dcf, ttfi
+
+[raptor-tp6-microsoft-firefox]
+apps = firefox
+test_url = https://www.microsoft.com/en-us/windows/get-windows-10
+page_timeout = 60000
+playback_recordings = microsoft.mp
+measure = fnbpaint, dcf, ttfi
+
+[raptor-tp6-apple-chrome]
+apps = chrome
+test_url = https://www.apple.com/macbook-pro/
+playback_recordings = apple.mp
+measure = fcp
+
+[raptor-tp6-microsoft-chrome]
+apps = chrome
+test_url = https://www.microsoft.com/en-us/windows/get-windows-10
+playback_recordings = microsoft.mp
+measure = fcp
new file mode 100644
--- /dev/null
+++ b/testing/raptor/raptor/tests/raptor-tp6-6.ini
@@ -0,0 +1,33 @@
+# 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/.
+
+# raptor tp6-6
+
+[DEFAULT]
+type =  pageload
+playback = mitmproxy
+playback_binary_manifest = mitmproxy-rel-bin-{platform}.manifest
+python3_win_manifest = python3{x64}.manifest
+playback_pageset_manifest = mitmproxy-recordings-raptor-tp6-6.manifest
+page_cycles = 25
+unit = ms
+lower_is_better = true
+alert_threshold = 2.0
+# TTI/TTFI can take a while on some pages, and requires at least 5 seconds
+# beyond typical pageload time
+page_timeout = 30000
+gecko_profile_interval = 1
+gecko_profile_entries = 2000000
+
+[raptor-tp6-reddit-firefox]
+apps = firefox
+test_url = https://www.reddit.com/r/technology/comments/9sqwyh/we_posed_as_100_senators_to_run_ads_on_facebook/
+playback_recordings = reddit.mp
+measure = fnbpaint, dcf, ttfi
+
+[raptor-tp6-reddit-chrome]
+apps = chrome
+test_url = https://www.reddit.com/r/technology/comments/9sqwyh/we_posed_as_100_senators_to_run_ads_on_facebook/
+playback_recordings = reddit.mp
+measure = fcp
--- a/testing/raptor/webext/raptor/manifest.json
+++ b/testing/raptor/webext/raptor/manifest.json
@@ -8,20 +8,32 @@
   "name": "Raptor",
   "version": "0.1",
   "description": "Performance measurement framework prototype",
   "background": {
     "scripts": ["auto_gen_test_config.js", "runner.js"]
   },
   "content_scripts": [
     {
-      "matches": ["*://*.amazon.com/*",
+      "matches": [
+                  "*://*.apple.com/*",
+                  "*://*.amazon.com/*",
+                  "*://*.bing.com/*",
                   "*://*.facebook.com/*",
                   "*://*.google.com/*",
-                  "*://*.youtube.com/*"],
+                  "*://*.imdb.com/*",
+                  "*://*.imgur.com/*",
+                  "*://*.microsoft.com/*",
+                  "*://*.reddit.com/*",
+                  "*://*.vice.com/*",
+                  "*://*.wikia.com/*",
+                  "*://*.wikipedia.org/*",
+                  "*://*.youtube.com/*",
+                  "*://*.yandex.ru/*"
+                  ],
       "js": ["measure.js"]
     },
     {
       "matches": ["*://*/Speedometer/index.html*",
                   "*://*/StyleBench/*",
                   "*://*/MotionMark/*",
                   "*://*/SunSpider/*",
                   "*://*/webaudio/*",
--- a/testing/specialpowers/content/SpecialPowersObserver.jsm
+++ b/testing/specialpowers/content/SpecialPowersObserver.jsm
@@ -22,16 +22,17 @@ const CHILD_LOGGER_SCRIPT = "resource://
 
 // Glue to add in the observer API to this object.  This allows us to share code with chrome tests
 Services.scriptloader.loadSubScript("resource://specialpowers/SpecialPowersObserverAPI.js");
 
 /* XPCOM gunk */
 function SpecialPowersObserver() {
   this._isFrameScriptLoaded = false;
   this._messageManager = Services.mm;
+  this._serviceWorkerListener = null;
 }
 
 
 SpecialPowersObserver.prototype = new SpecialPowersObserverAPI();
 
 SpecialPowersObserver.prototype.QueryInterface = ChromeUtils.generateQI([Ci.nsIObserver]);
 
 SpecialPowersObserver.prototype.observe = function(aSubject, aTopic, aData) {
@@ -104,28 +105,58 @@ SpecialPowersObserver.prototype.init = f
   var manifestFile = Services.io.newFileURI(testsURI).
                        QueryInterface(Ci.nsIFileURL).file;
 
   Components.manager.QueryInterface(Ci.nsIComponentRegistrar).
                  autoRegister(manifestFile);
 
   obs.addObserver(this, "http-on-modify-request");
 
+  // We would like to check after each test that there are no left over
+  // ServiceWorkers still registered. However, because that check happens in
+  // the child process, we would have to do IPC after ever test to verify in
+  // the parent process (the source of truth for ServiceWorkers) that our
+  // invariants are correct. To avoid that, we send messages down to the
+  // children when to let them know when ServiceWorkers are registered or
+  // unregistered (and become idle!) to be able to quicky verify this.
+  // If this seems to be randomly failing, make sure that you're waiting for
+  // the unregister promise to resolve before calling SimpleTest.finish.
+  let swm = Cc["@mozilla.org/serviceworkers/manager;1"]
+              .getService(Ci.nsIServiceWorkerManager);
+  let self = this;
+  this._serviceWorkerListener = {
+    cnt: 0,
+    onRegister() {
+      this.cnt++;
+      self.onRegister(this.cnt);
+    },
+
+    onUnregister() {
+      this.cnt--;
+      self.onUnregister(this.cnt);
+    },
+  };
+  swm.addListener(this._serviceWorkerListener);
+
   this._loadFrameScript();
 };
 
 SpecialPowersObserver.prototype.uninit = function() {
   var obs = Services.obs;
   obs.removeObserver(this, "chrome-document-global-created");
   obs.removeObserver(this, "http-on-modify-request");
   this._registerObservers._topics.forEach((element) => {
     obs.removeObserver(this._registerObservers, element);
   });
   this._removeProcessCrashObservers();
 
+  let swm = Cc["@mozilla.org/serviceworkers/manager;1"]
+              .getService(Ci.nsIServiceWorkerManager);
+  swm.removeListener(this._serviceWorkerListener);
+
   if (this._isFrameScriptLoaded) {
     this._messageManager.removeMessageListener("SPPrefService", this);
     this._messageManager.removeMessageListener("SPProcessCrashManagerWait", this);
     this._messageManager.removeMessageListener("SPProcessCrashService", this);
     this._messageManager.removeMessageListener("SPPingService", this);
     this._messageManager.removeMessageListener("SpecialPowers.Quit", this);
     this._messageManager.removeMessageListener("SpecialPowers.Focus", this);
     this._messageManager.removeMessageListener("SpecialPowers.CreateFiles", this);
@@ -277,8 +308,24 @@ SpecialPowersObserver.prototype.receiveM
         this._createdFiles = null;
       }
       break;
     default:
       return this._receiveMessage(aMessage);
   }
   return undefined;
 };
+
+SpecialPowersObserver.prototype.onRegister = function(cnt) {
+  // Send a message for the transition from 0 => 1.
+  if (cnt === 1) {
+    this._sendAsyncMessage("SPServiceWorkerRegistered",
+      { registered: true });
+  }
+};
+
+SpecialPowersObserver.prototype.onUnregister = function(cnt) {
+  // Send a message for the transition from 1 => 0.
+  if (cnt === 0) {
+    this._sendAsyncMessage("SPServiceWorkerRegistered",
+      { registered: false });
+  }
+};
--- a/testing/specialpowers/content/specialpowers.js
+++ b/testing/specialpowers/content/specialpowers.js
@@ -20,16 +20,17 @@ Cu.forcePermissiveCOWs();
 function SpecialPowers(window, mm) {
   this.mm = mm;
 
   this.window = Cu.getWeakReference(window);
   this._windowID = window.windowUtils.currentInnerWindowID;
   this._encounteredCrashDumpFiles = [];
   this._unexpectedCrashDumpFiles = { };
   this._crashDumpDir = null;
+  this._serviceWorkerRegistered = false;
   this.DOMWindowUtils = bindDOMWindowUtils(window);
   Object.defineProperty(this, "Components", {
       configurable: true, enumerable: true, value: this.getFullComponents(),
   });
   this._pongHandlers = [];
   this._messageListener = this._messageReceived.bind(this);
   this._grandChildFrameMM = null;
   this._createFilesOnError = null;
@@ -52,16 +53,17 @@ function SpecialPowers(window, mm) {
                             "SPLoadExtension",
                             "SPProcessCrashManagerWait",
                             "SPStartupExtension",
                             "SPUnloadExtension",
                             "SPExtensionMessage",
                             "SPRequestDumpCoverageCounters",
                             "SPRequestResetCoverageCounters"];
   mm.addMessageListener("SPPingService", this._messageListener);
+  mm.addMessageListener("SPServiceWorkerRegistered", this._messageListener);
   mm.addMessageListener("SpecialPowers.FilesCreated", this._messageListener);
   mm.addMessageListener("SpecialPowers.FilesError", this._messageListener);
   let self = this;
   Services.obs.addObserver(function onInnerWindowDestroyed(subject, topic, data) {
     var id = subject.QueryInterface(Ci.nsISupportsPRUint64).data;
     if (self._windowID === id) {
       Services.obs.removeObserver(onInnerWindowDestroyed, "inner-window-destroyed");
       try {
@@ -139,16 +141,20 @@ SpecialPowers.prototype._messageReceived
           handler();
         }
         if (this._grandChildFrameMM) {
           this._grandChildFrameMM.sendAsyncMessage("SPPingService", { op: "pong" });
         }
       }
       break;
 
+    case "SPServiceWorkerRegistered":
+      this._serviceWorkerRegistered = aMessage.data.registered;
+      break;
+
     case "SpecialPowers.FilesCreated":
       var createdHandler = this._createFilesOnSuccess;
       this._createFilesOnSuccess = null;
       this._createFilesOnError = null;
       if (createdHandler) {
         createdHandler(Cu.cloneInto(aMessage.data, this.mm.content));
       }
       break;
@@ -229,19 +235,28 @@ SpecialPowers.prototype.nestedFrameSetup
 
       let frameScript = "SpecialPowers.prototype.IsInNestedFrame=true;";
       mm.loadFrameScript("data:," + frameScript, false);
     }
   }, "remote-browser-shown");
 };
 
 SpecialPowers.prototype.isServiceWorkerRegistered = function() {
-  var swm = Cc["@mozilla.org/serviceworkers/manager;1"]
-              .getService(Ci.nsIServiceWorkerManager);
-  return swm.getAllRegistrations().length != 0;
+  // For the time being, if parent_intercept is false, we can assume that
+  // ServiceWorkers registered by the current test are all known to the SWM in
+  // this process.
+  if (!Services.prefs.getBoolPref("dom.serviceWorkers.parent_intercept", false)) {
+    let swm = Cc["@mozilla.org/serviceworkers/manager;1"]
+                .getService(Ci.nsIServiceWorkerManager);
+    return swm.getAllRegistrations().length != 0;
+  }
+
+  // Please see the comment in SpecialPowersObserver.jsm above
+  // this._serviceWorkerListener's assignment for what this returns.
+  return this._serviceWorkerRegistered;
 };
 
 // Attach our API to the window.
 function attachSpecialPowersToWindow(aWindow, mm) {
   try {
     if ((aWindow !== null) &&
         (aWindow !== undefined) &&
         (aWindow.wrappedJSObject) &&
--- a/testing/web-platform/meta/fetch/api/response/response-stream-with-broken-then.any.js.ini
+++ b/testing/web-platform/meta/fetch/api/response/response-stream-with-broken-then.any.js.ini
@@ -1,33 +1,9 @@
 [response-stream-with-broken-then.any.html]
-  [Attempt to inject undefined via Object.prototype.then.]
-    expected: FAIL
-
-  [Attempt to inject {done: false, value: bye} via Object.prototype.then.]
-    expected: FAIL
-
   [intercepting arraybuffer to body readable stream conversion via Object.prototype.then should not be possible]
     expected: FAIL
 
-  [Attempt to inject 8.2 via Object.prototype.then.]
-    expected: FAIL
-
-  [Attempt to inject value: undefined via Object.prototype.then.]
-    expected: FAIL
-
 
 [response-stream-with-broken-then.any.worker.html]
-  [Attempt to inject undefined via Object.prototype.then.]
-    expected: FAIL
-
-  [Attempt to inject {done: false, value: bye} via Object.prototype.then.]
-    expected: FAIL
-
   [intercepting arraybuffer to body readable stream conversion via Object.prototype.then should not be possible]
     expected: FAIL
 
-  [Attempt to inject 8.2 via Object.prototype.then.]
-    expected: FAIL
-
-  [Attempt to inject value: undefined via Object.prototype.then.]
-    expected: FAIL
-
--- a/testing/web-platform/meta/hr-time/idlharness.any.js.ini
+++ b/testing/web-platform/meta/hr-time/idlharness.any.js.ini
@@ -1,19 +1,2 @@
-[idlharness.any.worker.html]
-  [WorkerGlobalScope interface: attribute performance]
-    expected: FAIL
-
-
-[idlharness.any.sharedworker.html]
-  [WorkerGlobalScope interface: attribute performance]
-    expected: FAIL
-
-
-[idlharness.any.html]
-
 [idlharness.https.any.serviceworker.html]
   expected: TIMEOUT
-
-[idlharness.any.serviceworker.html]
-  [WorkerGlobalScope interface: attribute performance]
-    expected: FAIL
-
deleted file mode 100644
--- a/testing/web-platform/meta/streams/readable-streams/brand-checks.dedicatedworker.html.ini
+++ /dev/null
@@ -1,16 +0,0 @@
-[brand-checks.dedicatedworker.html]
-  [Can get the ReadableStreamReader constructor indirectly]
-    expected: FAIL
-
-  [ReadableStreamReader.prototype.closed enforces a brand check]
-    expected: FAIL
-
-  [ReadableStreamReader.prototype.cancel enforces a brand check]
-    expected: FAIL
-
-  [ReadableStreamReader.prototype.read enforces a brand check]
-    expected: FAIL
-
-  [ReadableStreamReader.prototype.releaseLock enforces a brand check]
-    expected: FAIL
-
deleted file mode 100644
--- a/testing/web-platform/meta/streams/readable-streams/brand-checks.html.ini
+++ /dev/null
@@ -1,16 +0,0 @@
-[brand-checks.html]
-  [Can get the ReadableStreamReader constructor indirectly]
-    expected: FAIL
-
-  [ReadableStreamReader.prototype.closed enforces a brand check]
-    expected: FAIL
-
-  [ReadableStreamReader.prototype.cancel enforces a brand check]
-    expected: FAIL
-
-  [ReadableStreamReader.prototype.read enforces a brand check]
-    expected: FAIL
-
-  [ReadableStreamReader.prototype.releaseLock enforces a brand check]
-    expected: FAIL
-
deleted file mode 100644
--- a/testing/web-platform/meta/streams/readable-streams/brand-checks.serviceworker.https.html.ini
+++ /dev/null
@@ -1,16 +0,0 @@
-[brand-checks.serviceworker.https.html]
-  [Can get the ReadableStreamReader constructor indirectly]
-    expected: FAIL
-
-  [ReadableStreamReader.prototype.closed enforces a brand check]
-    expected: FAIL
-
-  [ReadableStreamReader.prototype.cancel enforces a brand check]
-    expected: FAIL
-
-  [ReadableStreamReader.prototype.read enforces a brand check]
-    expected: FAIL
-
-  [ReadableStreamReader.prototype.releaseLock enforces a brand check]
-    expected: FAIL
-
deleted file mode 100644
--- a/testing/web-platform/meta/streams/readable-streams/brand-checks.sharedworker.html.ini
+++ /dev/null
@@ -1,16 +0,0 @@
-[brand-checks.sharedworker.html]
-  [Can get the ReadableStreamReader constructor indirectly]
-    expected: FAIL
-
-  [ReadableStreamReader.prototype.closed enforces a brand check]
-    expected: FAIL
-
-  [ReadableStreamReader.prototype.cancel enforces a brand check]
-    expected: FAIL
-
-  [ReadableStreamReader.prototype.read enforces a brand check]
-    expected: FAIL
-
-  [ReadableStreamReader.prototype.releaseLock enforces a brand check]
-    expected: FAIL
-
--- a/testing/web-platform/meta/streams/readable-streams/garbage-collection.dedicatedworker.html.ini
+++ b/testing/web-platform/meta/streams/readable-streams/garbage-collection.dedicatedworker.html.ini
@@ -1,7 +1,4 @@
 [garbage-collection.dedicatedworker.html]
-  [Garbage-collecting a ReadableStreamReader should not unlock its stream]
-    expected: FAIL
-
   [ReadableStreamController methods should continue working properly when scripts lose their reference to the readable stream]
     expected: FAIL
 
--- a/testing/web-platform/meta/streams/readable-streams/garbage-collection.html.ini
+++ b/testing/web-platform/meta/streams/readable-streams/garbage-collection.html.ini
@@ -1,7 +1,4 @@
 [garbage-collection.html]
-  [Garbage-collecting a ReadableStreamReader should not unlock its stream]
-    expected: FAIL
-
   [ReadableStreamController methods should continue working properly when scripts lose their reference to the readable stream]
     expected: FAIL
 
--- a/testing/web-platform/meta/streams/readable-streams/garbage-collection.serviceworker.https.html.ini
+++ b/testing/web-platform/meta/streams/readable-streams/garbage-collection.serviceworker.https.html.ini
@@ -1,7 +1,4 @@
 [garbage-collection.serviceworker.https.html]
-  [Garbage-collecting a ReadableStreamReader should not unlock its stream]
-    expected: FAIL
-
   [ReadableStreamController methods should continue working properly when scripts lose their reference to the readable stream]
     expected: FAIL
 
--- a/testing/web-platform/meta/streams/readable-streams/garbage-collection.sharedworker.html.ini
+++ b/testing/web-platform/meta/streams/readable-streams/garbage-collection.sharedworker.html.ini
@@ -1,7 +1,4 @@
 [garbage-collection.sharedworker.html]
-  [Garbage-collecting a ReadableStreamReader should not unlock its stream]
-    expected: FAIL
-
   [ReadableStreamController methods should continue working properly when scripts lose their reference to the readable stream]
     expected: FAIL
 
deleted file mode 100644
--- a/testing/web-platform/meta/streams/readable-streams/tee.dedicatedworker.html.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[tee.dedicatedworker.html]
-  disabled:
-    if verify: fails in verify mode
deleted file mode 100644
--- a/testing/web-platform/meta/streams/readable-streams/tee.html.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[tee.html]
-  disabled:
-    if verify and debug: fails in verify mode
deleted file mode 100644
--- a/testing/web-platform/meta/streams/readable-streams/tee.serviceworker.https.html.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[tee.serviceworker.https.html]
-  disabled:
-    if verify and debug: fails in verify mode
deleted file mode 100644
--- a/testing/web-platform/meta/streams/readable-streams/tee.sharedworker.html.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[tee.sharedworker.html]
-  disabled:
-    if verify and debug: fails in verify mode
--- a/testing/web-platform/meta/streams/readable-streams/templated.dedicatedworker.html.ini
+++ b/testing/web-platform/meta/streams/readable-streams/templated.dedicatedworker.html.ini
@@ -1,121 +1,7 @@
 [templated.dedicatedworker.html]
   disabled:
     if verify and (os == "linux") and debug: fails in verify mode
 
   [ReadableStream (empty): instances have the correct methods and properties]
     expected: FAIL
 
-  [calling getReader with invalid arguments should throw appropriate errors]
-    expected: FAIL
-
-  [locked should be true]
-    expected: FAIL
-
-  [read() should never settle]
-    expected: FAIL
-
-  [two read()s should both never settle]
-    expected: FAIL
-
-  [read() should return distinct promises each time]
-    expected: FAIL
-
-  [getReader() again on the stream should fail]
-    expected: FAIL
-
-  [releasing the lock with pending read requests should throw but the read requests should stay pending]
-    expected: FAIL
-
-  [releasing the lock should cause further read() calls to reject with a TypeError]
-    expected: FAIL
-
-  [releasing the lock should cause closed calls to reject with a TypeError]
-    expected: FAIL
-
-  [releasing the lock should cause locked to become false]
-    expected: FAIL
-
-  [canceling via the reader should cause the reader to act closed]
-    expected: FAIL
-
-  [canceling via the stream should fail]
-    expected: FAIL
-
-  [cancel() should return a distinct fulfilled promise each time]
-    expected: FAIL
-
-  [locked should be false]
-    expected: FAIL
-
-  [getReader() should be OK]
-    expected: FAIL
-
-  [should be able to acquire multiple readers if they are released in succession]
-    expected: FAIL
-
-  [should not be able to acquire a second reader if we don't release the first one]
-    expected: FAIL
-
-  [read() should fulfill with { value: undefined, done: true }]
-    expected: FAIL
-
-  [read() multiple times should fulfill with { value: undefined, done: true }]
-    expected: FAIL
-
-  [read() should work when used within another read() fulfill callback]
-    expected: FAIL
-
-  [closed should fulfill with undefined]
-    expected: FAIL
-
-  [releasing the lock should cause closed to reject and change identity]
-    expected: FAIL
-
-  [getReader() should return a reader that acts errored]
-    expected: FAIL
-
-  [read() twice should give the error each time]
-    expected: FAIL
-
-  [should be able to obtain a second reader, with the correct closed promise]
-    expected: FAIL
-
-  [should not be able to obtain additional readers if we don't release the first lock]
-    expected: FAIL
-
-  [cancel() should return a distinct rejected promise each time]
-    expected: FAIL
-
-  [reader cancel() should return a distinct rejected promise each time]
-    expected: FAIL
-
-  [closed should reject with the error]
-    expected: FAIL
-
-  [read() should reject with the error]
-    expected: FAIL
-
-  [calling read() twice without waiting will eventually give both chunks (sequential)]
-    expected: FAIL
-
-  [calling read() twice without waiting will eventually give both chunks (nested)]
-    expected: FAIL
-
-  [cancel() after a read() should still give that single read result]
-    expected: FAIL
-
-  [third read(), without waiting, should give { value: undefined, done: true } (sequential)]
-    expected: FAIL
-
-  [third read(), without waiting, should give { value: undefined, done: true } (nested)]
-    expected: FAIL
-
-  [draining the stream via read() should cause the reader closed promise to fulfill, but locked stays true]
-    expected: FAIL
-
-  [releasing the lock after the stream is closed should cause locked to become false]
-    expected: FAIL
-
-  [reader's closed property always returns the same promise]
-    expected: FAIL
-
--- a/testing/web-platform/meta/streams/readable-streams/templated.html.ini
+++ b/testing/web-platform/meta/streams/readable-streams/templated.html.ini
@@ -1,118 +1,4 @@
 [templated.html]
   [ReadableStream (empty): instances have the correct methods and properties]
     expected: FAIL
 
-  [calling getReader with invalid arguments should throw appropriate errors]
-    expected: FAIL
-
-  [locked should be true]
-    expected: FAIL
-
-  [read() should never settle]
-    expected: FAIL
-
-  [two read()s should both never settle]
-    expected: FAIL
-
-  [read() should return distinct promises each time]
-    expected: FAIL
-
-  [getReader() again on the stream should fail]
-    expected: FAIL
-
-  [releasing the lock with pending read requests should throw but the read requests should stay pending]
-    expected: FAIL
-
-  [releasing the lock should cause further read() calls to reject with a TypeError]
-    expected: FAIL
-
-  [releasing the lock should cause closed calls to reject with a TypeError]
-    expected: FAIL
-
-  [releasing the lock should cause locked to become false]
-    expected: FAIL
-
-  [canceling via the reader should cause the reader to act closed]
-    expected: FAIL
-
-  [canceling via the stream should fail]
-    expected: FAIL
-
-  [cancel() should return a distinct fulfilled promise each time]
-    expected: FAIL
-
-  [locked should be false]
-    expected: FAIL
-
-  [getReader() should be OK]
-    expected: FAIL
-
-  [should be able to acquire multiple readers if they are released in succession]
-    expected: FAIL
-
-  [should not be able to acquire a second reader if we don't release the first one]
-    expected: FAIL
-
-  [read() should fulfill with { value: undefined, done: true }]
-    expected: FAIL
-
-  [read() multiple times should fulfill with { value: undefined, done: true }]
-    expected: FAIL
-
-  [read() should work when used within another read() fulfill callback]
-    expected: FAIL
-
-  [closed should fulfill with undefined]
-    expected: FAIL
-
-  [releasing the lock should cause closed to reject and change identity]
-    expected: FAIL
-
-  [getReader() should return a reader that acts errored]
-    expected: FAIL
-
-  [read() twice should give the error each time]
-    expected: FAIL
-
-  [should be able to obtain a second reader, with the correct closed promise]
-    expected: FAIL
-
-  [should not be able to obtain additional readers if we don't release the first lock]
-    expected: FAIL
-
-  [cancel() should return a distinct rejected promise each time]
-    expected: FAIL
-
-  [reader cancel() should return a distinct rejected promise each time]
-    expected: FAIL
-
-  [closed should reject with the error]
-    expected: FAIL
-
-  [read() should reject with the error]
-    expected: FAIL
-
-  [calling read() twice without waiting will eventually give both chunks (sequential)]
-    expected: FAIL
-
-  [calling read() twice without waiting will eventually give both chunks (nested)]
-    expected: FAIL
-
-  [cancel() after a read() should still give that single read result]
-    expected: FAIL
-
-  [third read(), without waiting, should give { value: undefined, done: true } (sequential)]
-    expected: FAIL
-
-  [third read(), without waiting, should give { value: undefined, done: true } (nested)]
-    expected: FAIL
-
-  [draining the stream via read() should cause the reader closed promise to fulfill, but locked stays true]
-    expected: FAIL
-
-  [releasing the lock after the stream is closed should cause locked to become false]
-    expected: FAIL
-
-  [reader's closed property always returns the same promise]
-    expected: FAIL
-
--- a/testing/web-platform/meta/streams/readable-streams/templated.serviceworker.https.html.ini
+++ b/testing/web-platform/meta/streams/readable-streams/templated.serviceworker.https.html.ini
@@ -1,121 +1,6 @@
 [templated.serviceworker.https.html]
   disabled:
     if verify and (os == "linux") and debug: fails in verify mode
 
   [ReadableStream (empty): instances have the correct methods and properties]
     expected: FAIL
-
-  [calling getReader with invalid arguments should throw appropriate errors]
-    expected: FAIL
-
-  [locked should be true]
-    expected: FAIL
-
-  [read() should never settle]
-    expected: FAIL
-
-  [two read()s should both never settle]
-    expected: FAIL
-
-  [read() should return distinct promises each time]
-    expected: FAIL
-
-  [getReader() again on the stream should fail]
-    expected: FAIL
-
-  [releasing the lock with pending read requests should throw but the read requests should stay pending]
-    expected: FAIL
-
-  [releasing the lock should cause further read() calls to reject with a TypeError]
-    expected: FAIL
-
-  [releasing the lock should cause closed calls to reject with a TypeError]
-    expected: FAIL
-
-  [releasing the lock should cause locked to become false]
-    expected: FAIL
-
-  [canceling via the reader should cause the reader to act closed]
-    expected: FAIL
-
-  [canceling via the stream should fail]
-    expected: FAIL
-
-  [cancel() should return a distinct fulfilled promise each time]
-    expected: FAIL
-
-  [locked should be false]
-    expected: FAIL
-
-  [getReader() should be OK]
-    expected: FAIL
-
-  [should be able to acquire multiple readers if they are released in succession]
-    expected: FAIL
-
-  [should not be able to acquire a second reader if we don't release the first one]
-    expected: FAIL
-
-  [read() should fulfill with { value: undefined, done: true }]
-    expected: FAIL
-
-  [read() multiple times should fulfill with { value: undefined, done: true }]
-    expected: FAIL
-
-  [read() should work when used within another read() fulfill callback]
-    expected: FAIL
-
-  [closed should fulfill with undefined]
-    expected: FAIL
-
-  [releasing the lock should cause closed to reject and change identity]
-    expected: FAIL
-
-  [getReader() should return a reader that acts errored]
-    expected: FAIL
-
-  [read() twice should give the error each time]
-    expected: FAIL
-
-  [should be able to obtain a second reader, with the correct closed promise]
-    expected: FAIL
-
-  [should not be able to obtain additional readers if we don't release the first lock]
-    expected: FAIL
-
-  [cancel() should return a distinct rejected promise each time]
-    expected: FAIL
-
-  [reader cancel() should return a distinct rejected promise each time]
-    expected: FAIL
-
-  [closed should reject with the error]
-    expected: FAIL
-
-  [read() should reject with the error]
-    expected: FAIL
-
-  [calling read() twice without waiting will eventually give both chunks (sequential)]
-    expected: FAIL
-
-  [calling read() twice without waiting will eventually give both chunks (nested)]
-    expected: FAIL
-
-  [cancel() after a read() should still give that single read result]
-    expected: FAIL
-
-  [third read(), without waiting, should give { value: undefined, done: true } (sequential)]
-    expected: FAIL
-
-  [third read(), without waiting, should give { value: undefined, done: true } (nested)]
-    expected: FAIL
-
-  [draining the stream via read() should cause the reader closed promise to fulfill, but locked stays true]
-    expected: FAIL
-
-  [releasing the lock after the stream is closed should cause locked to become false]
-    expected: FAIL
-
-  [reader's closed property always returns the same promise]
-    expected: FAIL
-
--- a/testing/web-platform/meta/streams/readable-streams/templated.sharedworker.html.ini
+++ b/testing/web-platform/meta/streams/readable-streams/templated.sharedworker.html.ini
@@ -1,118 +1,4 @@
 [templated.sharedworker.html]
   [ReadableStream (empty): instances have the correct methods and properties]
     expected: FAIL
 
-  [calling getReader with invalid arguments should throw appropriate errors]
-    expected: FAIL
-
-  [locked should be true]
-    expected: FAIL
-
-  [read() should never settle]
-    expected: FAIL
-
-  [two read()s should both never settle]
-    expected: FAIL
-
-  [read() should return distinct promises each time]
-    expected: FAIL
-
-  [getReader() again on the stream should fail]
-    expected: FAIL
-
-  [releasing the lock with pending read requests should throw but the read requests should stay pending]
-    expected: FAIL
-
-  [releasing the lock should cause further read() calls to reject with a TypeError]
-    expected: FAIL
-
-  [releasing the lock should cause closed calls to reject with a TypeError]
-    expected: FAIL
-
-  [releasing the lock should cause locked to become false]
-    expected: FAIL
-
-  [canceling via the reader should cause the reader to act closed]
-    expected: FAIL
-
-  [canceling via the stream should fail]
-    expected: FAIL
-
-  [cancel() should return a distinct fulfilled promise each time]
-    expected: FAIL
-
-  [locked should be false]
-    expected: FAIL
-
-  [getReader() should be OK]
-    expected: FAIL
-
-  [should be able to acquire multiple readers if they are released in succession]
-    expected: FAIL
-
-  [should not be able to acquire a second reader if we don't release the first one]
-    expected: FAIL
-
-  [read() should fulfill with { value: undefined, done: true }]
-    expected: FAIL
-
-  [read() multiple times should fulfill with { value: undefined, done: true }]
-    expected: FAIL
-
-  [read() should work when used within another read() fulfill callback]
-    expected: FAIL
-
-  [closed should fulfill with undefined]
-    expected: FAIL
-
-  [releasing the lock should cause closed to reject and change identity]
-    expected: FAIL
-
-  [getReader() should return a reader that acts errored]
-    expected: FAIL
-
-  [read() twice should give the error each time]
-    expected: FAIL
-
-  [should be able to obtain a second reader, with the correct closed promise]
-    expected: FAIL
-
-  [should not be able to obtain additional readers if we don't release the first lock]
-    expected: FAIL
-
-  [cancel() should return a distinct rejected promise each time]
-    expected: FAIL
-
-  [reader cancel() should return a distinct rejected promise each time]
-    expected: FAIL
-
-  [closed should reject with the error]
-    expected: FAIL
-
-  [read() should reject with the error]
-    expected: FAIL
-
-  [calling read() twice without waiting will eventually give both chunks (sequential)]
-    expected: FAIL
-
-  [calling read() twice without waiting will eventually give both chunks (nested)]
-    expected: FAIL
-
-  [cancel() after a read() should still give that single read result]
-    expected: FAIL
-
-  [third read(), without waiting, should give { value: undefined, done: true } (sequential)]