Merge autoland to mozilla-central. a=merge
authorNorisz Fay <nfay@mozilla.com>
Tue, 25 Jan 2022 11:56:56 +0200
changeset 605392 e960e654cbc9f60ce79eb1535fd6ec4e3acc2029
parent 605346 3304275ad025ab9cb3d751efc837dd79c99d9bfe (current diff)
parent 605391 32af039c697d1d53d79ca3c787405138cc3404a3 (diff)
child 605428 0ed582a3706fcdad4756e9e026d771769c9c1f53
push id39192
push usernfay@mozilla.com
push dateTue, 25 Jan 2022 10:00:58 +0000
treeherdermozilla-central@e960e654cbc9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone98.0a1
first release with
nightly linux32
e960e654cbc9 / 98.0a1 / 20220125100058 / files
nightly linux64
e960e654cbc9 / 98.0a1 / 20220125100058 / files
nightly mac
e960e654cbc9 / 98.0a1 / 20220125100058 / files
nightly win32
e960e654cbc9 / 98.0a1 / 20220125100058 / files
nightly win64
e960e654cbc9 / 98.0a1 / 20220125100058 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge autoland to mozilla-central. a=merge
--- a/browser/base/content/aboutDialog.js
+++ b/browser/base/content/aboutDialog.js
@@ -18,37 +18,35 @@ if (AppConstants.MOZ_UPDATER) {
   );
 }
 
 async function init(aEvent) {
   if (aEvent.target != document) {
     return;
   }
 
-  var distroId = Services.prefs.getCharPref("distribution.id", "");
+  let defaults = Services.prefs.getDefaultBranch(null);
+  let distroId = defaults.getCharPref("distribution.id", "");
   if (distroId) {
-    var distroAbout = Services.prefs.getStringPref("distribution.about", "");
+    let distroAbout = defaults.getStringPref("distribution.about", "");
     // If there is about text, we always show it.
     if (distroAbout) {
-      var distroField = document.getElementById("distribution");
+      let distroField = document.getElementById("distribution");
       distroField.value = distroAbout;
       distroField.style.display = "block";
     }
     // If it's not a mozilla distribution, show the rest,
     // unless about text exists, then we always show.
     if (!distroId.startsWith("mozilla-") || distroAbout) {
-      var distroVersion = Services.prefs.getCharPref(
-        "distribution.version",
-        ""
-      );
+      let distroVersion = defaults.getCharPref("distribution.version", "");
       if (distroVersion) {
         distroId += " - " + distroVersion;
       }
 
-      var distroIdField = document.getElementById("distributionId");
+      let distroIdField = document.getElementById("distributionId");
       distroIdField.value = distroId;
       distroIdField.style.display = "block";
     }
   }
 
   // Include the build ID and display warning if this is an "a#" (nightly or aurora) build
   let versionId = "aboutDialog-version";
   let versionAttributes = {
--- a/browser/base/content/test/performance/browser_tabopen.js
+++ b/browser/base/content/test/performance/browser_tabopen.js
@@ -57,70 +57,93 @@ add_task(async function() {
     .getElementById("tabs-newtab-button")
     .getBoundingClientRect();
   let textBoxRect = gURLBar
     .querySelector("moz-input-box")
     .getBoundingClientRect();
 
   let inRange = (val, min, max) => min <= val && val <= max;
 
+  let inTabStrip = function(r) {
+    return (
+      r.y1 >= tabStripRect.top &&
+      r.y2 <= tabStripRect.bottom &&
+      r.x1 >= tabStripRect.left &&
+      r.x2 <= tabStripRect.right
+    );
+  };
+
+  let isExpectedChange = function(r) {
+    // We expect all changes to be within the tab strip.
+    if (!inTabStrip(r)) {
+      return false;
+    }
+
+    // The first tab should get deselected at the same time as the next tab
+    // starts appearing, so we should have one rect that includes the first tab
+    // but is wider.
+    if (
+      inRange(r.w, minTabWidth, maxTabWidth * 2) &&
+      inRange(r.x1, firstTabRect.x, firstTabRect.x + tabPaddingStart)
+    ) {
+      return true;
+    }
+
+    // The second tab gets painted several times due to tabopen animation.
+    let isSecondTabRect =
+      inRange(
+        r.x1,
+        firstTabRect.right - 1, // -1 for the border on Win7
+        firstTabRect.right + firstTabRect.width
+      ) &&
+      r.x2 <
+        firstTabRect.right +
+          firstTabRect.width +
+          // Sometimes the '+' is in the same rect.
+          newTabButtonRect.width;
+
+    if (isSecondTabRect) {
+      return true;
+    }
+    // The '+' icon moves with an animation. At the end of the animation
+    // the former and new positions can touch each other causing the rect
+    // to have twice the icon's width.
+    if (r.h == 13 && r.w <= 2 * 13 + kMaxEmptyPixels) {
+      return true;
+    }
+
+    // We sometimes have a rect for the right most 2px of the '+' button.
+    if (r.h == 2 && r.w == 2) {
+      return true;
+    }
+
+    // Same for the 'X' icon.
+    if (r.h == 10 && r.w <= 2 * 10) {
+      return true;
+    }
+
+    // Other changes are unexpected.
+    return false;
+  };
+
   // Add a reflow observer and open a new tab.
   await withPerfObserver(
     async function() {
       let switchDone = BrowserTestUtils.waitForEvent(window, "TabSwitchDone");
       BrowserOpenTab();
       await BrowserTestUtils.waitForEvent(
         gBrowser.selectedTab,
         "TabAnimationEnd"
       );
       await switchDone;
     },
     {
       expectedReflows: EXPECTED_REFLOWS,
       frames: {
-        filter: rects =>
-          rects.filter(
-            r =>
-              !(
-                // We expect all changes to be within the tab strip.
-                (
-                  r.y1 >= tabStripRect.top &&
-                  r.y2 <= tabStripRect.bottom &&
-                  r.x1 >= tabStripRect.left &&
-                  r.x2 <= tabStripRect.right &&
-                  // The first tab should get deselected at the same time as the next
-                  // tab starts appearing, so we should have one rect that includes the
-                  // first tab but is wider.
-                  ((inRange(r.w, minTabWidth, maxTabWidth * 2) &&
-                    inRange(
-                      r.x1,
-                      firstTabRect.x,
-                      firstTabRect.x + tabPaddingStart
-                    )) ||
-                  // The second tab gets painted several times due to tabopen animation.
-                  (inRange(
-                    r.x1,
-                    firstTabRect.right - 1, // -1 for the border on Win7
-                    firstTabRect.right + firstTabRect.width
-                  ) &&
-                    r.x2 <
-                      firstTabRect.right +
-                        firstTabRect.width +
-                        newTabButtonRect.width) || // Sometimes the '+' is in the same rect.
-                    // The '+' icon moves with an animation. At the end of the animation
-                    // the former and new positions can touch each other causing the rect
-                    // to have twice the icon's width.
-                    (r.h == 13 && r.w <= 2 * 13 + kMaxEmptyPixels) ||
-                    // We sometimes have a rect for the right most 2px of the '+' button.
-                    (r.h == 2 && r.w == 2) ||
-                    // Same for the 'X' icon.
-                    (r.h == 10 && r.w <= 2 * 10))
-                )
-              )
-          ),
+        filter: rects => rects.filter(r => !isExpectedChange(r)),
         exceptions: [
           {
             name:
               "bug 1446452 - the new tab should appear at the same time as the" +
               " previous one gets deselected",
             condition: r =>
               // In tab strip
               r.y1 >= tabStripRect.top &&
--- a/browser/components/pocket/webpack.config.js
+++ b/browser/components/pocket/webpack.config.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/. */
 /* eslint-env node */
 
 module.exports = {
+  mode: "production",
   entry: {
     main: "./content/panels/js/main.js",
   },
   output: {
     filename: "[name].bundle.js",
     path: `${__dirname}/content/panels/js/`,
   },
   module: {
--- a/browser/components/preferences/main.js
+++ b/browser/components/preferences/main.js
@@ -591,33 +591,31 @@ var gMainPane = {
         "app.releaseNotesURL"
       );
       if (relNotesURL != "about:blank") {
         relNotesLink.href = relNotesURL;
         relNotesLink.hidden = false;
       }
     }
 
-    let distroId = Services.prefs.getCharPref("distribution.id", "");
+    let defaults = Services.prefs.getDefaultBranch(null);
+    let distroId = defaults.getCharPref("distribution.id", "");
     if (distroId) {
       let distroString = distroId;
 
-      let distroVersion = Services.prefs.getCharPref(
-        "distribution.version",
-        ""
-      );
+      let distroVersion = defaults.getCharPref("distribution.version", "");
       if (distroVersion) {
         distroString += " - " + distroVersion;
       }
 
       let distroIdField = document.getElementById("distributionId");
       distroIdField.value = distroString;
       distroIdField.hidden = false;
 
-      let distroAbout = Services.prefs.getStringPref("distribution.about", "");
+      let distroAbout = defaults.getStringPref("distribution.about", "");
       if (distroAbout) {
         let distroField = document.getElementById("distribution");
         distroField.value = distroAbout;
         distroField.hidden = false;
       }
     }
 
     if (AppConstants.MOZ_UPDATER) {
--- a/browser/components/preferences/moreFromMozilla.inc.xhtml
+++ b/browser/components/preferences/moreFromMozilla.inc.xhtml
@@ -33,18 +33,18 @@
     </hbox>
     <html:div>
       <button class="primary small-button" hidden="true" />
     </html:div>
     <html:div class="qr-code-box" hidden="true">
       <label class="qr-code-title-label">
         <html:h3 class="qr-code-box-title" />
       </label>
-      <html:img class="qr-code-box-image" role="presentation" />
-      <html:a class="wants-telemetry qr-code-button text-link" target="_blank"/>
+      <html:img class="qr-code-box-image" />
+      <html:a class="wants-telemetry qr-code-button text-link" target="_blank" />
     </html:div>
   </html:div>
 </html:template>
 
 <html:template id="advanced">
   <vbox class="mozilla-product-item advanced">
     <hbox class="product-img">
       <vbox class="product-info" flex="1">
--- a/browser/components/preferences/moreFromMozilla.js
+++ b/browser/components/preferences/moreFromMozilla.js
@@ -231,16 +231,21 @@ var gMoreFromMozillaPane = {
           this.getTemplateName() +
           `${
             AppConstants.isChinaRepack() &&
             this.getTemplateName().includes("simple")
               ? "-cn"
               : ""
           }` +
           ".svg";
+        // Add image a11y attributes
+        img.setAttribute(
+          "data-l10n-id",
+          "more-from-moz-qr-code-firefox-mobile-img"
+        );
 
         // Note that the QR code image itself is _not_ a link; this is a link that
         // is directly below the image.
         let qrc_btn = template.querySelector(".qr-code-button");
 
         // So the telemetry includes info about which option is being used
         qrc_btn.id = `${this.option}-${product.qrcode.button.id}`;
         qrc_btn.setAttribute(
--- a/browser/extensions/pictureinpicture/tests/browser/browser.ini
+++ b/browser/extensions/pictureinpicture/tests/browser/browser.ini
@@ -9,10 +9,9 @@ prefs =
   media.videocontrols.picture-in-picture.enabled=true
   media.videocontrols.picture-in-picture.video-toggle.enabled=true
   media.videocontrols.picture-in-picture.video-toggle.testing=true
   media.videocontrols.picture-in-picture.video-toggle.always-show=true
   media.videocontrols.picture-in-picture.video-toggle.has-used=true
   media.videocontrols.picture-in-picture.video-toggle.position="right"
 
 [browser_mock_wrapper.js]
-skip-if =
-  debug || !nightly_build # Bug 1749819
+skip-if = !nightly_build # Bug 1751793
--- a/browser/locales/en-US/browser/preferences/moreFromMozilla.ftl
+++ b/browser/locales/en-US/browser/preferences/moreFromMozilla.ftl
@@ -25,13 +25,15 @@ more-from-moz-mozilla-rally-description 
 # This string is specific to the product Mozilla Rally which is US only.
 more-from-moz-mozilla-rally-description-advanced = Donate your data to research studies working to create a safer, more open internet that helps people, not Big Tech.
 # This string is specific to the product Mozilla Rally which is US only.
 more-from-moz-button-mozilla-rally =
   .label = Join { -rally-short-name }
 
 more-from-moz-qr-code-box-firefox-mobile-title = Download using your mobile device. Point your camera at the QR code. When a link appears, tap it.
 more-from-moz-qr-code-box-firefox-mobile-button = Send an email to your phone instead
+more-from-moz-qr-code-firefox-mobile-img =
+  .alt = QR code to download { -brand-product-name } Mobile
 
 more-from-moz-button-mozilla-vpn =
   .label = Get VPN
 
 more-from-moz-learn-more-link = Learn more
--- a/browser/themes/shared/preferences/preferences.inc.css
+++ b/browser/themes/shared/preferences/preferences.inc.css
@@ -1194,17 +1194,17 @@ richlistitem .text-link:hover {
   translate: 0 2px
 }
 
 .checkbox-row a {
   margin-inline-start: 6px;
 }
 
 #moreFromMozillaCategory-header .subtitle {
-  color: var(--in-content-deemphasized-text);
+  color: var(--in-content-text-color);
   margin: 4px 0 10px;
   line-height: 1.4em;
 }
 
 #moreFromMozillaCategory .mozilla-product-item {
   margin-top: 30px;
 }
 
@@ -1237,17 +1237,17 @@ richlistitem .text-link:hover {
 }
 
 #moreFromMozillaCategory .product-title:-moz-locale-dir(rtl),
 #moreFromMozillaCategory .product-img:-moz-locale-dir(rtl) {
   background-position-x: right;
 }
 
 #moreFromMozillaCategory .description {
-  color: var(--in-content-deemphasized-text);
+  color: var(--in-content-text-color);
   padding-inline-start: 30px;
   margin: 2px 0;
   line-height: 1.4em;
 }
 
 #moreFromMozillaCategory .advanced .description {
   padding-inline-start: 0;
 }
@@ -1276,21 +1276,17 @@ richlistitem .text-link:hover {
   }
 }
 
 #moreFromMozillaCategory .advanced .product-info {
   width: 500px;
 }
 
 #moreFromMozillaCategory .text-link {
-  font-size: .87em;
   line-height: 1.4em;
-
-  text-decoration: underline;
-
   margin-block: 2px 0;
   margin-inline: 6px 0;
 }
 
 #moreFromMozillaCategory .simple .text-link {
   display: block;
   margin-inline-start: 4px;
 }
@@ -1324,24 +1320,30 @@ richlistitem .text-link:hover {
  * The :not clause is required because hiding an element with `display: flex` only
  * partially hides it.
  */
 .qr-code-box:not([hidden="true"]) {
   max-width: 320px; /* a unit better than px for this & next? */
   min-height: 204px;
   margin-block: 10px;
   margin-inline-start: 30px;
-  background-color: #f9f9fb;
+  background-color: var(--in-content-box-info-background);
   display: flex;
   flex-direction: column;
   align-items: center;
   justify-content: space-around;
   border-radius: 4px;
 }
 
+@media (prefers-contrast) {
+  .qr-code-box:not([hidden="true"]) {
+    border: 1px solid currentColor;
+  }
+}
+
 .advanced .qr-code-box {
   align-items: flex-start;
   margin-inline-start: 12px;
 }
 
 .qr-code-top-box {
   display: flex;
 }
@@ -1405,19 +1407,16 @@ richlistitem .text-link:hover {
   margin-inline: 16px;
   margin-block: revert;
 }
 
 #moreFromMozillaCategory .qr-code-button {
   font-size: .87em;
   font-weight: 400;
   line-height: 1.42em;
-  text-decoration: underline;
-  color: #5b5b66;
-
   flex: 2 0 auto;
   margin-block-start: 10px;
   margin-inline: 0; /* make sure we're correctly centered by overriding button rule */
 }
 
 #moreFromMozillaCategory .advanced .qr-code-button {
   margin-inline: 16px;
   padding-block-start: 30px;
@@ -1428,16 +1427,12 @@ richlistitem .text-link:hover {
     background-color: rgba(91,91,102,0.45);
   }
 
   .advanced .qr-code-box-image {
     background-color: white;
     fill: black;
   }
 
-  #moreFromMozillaCategory .qr-code-button {
-    color: #fbfbfe;
-  }
-
   .qr-code-box-title {
     color: #fbfbfe;
   }
 }
--- a/devtools/client/debugger/test/mochitest/browser_dbg-windowless-service-workers.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg-windowless-service-workers.js
@@ -116,19 +116,24 @@ add_task(async function() {
   await removeTab(firstTab);
   await removeTab(secondTab);
 
   // Reset the SJS in case we will be repeating the test.
   await addTab(EXAMPLE_URL + "service-worker.sjs?setStatus=");
   await removeTab(gBrowser.selectedTab);
 });
 
+
 // Test setting breakpoints while the service worker is starting up.
 add_task(async function() {
   info("Subtest #4");
+  if (Services.appinfo.fissionAutostart) {
+    // Disabled when serviceworker isolation is used due to bug 1749341
+    return;
+  }
 
   const toolbox = await openNewTabAndToolbox(
     EXAMPLE_URL + "doc-service-workers.html",
     "jsdebugger"
   );
   const dbg = createDebuggerContext(toolbox);
 
   invokeInTab("registerWorker");
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -2112,25 +2112,16 @@ class nsContentUtils {
   static bool OfflineAppAllowed(nsIURI* aURI);
 
   /**
    * Check whether an application should be allowed to use offline APIs.
    */
   static bool OfflineAppAllowed(nsIPrincipal* aPrincipal);
 
   /**
-   * Determine whether the principal or document is allowed access to the
-   * localization system. We don't want the web to ever see this but all our UI
-   * including in content pages should pass this test.  aDocumentURI may be
-   * null.
-   */
-  static bool PrincipalAllowsL10n(nsIPrincipal& aPrincipal,
-                                  nsIURI* aDocumentURI);
-
-  /**
    * Increases the count of blockers preventing scripts from running.
    * NOTE: You might want to use nsAutoScriptBlocker rather than calling
    * this directly
    */
   static void AddScriptBlocker();
 
   /**
    * Decreases the count of blockers preventing scripts from running.
--- a/dom/base/nsMimeTypeArray.h
+++ b/dom/base/nsMimeTypeArray.h
@@ -11,58 +11,44 @@
 #include "nsCOMPtr.h"
 #include "nsPIDOMWindow.h"
 #include "nsTArray.h"
 #include "mozilla/dom/BindingDeclarations.h"
 
 class nsMimeType;
 class nsPluginElement;
 
-namespace mozilla::dom {
-enum class CallerType : uint32_t;
-}  // namespace mozilla::dom
-
 /**
  * Array class backing HTML's navigator.mimeTypes.  This array is always empty.
  */
 class nsMimeTypeArray final : public nsISupports, public nsWrapperCache {
  public:
   explicit nsMimeTypeArray(nsPIDOMWindowInner* aWindow);
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsMimeTypeArray)
 
   nsPIDOMWindowInner* GetParentObject() const;
   virtual JSObject* WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aGivenProto) override;
 
   // MimeTypeArray WebIDL methods
-  nsMimeType* Item(uint32_t index, mozilla::dom::CallerType aCallerType) {
-    return nullptr;
-  }
+  nsMimeType* Item(uint32_t index) { return nullptr; }
+
+  nsMimeType* NamedItem(const nsAString& name) { return nullptr; }
 
-  nsMimeType* NamedItem(const nsAString& name,
-                        mozilla::dom::CallerType aCallerType) {
+  nsMimeType* IndexedGetter(uint32_t index, bool& found) { return nullptr; }
+
+  nsMimeType* NamedGetter(const nsAString& name, bool& found) {
     return nullptr;
   }
 
-  nsMimeType* IndexedGetter(uint32_t index, bool& found,
-                            mozilla::dom::CallerType aCallerType) {
-    return nullptr;
-  }
+  uint32_t Length() { return 0; }
 
-  nsMimeType* NamedGetter(const nsAString& name, bool& found,
-                          mozilla::dom::CallerType aCallerType) {
-    return nullptr;
-  }
-
-  uint32_t Length(mozilla::dom::CallerType aCallerType) { return 0; }
-
-  void GetSupportedNames(nsTArray<nsString>& retval,
-                         mozilla::dom::CallerType aCallerType) {}
+  void GetSupportedNames(nsTArray<nsString>& retval) {}
 
  protected:
   virtual ~nsMimeTypeArray();
 
   nsCOMPtr<nsPIDOMWindowInner> mWindow;
 };
 
 /**
--- a/dom/canvas/ClientWebGLContext.cpp
+++ b/dom/canvas/ClientWebGLContext.cpp
@@ -3857,19 +3857,25 @@ static std::string ToString(const js::Sc
     _(Int8)
     _(Uint8)
     _(Uint8Clamped)
     _(Int16)
     _(Uint16)
     _(Int32)
     _(Uint32)
     _(Float32)
+    _(Float64)
+    _(Int64)
+    _(BigInt64)
+    _(BigUint64)
+    _(Simd128)
 #undef _
-    default:
-      break;
+    case js::Scalar::Type::MaxTypedArrayViewType:
+      // -_-
+      MOZ_CRASH("MaxTypedArrayViewType");
   }
   MOZ_ASSERT(false);
   return std::string("#") + std::to_string(UnderlyingValue(type));
 }
 
 /////////////////////////////////////////////////
 
 static inline uvec2 CastUvec2(const ivec2& val) {
--- a/dom/media/gtest/MockCubeb.cpp
+++ b/dom/media/gtest/MockCubeb.cpp
@@ -330,47 +330,53 @@ MockCubeb::~MockCubeb() { MOZ_ASSERT(!mF
 
 cubeb* MockCubeb::AsCubebContext() { return reinterpret_cast<cubeb*>(this); }
 
 MockCubeb* MockCubeb::AsMock(cubeb* aContext) {
   return reinterpret_cast<MockCubeb*>(aContext);
 }
 
 int MockCubeb::EnumerateDevices(cubeb_device_type aType,
-                                cubeb_device_collection* collection) {
+                                cubeb_device_collection* aCollection) {
 #ifdef ANDROID
   EXPECT_TRUE(false) << "This is not to be called on Android.";
 #endif
   size_t count = 0;
   if (aType & CUBEB_DEVICE_TYPE_INPUT) {
     count += mInputDevices.Length();
   }
   if (aType & CUBEB_DEVICE_TYPE_OUTPUT) {
     count += mOutputDevices.Length();
   }
-  collection->device = new cubeb_device_info[count];
-  collection->count = count;
+  aCollection->device = new cubeb_device_info[count];
+  aCollection->count = count;
 
   uint32_t collection_index = 0;
   if (aType & CUBEB_DEVICE_TYPE_INPUT) {
     for (auto& device : mInputDevices) {
-      collection->device[collection_index] = device;
+      aCollection->device[collection_index] = device;
       collection_index++;
     }
   }
   if (aType & CUBEB_DEVICE_TYPE_OUTPUT) {
     for (auto& device : mOutputDevices) {
-      collection->device[collection_index] = device;
+      aCollection->device[collection_index] = device;
       collection_index++;
     }
   }
 
   return CUBEB_OK;
 }
 
+int MockCubeb::DestroyDeviceCollection(cubeb_device_collection* aCollection) {
+  delete[] aCollection->device;
+  aCollection->count = 0;
+  return CUBEB_OK;
+}
+
 int MockCubeb::RegisterDeviceCollectionChangeCallback(
     cubeb_device_type aDevType,
     cubeb_device_collection_changed_callback aCallback, void* aUserPtr) {
   if (!mSupportsDeviceCollectionChangedCallback) {
     return CUBEB_ERROR;
   }
 
   if (aDevType & CUBEB_DEVICE_TYPE_INPUT) {
--- a/dom/media/gtest/MockCubeb.h
+++ b/dom/media/gtest/MockCubeb.h
@@ -250,17 +250,19 @@ class MockCubeb {
   MockCubeb();
   ~MockCubeb();
   // Cubeb backend implementation
   // This allows passing this class as a cubeb* instance.
   cubeb* AsCubebContext();
   static MockCubeb* AsMock(cubeb* aContext);
   // Fill in the collection parameter with all devices of aType.
   int EnumerateDevices(cubeb_device_type aType,
-                       cubeb_device_collection* collection);
+                       cubeb_device_collection* aCollection);
+  // Clear the collection parameter and deallocate its related memory space.
+  int DestroyDeviceCollection(cubeb_device_collection* aCollection);
 
   // For a given device type, add a callback, called with a user pointer, when
   // the device collection for this backend changes (i.e. a device has been
   // removed or added).
   int RegisterDeviceCollectionChangeCallback(
       cubeb_device_type aDevType,
       cubeb_device_collection_changed_callback aCallback, void* aUserPtr);
 
@@ -386,18 +388,17 @@ class MockCubeb {
 
 int cubeb_mock_enumerate_devices(cubeb* context, cubeb_device_type type,
                                  cubeb_device_collection* out) {
   return MockCubeb::AsMock(context)->EnumerateDevices(type, out);
 }
 
 int cubeb_mock_device_collection_destroy(cubeb* context,
                                          cubeb_device_collection* collection) {
-  delete[] collection->device;
-  return CUBEB_OK;
+  return MockCubeb::AsMock(context)->DestroyDeviceCollection(collection);
 }
 
 int cubeb_mock_register_device_collection_changed(
     cubeb* context, cubeb_device_type devtype,
     cubeb_device_collection_changed_callback callback, void* user_ptr) {
   return MockCubeb::AsMock(context)->RegisterDeviceCollectionChangeCallback(
       devtype, callback, user_ptr);
 }
--- a/dom/media/gtest/TestAudioDeviceEnumerator.cpp
+++ b/dom/media/gtest/TestAudioDeviceEnumerator.cpp
@@ -107,16 +107,79 @@ TEST(CubebDeviceEnumerator, EnumerateSim
       AddDevices(mock, device_count, deviceType);
       TestEnumeration(mock, device_count, op, deviceType);
     }
   }
   // Shutdown to clean up the last `supports` effect
   CubebDeviceEnumerator::Shutdown();
 }
 
+TEST(CubebDeviceEnumerator, ZeroChannelDevices)
+{
+  MockCubeb* mock = new MockCubeb();
+  mozilla::CubebUtils::ForceSetCubebContext(mock->AsCubebContext());
+
+  // Create devices with different channel count, including 0-channel
+
+  cubeb_device_info dev1 = DeviceTemplate(reinterpret_cast<cubeb_devid>(1),
+                                          CUBEB_DEVICE_TYPE_INPUT, "dev 1");
+  dev1.max_channels = 1;
+  mock->AddDevice(dev1);
+
+  cubeb_device_info dev2 = DeviceTemplate(reinterpret_cast<cubeb_devid>(2),
+                                          CUBEB_DEVICE_TYPE_INPUT, "dev 2");
+  dev2.max_channels = 0;
+  mock->AddDevice(dev2);
+
+  cubeb_device_info dev3 = DeviceTemplate(reinterpret_cast<cubeb_devid>(3),
+                                          CUBEB_DEVICE_TYPE_OUTPUT, "dev 3");
+  dev3.max_channels = 2;
+  mock->AddDevice(dev3);
+
+  cubeb_device_info dev4 = DeviceTemplate(reinterpret_cast<cubeb_devid>(4),
+                                          CUBEB_DEVICE_TYPE_OUTPUT, "dev 4");
+  dev4.max_channels = 0;
+  mock->AddDevice(dev4);
+
+  // Make sure the devices are added to cubeb.
+
+  cubeb_device_collection inputCollection = {nullptr, 0};
+  mock->EnumerateDevices(CUBEB_DEVICE_TYPE_INPUT, &inputCollection);
+  EXPECT_EQ(inputCollection.count, 2U);
+  EXPECT_EQ(inputCollection.device[0].devid, dev1.devid);
+  EXPECT_EQ(inputCollection.device[1].devid, dev2.devid);
+  mock->DestroyDeviceCollection(&inputCollection);
+  EXPECT_EQ(inputCollection.count, 0U);
+
+  cubeb_device_collection outputCollection = {nullptr, 0};
+  mock->EnumerateDevices(CUBEB_DEVICE_TYPE_OUTPUT, &outputCollection);
+  EXPECT_EQ(outputCollection.count, 2U);
+  EXPECT_EQ(outputCollection.device[0].devid, dev3.devid);
+  EXPECT_EQ(outputCollection.device[1].devid, dev4.devid);
+  mock->DestroyDeviceCollection(&outputCollection);
+  EXPECT_EQ(outputCollection.count, 0U);
+
+  // Enumerate the devices. The result should exclude the 0-channel devices.
+
+  RefPtr<CubebDeviceEnumerator> enumerator =
+      CubebDeviceEnumerator::GetInstance();
+
+  RefPtr<const AudioDeviceSet> inputDevices =
+      enumerator->EnumerateAudioInputDevices();
+  EXPECT_EQ(inputDevices->Length(), 1U);
+  EXPECT_EQ(inputDevices->ElementAt(0)->DeviceID(), dev1.devid);
+  EXPECT_EQ(inputDevices->ElementAt(0)->MaxChannels(), dev1.max_channels);
+
+  RefPtr<const AudioDeviceSet> outputDevices =
+      enumerator->EnumerateAudioOutputDevices();
+  EXPECT_EQ(outputDevices->Length(), 1U);
+  EXPECT_EQ(outputDevices->ElementAt(0)->DeviceID(), dev3.devid);
+  EXPECT_EQ(outputDevices->ElementAt(0)->MaxChannels(), dev3.max_channels);
+}
+
 #else  // building for Android, which has no device enumeration support
 TEST(CubebDeviceEnumerator, EnumerateAndroid)
 {
   MockCubeb* mock = new MockCubeb();
   mozilla::CubebUtils::ForceSetCubebContext(mock->AsCubebContext());
 
   RefPtr<CubebDeviceEnumerator> enumerator =
       CubebDeviceEnumerator::GetInstance();
--- a/dom/media/platforms/ffmpeg/FFmpegLibWrapper.cpp
+++ b/dom/media/platforms/ffmpeg/FFmpegLibWrapper.cpp
@@ -137,35 +137,45 @@ FFmpegLibWrapper::LinkResult FFmpegLibWr
   AV_FUNC(avcodec_find_decoder, AV_FUNC_AVCODEC_ALL)
   AV_FUNC(avcodec_flush_buffers, AV_FUNC_AVCODEC_ALL)
   AV_FUNC(avcodec_open2, AV_FUNC_AVCODEC_ALL)
   AV_FUNC(avcodec_register_all, AV_FUNC_AVCODEC_ALL)
   AV_FUNC(av_init_packet, AV_FUNC_AVCODEC_ALL)
   AV_FUNC(av_parser_init, AV_FUNC_AVCODEC_ALL)
   AV_FUNC(av_parser_close, AV_FUNC_AVCODEC_ALL)
   AV_FUNC(av_parser_parse2, AV_FUNC_AVCODEC_ALL)
+  AV_FUNC(avcodec_align_dimensions, AV_FUNC_AVCODEC_ALL)
   AV_FUNC(avcodec_alloc_frame, (AV_FUNC_53 | AV_FUNC_54))
   AV_FUNC(avcodec_get_frame_defaults, (AV_FUNC_53 | AV_FUNC_54))
   AV_FUNC(avcodec_free_frame, AV_FUNC_54)
   AV_FUNC(avcodec_send_packet, AV_FUNC_58)
   AV_FUNC(avcodec_receive_frame, AV_FUNC_58)
+  AV_FUNC(avcodec_default_get_buffer2,
+          (AV_FUNC_55 | AV_FUNC_56 | AV_FUNC_57 | AV_FUNC_58))
   AV_FUNC_OPTION(av_rdft_init, AV_FUNC_AVCODEC_ALL)
   AV_FUNC_OPTION(av_rdft_calc, AV_FUNC_AVCODEC_ALL)
   AV_FUNC_OPTION(av_rdft_end, AV_FUNC_AVCODEC_ALL)
   AV_FUNC(av_log_set_level, AV_FUNC_AVUTIL_ALL)
   AV_FUNC(av_malloc, AV_FUNC_AVUTIL_ALL)
   AV_FUNC(av_freep, AV_FUNC_AVUTIL_ALL)
   AV_FUNC(av_frame_alloc, (AV_FUNC_AVUTIL_55 | AV_FUNC_AVUTIL_56 |
                            AV_FUNC_AVUTIL_57 | AV_FUNC_AVUTIL_58))
   AV_FUNC(av_frame_free, (AV_FUNC_AVUTIL_55 | AV_FUNC_AVUTIL_56 |
                           AV_FUNC_AVUTIL_57 | AV_FUNC_AVUTIL_58))
   AV_FUNC(av_frame_unref, (AV_FUNC_AVUTIL_55 | AV_FUNC_AVUTIL_56 |
                            AV_FUNC_AVUTIL_57 | AV_FUNC_AVUTIL_58))
+  AV_FUNC(av_image_check_size, AV_FUNC_AVUTIL_ALL)
+  AV_FUNC(av_image_get_buffer_size, AV_FUNC_AVUTIL_ALL)
+  AV_FUNC_OPTION(av_buffer_get_opaque,
+                 (AV_FUNC_AVUTIL_56 | AV_FUNC_AVUTIL_57 | AV_FUNC_AVUTIL_58))
+  AV_FUNC(av_buffer_create, (AV_FUNC_AVUTIL_55 | AV_FUNC_AVUTIL_56 |
+                             AV_FUNC_AVUTIL_57 | AV_FUNC_AVUTIL_58))
   AV_FUNC_OPTION(av_frame_get_colorspace, AV_FUNC_AVUTIL_ALL)
   AV_FUNC_OPTION(av_frame_get_color_range, AV_FUNC_AVUTIL_ALL)
+
 #ifdef MOZ_WAYLAND
   AV_FUNC_OPTION_SILENT(avcodec_get_hw_config, AV_FUNC_58)
   AV_FUNC_OPTION_SILENT(av_codec_iterate, AV_FUNC_58)
   AV_FUNC_OPTION_SILENT(av_codec_is_decoder, AV_FUNC_58)
   AV_FUNC_OPTION_SILENT(av_hwdevice_ctx_init, AV_FUNC_58)
   AV_FUNC_OPTION_SILENT(av_hwdevice_ctx_alloc, AV_FUNC_58)
   AV_FUNC_OPTION_SILENT(av_hwdevice_hwconfig_alloc, AV_FUNC_58)
   AV_FUNC_OPTION_SILENT(av_hwdevice_get_hwframe_constraints, AV_FUNC_58)
--- a/dom/media/platforms/ffmpeg/FFmpegLibWrapper.h
+++ b/dom/media/platforms/ffmpeg/FFmpegLibWrapper.h
@@ -13,20 +13,20 @@ struct AVCodec;
 struct AVCodecContext;
 struct AVFrame;
 struct AVPacket;
 struct AVDictionary;
 struct AVCodecParserContext;
 struct PRLibrary;
 #ifdef MOZ_WAYLAND
 struct AVCodecHWConfig;
-struct AVBufferRef;
 struct AVVAAPIHWConfig;
 struct AVHWFramesConstraints;
 #endif
+struct AVBufferRef;
 
 namespace mozilla {
 
 struct MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS FFmpegLibWrapper {
   // The class is used only in static storage and so is zero initialized.
   FFmpegLibWrapper() = default;
   // The libraries are not unloaded in the destructor, because doing so would
   // require a static constructor to register the static destructor.  As the
@@ -83,41 +83,57 @@ struct MOZ_ONLY_USED_TO_AVOID_STATIC_CON
   AVCodecParserContext* (*av_parser_init)(int codec_id);
   void (*av_parser_close)(AVCodecParserContext* s);
   int (*av_parser_parse2)(AVCodecParserContext* s, AVCodecContext* avctx,
                           uint8_t** poutbuf, int* poutbuf_size,
                           const uint8_t* buf, int buf_size, int64_t pts,
                           int64_t dts, int64_t pos);
   AVCodec* (*av_codec_iterate)(void** opaque);
   int (*av_codec_is_decoder)(const AVCodec* codec);
+  void (*avcodec_align_dimensions)(AVCodecContext* s, int* width, int* height);
 
   // only used in libavcodec <= 54
   AVFrame* (*avcodec_alloc_frame)();
   void (*avcodec_get_frame_defaults)(AVFrame* pic);
+
   // libavcodec v54 only
   void (*avcodec_free_frame)(AVFrame** frame);
 
+  // libavcodec >= v55
+  int (*avcodec_default_get_buffer2)(AVCodecContext* s, AVFrame* frame,
+                                     int flags);
+
   // libavcodec v58 and later only
   int (*avcodec_send_packet)(AVCodecContext* avctx, const AVPacket* avpkt);
   int (*avcodec_receive_frame)(AVCodecContext* avctx, AVFrame* frame);
 
   // libavcodec optional
   AvRdftInitFn av_rdft_init;
   AvRdftCalcFn av_rdft_calc;
   AvRdftEndFn av_rdft_end;
 
   // libavutil
   void (*av_log_set_level)(int level);
   void* (*av_malloc)(size_t size);
   void (*av_freep)(void* ptr);
+  int (*av_image_check_size)(unsigned int w, unsigned int h, int log_offset,
+                             void* log_ctx);
+  int (*av_image_get_buffer_size)(int pix_fmt, int width, int height,
+                                  int align);
 
   // libavutil v55 and later only
   AVFrame* (*av_frame_alloc)();
   void (*av_frame_free)(AVFrame** frame);
   void (*av_frame_unref)(AVFrame* frame);
+  AVBufferRef* (*av_buffer_create)(uint8_t* data, int size,
+                                   void (*free)(void* opaque, uint8_t* data),
+                                   void* opaque, int flags);
+
+  // libavutil >= v56
+  void* (*av_buffer_get_opaque)(const AVBufferRef* buf);
 
   // libavutil optional
   int (*av_frame_get_colorspace)(const AVFrame* frame);
   int (*av_frame_get_color_range)(const AVFrame* frame);
 
 #ifdef MOZ_WAYLAND
   const AVCodecHWConfig* (*avcodec_get_hw_config)(const AVCodec* codec,
                                                   int index);
--- a/dom/media/platforms/ffmpeg/FFmpegLog.h
+++ b/dom/media/platforms/ffmpeg/FFmpegLog.h
@@ -12,9 +12,12 @@
 #ifdef FFVPX_VERSION
 #  define FFMPEG_LOG(str, ...) \
     MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, ("FFVPX: " str, ##__VA_ARGS__))
 #else
 #  define FFMPEG_LOG(str, ...) \
     MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, ("FFMPEG: " str, ##__VA_ARGS__))
 #endif
 
+#define FFMPEG_LOGV(...) \
+  MOZ_LOG(sPDMLog, mozilla::LogLevel::Verbose, (__VA_ARGS__))
+
 #endif  // __FFmpegLog_h__
--- a/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.cpp
+++ b/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.cpp
@@ -11,16 +11,19 @@
 #include "MP4Decoder.h"
 #include "MediaInfo.h"
 #include "VideoUtils.h"
 #include "VPXDecoder.h"
 #include "mozilla/layers/KnowsCompositor.h"
 #if defined(MOZ_AV1) && defined(FFVPX_VERSION) && defined(MOZ_WAYLAND)
 #  include "AOMDecoder.h"
 #endif
+#if LIBAVCODEC_VERSION_MAJOR >= 57
+#  include "mozilla/layers/TextureClient.h"
+#endif
 #ifdef MOZ_WAYLAND_USE_VAAPI
 #  include "H264.h"
 #  include "mozilla/layers/DMABUFSurfaceImage.h"
 #  include "mozilla/widget/DMABufLibWrapper.h"
 #  include "FFmpegVideoFramePool.h"
 #  include "va/va.h"
 #endif
 
@@ -32,33 +35,36 @@
 #  define AV_PIX_FMT_YUV420P10LE PIX_FMT_YUV420P10LE
 #  define AV_PIX_FMT_YUV422P PIX_FMT_YUV422P
 #  define AV_PIX_FMT_YUV422P10LE PIX_FMT_YUV422P10LE
 #  define AV_PIX_FMT_YUV444P PIX_FMT_YUV444P
 #  define AV_PIX_FMT_YUV444P10LE PIX_FMT_YUV444P10LE
 #  define AV_PIX_FMT_NONE PIX_FMT_NONE
 #endif
 #include "mozilla/PodOperations.h"
-#include "mozilla/ScopeExit.h"
 #include "mozilla/StaticPrefs_media.h"
 #include "mozilla/TaskQueue.h"
 #include "nsThreadUtils.h"
 #include "prsystem.h"
 
 // Forward declare from va.h
 #ifdef MOZ_WAYLAND_USE_VAAPI
 typedef int VAStatus;
 #  define VA_EXPORT_SURFACE_READ_ONLY 0x0001
 #  define VA_EXPORT_SURFACE_SEPARATE_LAYERS 0x0004
 #  define VA_STATUS_SUCCESS 0x00000000
 #endif
 
 // Use some extra HW frames for potential rendering lags.
 #define EXTRA_HW_FRAMES 6
 
+#if LIBAVCODEC_VERSION_MAJOR >= 57 && LIBAVUTIL_VERSION_MAJOR >= 56
+#  define CUSTOMIZED_BUFFER_ALLOCATION 1
+#endif
+
 typedef mozilla::layers::Image Image;
 typedef mozilla::layers::PlanarYCbCrImage PlanarYCbCrImage;
 
 namespace mozilla {
 
 #ifdef MOZ_WAYLAND_USE_VAAPI
 nsTArray<AVCodecID> FFmpegVideoDecoder<LIBAV_VER>::mAcceleratedFormats;
 #endif
@@ -400,16 +406,23 @@ FFmpegVideoDecoder<LIBAV_VER>::FFmpegVid
 #ifdef MOZ_WAYLAND_USE_VAAPI
   InitHWDecodingPrefs();
   if (mUseDMABufSurfaces || mEnableHardwareDecoding) {
     mVideoFramePool = MakeUnique<VideoFramePool>(mEnableHardwareDecoding);
   }
 #endif
 }
 
+FFmpegVideoDecoder<LIBAV_VER>::~FFmpegVideoDecoder() {
+#ifdef CUSTOMIZED_BUFFER_ALLOCATION
+  MOZ_DIAGNOSTIC_ASSERT(mAllocatedImages.IsEmpty(),
+                        "Should release all shmem buffers before destroy!");
+#endif
+}
+
 RefPtr<MediaDataDecoder::InitPromise> FFmpegVideoDecoder<LIBAV_VER>::Init() {
   MediaResult rv;
 
 #ifdef MOZ_WAYLAND_USE_VAAPI
   if (mEnableHardwareDecoding) {
     rv = InitVAAPIDecoder();
     if (NS_SUCCEEDED(rv)) {
       return InitPromise::CreateAndResolve(TrackInfo::kVideoTrack, __func__);
@@ -421,16 +434,314 @@ RefPtr<MediaDataDecoder::InitPromise> FF
   rv = InitDecoder();
   if (NS_SUCCEEDED(rv)) {
     return InitPromise::CreateAndResolve(TrackInfo::kVideoTrack, __func__);
   }
 
   return InitPromise::CreateAndReject(rv, __func__);
 }
 
+#ifdef CUSTOMIZED_BUFFER_ALLOCATION
+static int GetVideoBufferWrapper(struct AVCodecContext* aCodecContext,
+                                 AVFrame* aFrame, int aFlags) {
+  auto* decoder =
+      static_cast<FFmpegVideoDecoder<LIBAV_VER>*>(aCodecContext->opaque);
+  int rv = decoder->GetVideoBuffer(aCodecContext, aFrame, aFlags);
+  return rv < 0 ? decoder->GetVideoBufferDefault(aCodecContext, aFrame, aFlags)
+                : rv;
+}
+
+static void ReleaseVideoBufferWrapper(void* opaque, uint8_t* data) {
+  if (opaque) {
+    FFMPEG_LOGV("ReleaseVideoBufferWrapper: PlanarYCbCrImage=%p", opaque);
+    RefPtr<ImageBufferWrapper> image = static_cast<ImageBufferWrapper*>(opaque);
+    image->ReleaseBuffer();
+  }
+}
+
+static gfx::YUVColorSpace TransferAVColorSpaceToYUVColorSpace(
+    AVColorSpace aSpace) {
+  switch (aSpace) {
+    case AVCOL_SPC_BT2020_NCL:
+    case AVCOL_SPC_BT2020_CL:
+      return gfx::YUVColorSpace::BT2020;
+    case AVCOL_SPC_BT709:
+      return gfx::YUVColorSpace::BT709;
+    case AVCOL_SPC_SMPTE170M:
+    case AVCOL_SPC_BT470BG:
+      return gfx::YUVColorSpace::BT601;
+    default:
+      return gfx::YUVColorSpace::Default;
+  }
+}
+
+static bool IsColorFormatSupportedForUsingCustomizedBuffer(
+    const AVPixelFormat& aFormat) {
+#  if XP_WIN
+  // Currently the web render doesn't support uploading R16 surface, so we can't
+  // use the shmem texture for 10 bit+ videos which would be uploaded by the
+  // web render. See Bug 1751498.
+  return aFormat == AV_PIX_FMT_YUV420P || aFormat == AV_PIX_FMT_YUVJ420P ||
+         aFormat == AV_PIX_FMT_YUV444P;
+#  else
+  // For now, we only support for YUV420P, YUVJ420P and YUV444 which are the
+  // only non-HW accelerated format supported by FFmpeg's H264 and VP9 decoder.
+  return aFormat == AV_PIX_FMT_YUV420P || aFormat == AV_PIX_FMT_YUVJ420P ||
+         aFormat == AV_PIX_FMT_YUV420P10LE ||
+         aFormat == AV_PIX_FMT_YUV420P12LE || aFormat == AV_PIX_FMT_YUV444P ||
+         aFormat == AV_PIX_FMT_YUV444P10LE || aFormat == AV_PIX_FMT_YUV444P12LE;
+#  endif
+}
+
+static gfx::ColorDepth GetColorDepth(const AVPixelFormat& aFormat) {
+  switch (aFormat) {
+    case AV_PIX_FMT_YUV420P:
+    case AV_PIX_FMT_YUVJ420P:
+    case AV_PIX_FMT_YUV422P:
+    case AV_PIX_FMT_YUV444P:
+      return gfx::ColorDepth::COLOR_8;
+    case AV_PIX_FMT_YUV420P10LE:
+    case AV_PIX_FMT_YUV422P10LE:
+    case AV_PIX_FMT_YUV444P10LE:
+      return gfx::ColorDepth::COLOR_10;
+    case AV_PIX_FMT_YUV420P12LE:
+    case AV_PIX_FMT_YUV422P12LE:
+    case AV_PIX_FMT_YUV444P12LE:
+      return gfx::ColorDepth::COLOR_12;
+    default:
+      MOZ_ASSERT_UNREACHABLE("Not supported format?");
+      return gfx::ColorDepth::COLOR_8;
+  }
+}
+
+static bool IsYUV420Sampling(const AVPixelFormat& aFormat) {
+  return aFormat == AV_PIX_FMT_YUV420P || aFormat == AV_PIX_FMT_YUVJ420P ||
+         aFormat == AV_PIX_FMT_YUV420P10LE || aFormat == AV_PIX_FMT_YUV420P12LE;
+}
+
+layers::TextureClient*
+FFmpegVideoDecoder<LIBAV_VER>::AllocateTextueClientForImage(
+    struct AVCodecContext* aCodecContext, PlanarYCbCrImage* aImage) {
+  layers::PlanarYCbCrData data =
+      CreateEmptyPlanarYCbCrData(aCodecContext, mInfo);
+  // Allocate a shmem buffer for image.
+  if (!aImage->CreateEmptyBuffer(data)) {
+    return nullptr;
+  }
+  return aImage->GetTextureClient(mImageAllocator);
+}
+
+layers::PlanarYCbCrData
+FFmpegVideoDecoder<LIBAV_VER>::CreateEmptyPlanarYCbCrData(
+    struct AVCodecContext* aCodecContext, const VideoInfo& aInfo) {
+  MOZ_ASSERT(
+      IsColorFormatSupportedForUsingCustomizedBuffer(aCodecContext->pix_fmt));
+
+  // FFmpeg will store images with color depth > 8 bits in 16 bits with extra
+  // padding.
+  const int32_t bytesPerChannel =
+      GetColorDepth(aCodecContext->pix_fmt) == gfx::ColorDepth::COLOR_8 ? 1 : 2;
+
+  // If adjusted Ysize is larger than the actual image size (coded_width *
+  // coded_height), that means ffmpeg decoder needs extra padding on both width
+  // and height. If that happens, the planes will need to be cropped later in
+  // order to avoid visible incorrect border on the right and bottom of the
+  // actual image.
+  //
+  // Here are examples of various sizes video in YUV420P format, the width and
+  // height would need to be adjusted in order to align padding.
+  //
+  // Eg1. video (1920*1080)
+  // plane Y
+  // width 1920 height 1080 -> adjusted-width 1920 adjusted-height 1088
+  // plane Cb/Cr
+  // width 960  height  540 -> adjusted-width 1024 adjusted-height 544
+  //
+  // Eg2. video (2560*1440)
+  // plane Y
+  // width 2560 height 1440 -> adjusted-width 2560 adjusted-height 1440
+  // plane Cb/Cr
+  // width 1280 height  720 -> adjusted-width 1280 adjusted-height 736
+  layers::PlanarYCbCrData data;
+  auto paddedYSize =
+      gfx::IntSize{aCodecContext->coded_width, aCodecContext->coded_height};
+  mLib->avcodec_align_dimensions(aCodecContext, &paddedYSize.width,
+                                 &paddedYSize.height);
+  data.mYSize = gfx::IntSize{paddedYSize.Width(), paddedYSize.Height()};
+  data.mYStride = data.mYSize.Width() * bytesPerChannel;
+  data.mCroppedYSize = Some(
+      gfx::IntSize{aCodecContext->coded_width, aCodecContext->coded_height});
+
+  MOZ_ASSERT(
+      IsColorFormatSupportedForUsingCustomizedBuffer(aCodecContext->pix_fmt));
+  const auto yDims =
+      gfx::IntSize{aCodecContext->coded_width, aCodecContext->coded_height};
+  auto uvDims = yDims;
+  if (IsYUV420Sampling(aCodecContext->pix_fmt)) {
+    uvDims.width = (uvDims.width + 1) / 2;
+    uvDims.height = (uvDims.height + 1) / 2;
+  }
+  auto paddedCbCrSize = uvDims;
+  mLib->avcodec_align_dimensions(aCodecContext, &paddedCbCrSize.width,
+                                 &paddedCbCrSize.height);
+  data.mCbCrSize =
+      gfx::IntSize{paddedCbCrSize.Width(), paddedCbCrSize.Height()};
+  data.mCbCrStride = data.mCbCrSize.Width() * bytesPerChannel;
+  data.mCroppedCbCrSize = Some(gfx::IntSize{uvDims.Width(), uvDims.Height()});
+
+  // Setting other attributes
+  data.mPicSize =
+      gfx::IntSize{aCodecContext->coded_width, aCodecContext->coded_height};
+  const gfx::IntRect picture =
+      aInfo.ScaledImageRect(data.mPicSize.Width(), data.mPicSize.Height());
+  data.mPicX = picture.x;
+  data.mPicY = picture.y;
+  data.mStereoMode = aInfo.mStereoMode;
+  if (aCodecContext->colorspace != AVCOL_SPC_UNSPECIFIED) {
+    data.mYUVColorSpace =
+        TransferAVColorSpaceToYUVColorSpace(aCodecContext->colorspace);
+  } else {
+    data.mYUVColorSpace = aInfo.mColorSpace ? *aInfo.mColorSpace
+                                            : DefaultColorSpace(data.mPicSize);
+  }
+  data.mColorDepth = GetColorDepth(aCodecContext->pix_fmt);
+  data.mColorRange = aCodecContext->color_range == AVCOL_RANGE_JPEG
+                         ? gfx::ColorRange::FULL
+                         : gfx::ColorRange::LIMITED;
+  FFMPEG_LOGV(
+      "Created plane data, YSize=(%d, %d), CbCrSize=(%d, %d), "
+      "CroppedYSize=(%d, %d), CroppedCbCrSize=(%d, %d), ColorDepth=%hhu",
+      data.mYSize.Width(), data.mYSize.Height(), data.mCbCrSize.Width(),
+      data.mCbCrSize.Height(), data.mCroppedYSize->Width(),
+      data.mCroppedYSize->Height(), data.mCroppedCbCrSize->Width(),
+      data.mCroppedCbCrSize->Height(), static_cast<uint8_t>(data.mColorDepth));
+  return data;
+}
+
+int FFmpegVideoDecoder<LIBAV_VER>::GetVideoBuffer(
+    struct AVCodecContext* aCodecContext, AVFrame* aFrame, int aFlags) {
+  FFMPEG_LOGV("GetVideoBuffer: aCodecContext=%p aFrame=%p", aCodecContext,
+              aFrame);
+  if (!StaticPrefs::media_ffmpeg_customized_buffer_allocation()) {
+    return AVERROR(EINVAL);
+  }
+
+  if (mIsUsingShmemBufferForDecode && !*mIsUsingShmemBufferForDecode) {
+    return AVERROR(EINVAL);
+  }
+
+  // Codec doesn't support custom allocator.
+  if (!(aCodecContext->codec->capabilities & AV_CODEC_CAP_DR1)) {
+    return AVERROR(EINVAL);
+  }
+
+  // Pre-allocation is only for sw decoding. During decoding, ffmpeg decoder
+  // will need to reference decoded frames, if those frames are on shmem buffer,
+  // then it would cause a need to read CPU data from GPU, which is slow.
+  if (IsHardwareAccelerated()) {
+    return AVERROR(EINVAL);
+  }
+
+#  ifdef MOZ_WAYLAND_USE_VAAPI
+  // For SW decoding + DMABuf case, it's the opposite from the above case, we
+  // don't want to access GPU data too frequently from CPU.
+  if (mUseDMABufSurfaces) {
+    return AVERROR(EINVAL);
+  }
+#  endif
+
+  if (!IsColorFormatSupportedForUsingCustomizedBuffer(aCodecContext->pix_fmt)) {
+    FFMPEG_LOG("Not support color format %d", aCodecContext->pix_fmt);
+    return AVERROR(EINVAL);
+  }
+
+  if (aCodecContext->lowres != 0) {
+    FFMPEG_LOG("Not support low resolution decoding");
+    return AVERROR(EINVAL);
+  }
+
+  const gfx::IntSize size(aCodecContext->width, aCodecContext->height);
+  int rv = mLib->av_image_check_size(size.Width(), size.Height(), 0, nullptr);
+  if (rv < 0) {
+    FFMPEG_LOG("Invalid image size");
+    return rv;
+  }
+
+  CheckedInt32 dataSize = mLib->av_image_get_buffer_size(
+      aCodecContext->pix_fmt, aCodecContext->coded_width,
+      aCodecContext->coded_height, 16);
+  if (!dataSize.isValid()) {
+    FFMPEG_LOG("Data size overflow!");
+    return AVERROR(EINVAL);
+  }
+
+  if (!mImageContainer) {
+    FFMPEG_LOG("No Image container!");
+    return AVERROR(EINVAL);
+  }
+
+  RefPtr<PlanarYCbCrImage> image = mImageContainer->CreatePlanarYCbCrImage();
+  if (!image) {
+    FFMPEG_LOG("Failed to create YCbCr image");
+    return AVERROR(EINVAL);
+  }
+
+  RefPtr<layers::TextureClient> texture =
+      AllocateTextueClientForImage(aCodecContext, image);
+  if (!texture) {
+    FFMPEG_LOG("Failed to allocate a texture client");
+    return AVERROR(EINVAL);
+  }
+
+  if (!texture->Lock(layers::OpenMode::OPEN_WRITE)) {
+    FFMPEG_LOG("Failed to lock the texture");
+    return AVERROR(EINVAL);
+  }
+
+  layers::MappedYCbCrTextureData mapped;
+  if (!texture->BorrowMappedYCbCrData(mapped)) {
+    FFMPEG_LOG("Failed to borrow mapped data for the texture");
+    texture->Unlock();
+    return AVERROR(EINVAL);
+  }
+
+  aFrame->data[0] = mapped.y.data;
+  aFrame->data[1] = mapped.cb.data;
+  aFrame->data[2] = mapped.cr.data;
+
+  aFrame->linesize[0] = mapped.y.stride;
+  aFrame->linesize[1] = mapped.cb.stride;
+  aFrame->linesize[2] = mapped.cr.stride;
+
+  aFrame->width = aCodecContext->coded_width;
+  aFrame->height = aCodecContext->coded_height;
+  aFrame->format = aCodecContext->pix_fmt;
+  aFrame->extended_data = aFrame->data;
+  aFrame->reordered_opaque = aCodecContext->reordered_opaque;
+  MOZ_ASSERT(aFrame->data[0] && aFrame->data[1] && aFrame->data[2]);
+
+  // This will hold a reference to image, and the reference would be dropped
+  // when ffmpeg tells us that the buffer is no longer needed.
+  auto imageWrapper = MakeRefPtr<ImageBufferWrapper>(image.get(), this);
+  aFrame->buf[0] =
+      mLib->av_buffer_create(aFrame->data[0], dataSize.value(),
+                             ReleaseVideoBufferWrapper, imageWrapper.get(), 0);
+  if (!aFrame->buf[0]) {
+    FFMPEG_LOG("Failed to allocate buffer");
+    return AVERROR(EINVAL);
+  }
+
+  FFMPEG_LOG("Created av buffer, buf=%p, data=%p, image=%p, sz=%d",
+             aFrame->buf[0], aFrame->data[0], image.get(), dataSize.value());
+  mAllocatedImages.Insert(imageWrapper.get());
+  mIsUsingShmemBufferForDecode = Some(true);
+  return 0;
+}
+#endif
+
 void FFmpegVideoDecoder<LIBAV_VER>::InitCodecContext() {
   mCodecContext->width = mInfo.mImage.width;
   mCodecContext->height = mInfo.mImage.height;
 
   // We use the same logic as libvpx in determining the number of threads to use
   // so that we end up behaving in the same fashion when using ffmpeg as
   // we would otherwise cause various crashes (see bug 1236167)
   int decode_threads = 1;
@@ -453,16 +764,24 @@ void FFmpegVideoDecoder<LIBAV_VER>::Init
     mCodecContext->thread_count = decode_threads;
     if (decode_threads > 1) {
       mCodecContext->thread_type = FF_THREAD_SLICE | FF_THREAD_FRAME;
     }
   }
 
   // FFmpeg will call back to this to negotiate a video pixel format.
   mCodecContext->get_format = ChoosePixelFormat;
+#ifdef CUSTOMIZED_BUFFER_ALLOCATION
+  FFMPEG_LOG("Set get_buffer2 for customized buffer allocation");
+  mCodecContext->get_buffer2 = GetVideoBufferWrapper;
+  mCodecContext->opaque = this;
+#  if FF_API_THREAD_SAFE_CALLBACKS
+  mCodecContext->thread_safe_callbacks = 1;
+#  endif
+#endif
 }
 
 #ifdef MOZ_WAYLAND_USE_VAAPI
 void FFmpegVideoDecoder<LIBAV_VER>::InitVAAPICodecContext() {
   mCodecContext->width = mInfo.mImage.width;
   mCodecContext->height = mInfo.mImage.height;
   mCodecContext->thread_count = 1;
   mCodecContext->get_format = ChooseVAAPIPixelFormat;
@@ -504,17 +823,17 @@ MediaResult FFmpegVideoDecoder<LIBAV_VER
                        RESULT_DETAIL("avcodec_send_packet error: %d", res));
   }
 
   if (aGotFrame) {
     *aGotFrame = false;
   }
   do {
     if (!PrepareFrame()) {
-      NS_WARNING("FFmpeg h264 decoder failed to allocate frame.");
+      NS_WARNING("FFmpeg decoder failed to allocate frame.");
       return MediaResult(NS_ERROR_OUT_OF_MEMORY, __func__);
     }
 
 #  ifdef MOZ_WAYLAND_USE_VAAPI
     // Create VideoFramePool in case we need it.
     if (!mVideoFramePool && (mUseDMABufSurfaces || mEnableHardwareDecoding)) {
       mVideoFramePool = MakeUnique<VideoFramePool>(mEnableHardwareDecoding);
     }
@@ -537,17 +856,17 @@ MediaResult FFmpegVideoDecoder<LIBAV_VER
     if (res < 0) {
       FFMPEG_LOG("  avcodec_receive_frame error: %d", res);
       return MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR,
                          RESULT_DETAIL("avcodec_receive_frame error: %d", res));
     }
 
     MediaResult rv;
 #  ifdef MOZ_WAYLAND_USE_VAAPI
-    if (mVAAPIDeviceContext) {
+    if (IsHardwareAccelerated()) {
       rv = CreateImageVAAPI(mFrame->pkt_pos, mFrame->pkt_pts,
                             mFrame->pkt_duration, aResults);
       // If VA-API playback failed, just quit. Decoder is going to be restarted
       // without VA-API.
       if (NS_FAILED(rv)) {
         return rv;
       }
     } else if (mUseDMABufSurfaces) {
@@ -576,17 +895,17 @@ MediaResult FFmpegVideoDecoder<LIBAV_VER
   // (FFmpeg >= 1.0 provides av_frame_get_pkt_duration)
   // As such we instead use a map using the dts as key that we will retrieve
   // later.
   // The map will have a typical size of 16 entry.
   mDurationMap.Insert(aSample->mTimecode.ToMicroseconds(),
                       aSample->mDuration.ToMicroseconds());
 
   if (!PrepareFrame()) {
-    NS_WARNING("FFmpeg h264 decoder failed to allocate frame.");
+    NS_WARNING("FFmpeg decoder failed to allocate frame.");
     return MediaResult(NS_ERROR_OUT_OF_MEMORY, __func__);
   }
 
   // Required with old version of FFmpeg/LibAV
   mFrame->reordered_opaque = AV_NOPTS_VALUE;
 
   int decoded;
   int bytesConsumed =
@@ -730,21 +1049,45 @@ MediaResult FFmpegVideoDecoder<LIBAV_VER
   b.mYUVColorSpace = GetFrameColorSpace();
 
   if (mLib->av_frame_get_color_range) {
     auto range = mLib->av_frame_get_color_range(mFrame);
     b.mColorRange = range == AVCOL_RANGE_JPEG ? gfx::ColorRange::FULL
                                               : gfx::ColorRange::LIMITED;
   }
 
-  RefPtr<VideoData> v = VideoData::CreateAndCopyData(
-      mInfo, mImageContainer, aOffset, TimeUnit::FromMicroseconds(aPts),
-      TimeUnit::FromMicroseconds(aDuration), b, !!mFrame->key_frame,
-      TimeUnit::FromMicroseconds(-1),
-      mInfo.ScaledImageRect(mFrame->width, mFrame->height), mImageAllocator);
+  RefPtr<VideoData> v;
+#ifdef CUSTOMIZED_BUFFER_ALLOCATION
+  if (mIsUsingShmemBufferForDecode && *mIsUsingShmemBufferForDecode) {
+    RefPtr<ImageBufferWrapper> wrapper = static_cast<ImageBufferWrapper*>(
+        mLib->av_buffer_get_opaque(mFrame->buf[0]));
+    MOZ_ASSERT(wrapper);
+    auto* image = wrapper->AsPlanarYCbCrImage();
+    RefPtr<layers::TextureClient> texture = image->GetTextureClient(nullptr);
+    if (!texture) {
+      NS_WARNING("Failed to get the texture client!");
+    } else {
+      // Texture was locked to ensure no one can modify or access texture's data
+      // except ffmpeg decoder. After finisheing decoding, texture's data would
+      // be avaliable for accessing for everyone so we unlock texture.
+      texture->Unlock();
+      v = VideoData::CreateFromImage(
+          mInfo.mDisplay, aOffset, TimeUnit::FromMicroseconds(aPts),
+          TimeUnit::FromMicroseconds(aDuration), image, !!mFrame->key_frame,
+          TimeUnit::FromMicroseconds(-1));
+    }
+  }
+#endif
+  if (!v) {
+    v = VideoData::CreateAndCopyData(
+        mInfo, mImageContainer, aOffset, TimeUnit::FromMicroseconds(aPts),
+        TimeUnit::FromMicroseconds(aDuration), b, !!mFrame->key_frame,
+        TimeUnit::FromMicroseconds(-1),
+        mInfo.ScaledImageRect(mFrame->width, mFrame->height), mImageAllocator);
+  }
 
   if (!v) {
     return MediaResult(NS_ERROR_OUT_OF_MEMORY,
                        RESULT_DETAIL("image allocation error"));
   }
   aResults.AppendElement(std::move(v));
   return NS_OK;
 }
@@ -758,20 +1101,16 @@ bool FFmpegVideoDecoder<LIBAV_VER>::GetV
       VA_EXPORT_SURFACE_READ_ONLY | VA_EXPORT_SURFACE_SEPARATE_LAYERS, aVaDesc);
   if (vas != VA_STATUS_SUCCESS) {
     return false;
   }
   vas = mLib->vaSyncSurface(mDisplay, surface_id);
   if (vas != VA_STATUS_SUCCESS) {
     NS_WARNING("vaSyncSurface() failed.");
   }
-
-  aVaDesc->width = mFrame->width;
-  aVaDesc->height = mFrame->height;
-
   return true;
 }
 
 MediaResult FFmpegVideoDecoder<LIBAV_VER>::CreateImageVAAPI(
     int64_t aOffset, int64_t aPts, int64_t aDuration,
     MediaDataDecoder::DecodedData& aResults) {
   FFMPEG_LOG("VA-API Got one frame output with pts=%" PRId64 "dts=%" PRId64
              " duration=%" PRId64 " opaque=%" PRId64,
@@ -890,29 +1229,31 @@ AVCodecID FFmpegVideoDecoder<LIBAV_VER>:
 
   return AV_CODEC_ID_NONE;
 }
 
 void FFmpegVideoDecoder<LIBAV_VER>::ProcessShutdown() {
   MOZ_ASSERT(mTaskQueue->IsOnCurrentThread());
 #ifdef MOZ_WAYLAND_USE_VAAPI
   mVideoFramePool = nullptr;
-  if (mVAAPIDeviceContext) {
+  if (IsHardwareAccelerated()) {
     mLib->av_buffer_unref(&mVAAPIDeviceContext);
   }
 #endif
   FFmpegDataDecoder<LIBAV_VER>::ProcessShutdown();
 }
 
-#ifdef MOZ_WAYLAND_USE_VAAPI
 bool FFmpegVideoDecoder<LIBAV_VER>::IsHardwareAccelerated(
     nsACString& aFailureReason) const {
+#ifdef MOZ_WAYLAND_USE_VAAPI
   return !!mVAAPIDeviceContext;
+#else
+  return false;
+#endif
 }
-#endif
 
 #ifdef MOZ_WAYLAND_USE_VAAPI
 bool FFmpegVideoDecoder<LIBAV_VER>::IsFormatAccelerated(
     AVCodecID aCodecID) const {
   for (const auto& format : mAcceleratedFormats) {
     if (format == aCodecID) {
       return true;
     }
--- a/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.h
+++ b/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.h
@@ -2,25 +2,32 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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 __FFmpegVideoDecoder_h__
 #define __FFmpegVideoDecoder_h__
 
+#include "ImageContainer.h"
 #include "FFmpegDataDecoder.h"
 #include "FFmpegLibWrapper.h"
 #include "SimpleMap.h"
+#include "mozilla/ScopeExit.h"
+#include "nsTHashSet.h"
+#if LIBAVCODEC_VERSION_MAJOR >= 57 && LIBAVUTIL_VERSION_MAJOR >= 56
+#  include "mozilla/layers/TextureClient.h"
+#endif
 
 struct _VADRMPRIMESurfaceDescriptor;
 typedef struct _VADRMPRIMESurfaceDescriptor VADRMPRIMESurfaceDescriptor;
 
 namespace mozilla {
 
+class ImageBufferWrapper;
 class VideoFramePool;
 
 template <int V>
 class FFmpegVideoDecoder : public FFmpegDataDecoder<V> {};
 
 template <>
 class FFmpegVideoDecoder<LIBAV_VER>;
 DDLoggedTypeNameAndBase(FFmpegVideoDecoder<LIBAV_VER>,
@@ -36,31 +43,46 @@ class FFmpegVideoDecoder<LIBAV_VER>
   typedef SimpleMap<int64_t> DurationMap;
 
  public:
   FFmpegVideoDecoder(FFmpegLibWrapper* aLib, const VideoInfo& aConfig,
                      KnowsCompositor* aAllocator,
                      ImageContainer* aImageContainer, bool aLowLatency,
                      bool aDisableHardwareDecoding);
 
+  ~FFmpegVideoDecoder();
+
   RefPtr<InitPromise> Init() override;
   void InitCodecContext() override;
   nsCString GetDescriptionName() const override {
 #ifdef USING_MOZFFVPX
     return "ffvpx video decoder"_ns;
 #else
     return "ffmpeg video decoder"_ns;
 #endif
   }
   ConversionRequired NeedsConversion() const override {
     return ConversionRequired::kNeedAVCC;
   }
 
   static AVCodecID GetCodecId(const nsACString& aMimeType);
 
+#if LIBAVCODEC_VERSION_MAJOR >= 57 && LIBAVUTIL_VERSION_MAJOR >= 56
+  int GetVideoBuffer(struct AVCodecContext* aCodecContext, AVFrame* aFrame,
+                     int aFlags);
+  int GetVideoBufferDefault(struct AVCodecContext* aCodecContext,
+                            AVFrame* aFrame, int aFlags) {
+    mIsUsingShmemBufferForDecode = Some(false);
+    return mLib->avcodec_default_get_buffer2(aCodecContext, aFrame, aFlags);
+  }
+  void ReleaseAllocatedImage(ImageBufferWrapper* aImage) {
+    mAllocatedImages.Remove(aImage);
+  }
+#endif
+
  private:
   RefPtr<FlushPromise> ProcessFlush() override;
   void ProcessShutdown() override;
   MediaResult DoDecode(MediaRawData* aSample, uint8_t* aData, int aSize,
                        bool* aGotFrame, DecodedData& aResults) override;
   void OutputDelayedFrames();
   bool NeedParser() const override {
     return
@@ -73,23 +95,40 @@ class FFmpegVideoDecoder<LIBAV_VER>
         mCodecID == AV_CODEC_ID_VP8;
 #endif
   }
   gfx::YUVColorSpace GetFrameColorSpace() const;
 
   MediaResult CreateImage(int64_t aOffset, int64_t aPts, int64_t aDuration,
                           MediaDataDecoder::DecodedData& aResults) const;
 
+  bool IsHardwareAccelerated(nsACString& aFailureReason) const override;
+  bool IsHardwareAccelerated() const {
+    nsAutoCString dummy;
+    return IsHardwareAccelerated(dummy);
+  }
+
+#if LIBAVCODEC_VERSION_MAJOR >= 57 && LIBAVUTIL_VERSION_MAJOR >= 56
+  layers::TextureClient* AllocateTextueClientForImage(
+      struct AVCodecContext* aCodecContext, layers::PlanarYCbCrImage* aImage);
+
+  layers::PlanarYCbCrData CreateEmptyPlanarYCbCrData(
+      struct AVCodecContext* aCodecContext, const VideoInfo& aInfo);
+
+  gfx::IntSize GetAlignmentVideoFrameSize(struct AVCodecContext* aCodecContext,
+                                          int32_t aWidth,
+                                          int32_t aHeight) const;
+#endif
+
 #ifdef MOZ_WAYLAND_USE_VAAPI
   void InitHWDecodingPrefs();
   MediaResult InitVAAPIDecoder();
   bool CreateVAAPIDeviceContext();
   void InitVAAPICodecContext();
   AVCodec* FindVAAPICodec();
-  bool IsHardwareAccelerated(nsACString& aFailureReason) const override;
   bool GetVAAPISurfaceDescriptor(VADRMPRIMESurfaceDescriptor* aVaDesc);
   void AddAcceleratedFormats(nsTArray<AVCodecID>& aCodecList,
                              AVCodecID aCodecID, AVVAAPIHWConfig* hwconfig);
   nsTArray<AVCodecID> GetAcceleratedFormats();
   bool IsFormatAccelerated(AVCodecID aCodecID) const;
 
   MediaResult CreateImageVAAPI(int64_t aOffset, int64_t aPts, int64_t aDuration,
                                MediaDataDecoder::DecodedData& aResults);
@@ -123,13 +162,70 @@ class FFmpegVideoDecoder<LIBAV_VER>
     int64_t mLastPts;       /// PTS of the last frame
     int64_t mLastDts;       /// DTS of the last frame
   };
 
   PtsCorrectionContext mPtsContext;
 
   DurationMap mDurationMap;
   const bool mLowLatency;
+
+  // True if we're allocating shmem for ffmpeg decode buffer.
+  Maybe<Atomic<bool>> mIsUsingShmemBufferForDecode;
+
+#if LIBAVCODEC_VERSION_MAJOR >= 57 && LIBAVUTIL_VERSION_MAJOR >= 56
+  // These images are buffers for ffmpeg in order to store decoded data when
+  // using custom allocator for decoding. We want to explictly track all images
+  // we allocate to ensure that we won't leak any of them.
+  nsTHashSet<RefPtr<ImageBufferWrapper>> mAllocatedImages;
+#endif
 };
 
+#if LIBAVCODEC_VERSION_MAJOR >= 57 && LIBAVUTIL_VERSION_MAJOR >= 56
+class ImageBufferWrapper final {
+ public:
+  typedef mozilla::layers::Image Image;
+  typedef mozilla::layers::PlanarYCbCrImage PlanarYCbCrImage;
+
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ImageBufferWrapper)
+
+  ImageBufferWrapper(Image* aImage, void* aDecoder)
+      : mImage(aImage), mDecoder(aDecoder) {
+    MOZ_ASSERT(aImage);
+    MOZ_ASSERT(mDecoder);
+  }
+
+  PlanarYCbCrImage* AsPlanarYCbCrImage() {
+    return mImage->AsPlanarYCbCrImage();
+  }
+
+  void ReleaseBuffer() {
+    auto clear = MakeScopeExit([&]() {
+      auto* decoder = static_cast<FFmpegVideoDecoder<LIBAV_VER>*>(mDecoder);
+      decoder->ReleaseAllocatedImage(this);
+    });
+    if (!mImage) {
+      return;
+    }
+    PlanarYCbCrImage* image = mImage->AsPlanarYCbCrImage();
+    RefPtr<layers::TextureClient> texture = image->GetTextureClient(nullptr);
+    // Usually the decoded video buffer would be locked when it is allocated,
+    // and gets unlocked when we create the video data via `DoDecode`. However,
+    // sometime the buffer won't be used for the decoded data (maybe just as
+    // an internal temporary usage?) so we will need to unlock the texture here
+    // before sending it back to recycle.
+    if (!texture) {
+      NS_WARNING("Failed to get the texture client during release!");
+    } else if (texture->IsLocked()) {
+      texture->Unlock();
+    }
+  }
+
+ private:
+  ~ImageBufferWrapper() = default;
+  const RefPtr<Image> mImage;
+  void* const MOZ_NON_OWNING_REF mDecoder;
+};
+#endif
+
 }  // namespace mozilla
 
 #endif  // __FFmpegVideoDecoder_h__
--- a/dom/media/platforms/ffmpeg/ffmpeg57/moz.build
+++ b/dom/media/platforms/ffmpeg/ffmpeg57/moz.build
@@ -21,9 +21,11 @@ if CONFIG['CC_TYPE'] == 'clang':
   CXXFLAGS += [
     '-Wno-unknown-attributes',
   ]
 if CONFIG['CC_TYPE'] == 'gcc':
   CXXFLAGS += [
     '-Wno-attributes',
   ]
 
+include("/ipc/chromium/chromium-config.mozbuild")
+
 FINAL_LIBRARY = 'xul'
--- a/dom/media/platforms/ffmpeg/ffmpeg58/moz.build
+++ b/dom/media/platforms/ffmpeg/ffmpeg58/moz.build
@@ -29,9 +29,11 @@ if CONFIG['CC_TYPE'] == 'gcc':
 if CONFIG['MOZ_WAYLAND']:
   CXXFLAGS += CONFIG['MOZ_GTK3_CFLAGS']
   DEFINES['MOZ_WAYLAND_USE_VAAPI'] = 1
   UNIFIED_SOURCES += [
     '../FFmpegVideoFramePool.cpp',
   ]
   USE_LIBS += ['mozva']
 
+include("/ipc/chromium/chromium-config.mozbuild")
+
 FINAL_LIBRARY = 'xul'
--- a/dom/media/platforms/ffmpeg/ffvpx/moz.build
+++ b/dom/media/platforms/ffmpeg/ffvpx/moz.build
@@ -37,9 +37,11 @@ if CONFIG["CC_TYPE"] == "gcc":
 DEFINES["FFVPX_VERSION"] = 46465650
 DEFINES["USING_MOZFFVPX"] = True
 
 if CONFIG["MOZ_WAYLAND"]:
     CXXFLAGS += CONFIG["MOZ_GTK3_CFLAGS"]
     DEFINES["MOZ_WAYLAND_USE_VAAPI"] = 1
     USE_LIBS += ["mozva"]
 
+include("/ipc/chromium/chromium-config.mozbuild")
+
 FINAL_LIBRARY = "xul"
--- a/dom/media/platforms/wrappers/AudioTrimmer.cpp
+++ b/dom/media/platforms/wrappers/AudioTrimmer.cpp
@@ -5,16 +5,20 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "AudioTrimmer.h"
 
 #define LOG(arg, ...)                                                  \
   DDMOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, "::%s: " arg, __func__, \
             ##__VA_ARGS__)
 
+#define LOGV(arg, ...)                                                   \
+  DDMOZ_LOG(sPDMLog, mozilla::LogLevel::Verbose, "::%s: " arg, __func__, \
+            ##__VA_ARGS__)
+
 namespace mozilla {
 
 using media::TimeInterval;
 using media::TimeUnit;
 
 RefPtr<MediaDataDecoder::InitPromise> AudioTrimmer::Init() {
   mThread = GetCurrentSerialEventTarget();
   return mDecoder->Init();
@@ -113,20 +117,20 @@ RefPtr<MediaDataDecoder::DecodePromise> 
       i++;
       continue;
     }
 
     Maybe<TimeInterval> trimmer = mTrimmers[0];
     mTrimmers.RemoveElementAt(0);
     if (!trimmer) {
       // Those frames didn't need trimming.
-      LOG("sample[%" PRId64 ",%" PRId64 "] (decoded[%" PRId64 ",%" PRId64
-          "] no trimming needed",
-          rawStart, rawEnd, sampleInterval.mStart.ToMicroseconds(),
-          sampleInterval.mEnd.ToMicroseconds());
+      LOGV("sample[%" PRId64 ",%" PRId64 "] (decoded[%" PRId64 ",%" PRId64
+           "] no trimming needed",
+           rawStart, rawEnd, sampleInterval.mStart.ToMicroseconds(),
+           sampleInterval.mEnd.ToMicroseconds());
       i++;
       continue;
     }
     if (!trimmer->Intersects(sampleInterval)) {
       LOG("sample[%" PRId64 ",%" PRId64 "] (decoded[%" PRId64 ",%" PRId64
           "] would be empty after trimming, dropping it",
           rawStart, rawEnd, sampleInterval.mStart.ToMicroseconds(),
           sampleInterval.mEnd.ToMicroseconds());
@@ -194,17 +198,17 @@ void AudioTrimmer::PrepareTrimmers(Media
         aRaw->mOriginalPresentationWindow->mStart.ToMicroseconds(),
         aRaw->mOriginalPresentationWindow->mEnd.ToMicroseconds(),
         aRaw->mTime.ToMicroseconds(), aRaw->GetEndTime().ToMicroseconds());
     mTrimmers.AppendElement(
         Some(TimeInterval(aRaw->mTime, aRaw->GetEndTime())));
     aRaw->mTime = aRaw->mOriginalPresentationWindow->mStart;
     aRaw->mDuration = aRaw->mOriginalPresentationWindow->Length();
   } else {
-    LOG("sample[%" PRId64 ",%" PRId64 "] no trimming information",
-        aRaw->mTime.ToMicroseconds(), aRaw->GetEndTime().ToMicroseconds());
+    LOGV("sample[%" PRId64 ",%" PRId64 "] no trimming information",
+         aRaw->mTime.ToMicroseconds(), aRaw->GetEndTime().ToMicroseconds());
     mTrimmers.AppendElement(Nothing());
   }
 }
 
 }  // namespace mozilla
 
 #undef LOG
--- a/dom/media/test/reftest/color_quads/reftest.list
+++ b/dom/media/test/reftest/color_quads/reftest.list
@@ -9,27 +9,30 @@
 # So, we just need to first check if e.g. av1.webm decodes to what we expect,
 # and then we have generally trivially compare other codecs/containers to that.
 
 defaults pref(media.av1.enabled,true)
 
 # -
 # yuv420p
 
-fuzzy(16-50,5234-5622) fuzzy-if(swgl,32-38,1600-91746) fuzzy-if(useDrawSnapshot,16-16,11600-11600) fuzzy-if(OSX,16-73,5212-5622) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p.av1.webm ../reftest_img.html?src=color_quads/720p.png
-fuzzy-if(Android,254-255,273680-273807) fuzzy-if(appleSilicon,30-48,1760-187409) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p.vp9.webm  ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p.av1.webm
+fuzzy(16-51,5234-5622) fuzzy-if(swgl,32-38,1600-91746) fuzzy-if(useDrawSnapshot,16-16,11600-11600) fuzzy-if(OSX,16-73,5212-5622) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p.av1.webm ../reftest_img.html?src=color_quads/720p.png
+fuzzy-if(winWidget&&swgl,0-20,0-5620) fuzzy-if(Android,254-255,273680-273807) fuzzy-if(OSX,0-35,0-1947) fuzzy-if(OSX&&swgl,0-67,0-5451) fuzzy-if(appleSilicon,30-48,1760-187409) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p.vp9.webm  ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p.av1.webm
 == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p.av1.mp4   ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p.av1.webm
 skip-if(winWidget&&isCoverageBuild) fuzzy(0-2,75-225) fuzzy-if(Android,254-255,273680-273807) fuzzy-if(OSX,30-32,187326-187407) fuzzy-if(appleSilicon,30-48,1835-187409) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p.h264.mp4 ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p.av1.webm
-fuzzy(0-1,0-75) fuzzy-if(Android,254-255,273680-273807) fuzzy-if(appleSilicon,30-48,1760-187409) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p.vp9.mp4   ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p.av1.webm
+fuzzy-if(winWidget&&swgl,0-20,0-5620) fuzzy-if(Android,254-255,273680-273807) fuzzy-if(OSX,0-35,0-1947) fuzzy-if(OSX&&swgl,0-67,0-5451) fuzzy-if(appleSilicon,30-48,1760-187409) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p.vp9.mp4   ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p.av1.webm
 
 skip-if(Android) fuzzy(16-48,8349-8818) fuzzy-if(winWidget&&swgl,31-38,8240-184080) fuzzy-if(appleSilicon,33-38,8819-11705) fuzzy-if(useDrawSnapshot,20-20,187200-187200) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.pc.yuv420p.av1.webm ../reftest_img.html?src=color_quads/720p.png
-skip-if(Android) fuzzy-if(Android,255-255,273726-273726) fuzzy-if(appleSilicon,36-49,187329-187407) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.pc.yuv420p.vp9.webm ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.pc.yuv420p.av1.webm
 skip-if(Android) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.pc.yuv420p.av1.mp4 ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.pc.yuv420p.av1.webm
+# On Windows & sw render, we noticed that the comparison image captured from AV1 is not equal to its displayed video frame, so we would need to compare other codecs directly to PNG file. That should be fixed in bug 1748540.
+skip-if(Android) skip-if(winWidget&&swgl) fuzzy-if(Android,255-255,273726-273726) fuzzy-if(OSX,0-16,0-1718) fuzzy-if(OSX&&swgl,0-20,0-2423) fuzzy-if(appleSilicon,36-49,187329-187407) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.pc.yuv420p.vp9.webm ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.pc.yuv420p.av1.webm
 skip-if(Android) skip-if(winWidget&&swgl) fuzzy-if(Android,255-255,273726-273726) fuzzy-if(OSX,2-36,184281-187407) fuzzy-if(winWidget,0-1,0-7) fuzzy-if(appleSilicon,36-49,187329-187407) fuzzy-if(useDrawSnapshot,0-1,0-10) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.pc.yuv420p.h264.mp4 ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.pc.yuv420p.av1.webm
-skip-if(Android) fuzzy-if(Android,255-255,273726-273726) fuzzy-if(appleSilicon,36-49,187329-187407)  == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.pc.yuv420p.vp9.mp4 ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.pc.yuv420p.av1.webm
+skip-if(Android) skip-if(winWidget&&swgl) fuzzy-if(Android,255-255,273726-273726) fuzzy-if(OSX,0-16,0-1718) fuzzy-if(OSX&&swgl,0-20,0-2423) fuzzy-if(appleSilicon,36-49,187329-187407)  == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.pc.yuv420p.vp9.mp4 ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.pc.yuv420p.av1.webm
+skip-if(Android) skip-if(!(winWidget&&swgl)) fuzzy(0-31,0-8240) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.pc.yuv420p.vp9.webm ../reftest_img.html?src=color_quads/720p.png
+skip-if(Android) skip-if(!(winWidget&&swgl)) fuzzy(0-31,0-8240) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.pc.yuv420p.vp9.mp4 ../reftest_img.html?src=color_quads/720p.png
 
 # -
 # yuv420p10
 
 skip-if(Android) fuzzy(33-49,2346-2579) fuzzy-if(swgl,34-52,181053-270528) fuzzy-if(appleSilicon,49-49,2263-2263) fuzzy-if(useDrawSnapshot,16-16,183840-183840) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p10.av1.webm ../reftest_img.html?src=color_quads/720p.png
 skip-if(Android) fuzzy-if(appleSilicon,38-38,273726-273726) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p10.vp9.webm ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p10.av1.webm
 skip-if(Android) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p10.av1.mp4 ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p10.av1.webm
 #[2] skip-if(Android) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p10.h264.mp4  ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p10.av1.webm
--- a/dom/media/test/reftest/gen_combos.py
+++ b/dom/media/test/reftest/gen_combos.py
@@ -1,17 +1,20 @@
 #!/usr/bin/env python3
 
+# E.g. `./gen_combos.py [--write] color_quads/720p.png`
+
 import concurrent.futures
 import pathlib
 import subprocess
 import sys
 
 ARGS = sys.argv
 SRC_PATH = pathlib.Path(ARGS.pop())
+assert SRC_PATH.exists(), "gen_combos.py [--flags] <src file path>"
 DIR = SRC_PATH.parent
 
 
 # crossCombine([{a:false},{a:5}], [{},{b:5}])
 # [{a:false}, {a:true}, {a:false,b:5}, {a:true,b:5}]
 def cross_combine(*args):
     args = list(args)
 
@@ -229,17 +232,21 @@ if "--write" not in ARGS:
 
 # -
 
 
 def run_cmd(args):
     dest = None
     if "-vv" not in ARGS:
         dest = subprocess.DEVNULL
-    subprocess.run(args, stderr=dest)
+    try:
+        subprocess.run(args, stderr=dest)
+    except FileNotFoundError:
+        print("FileNotFoundError, is ffmpeg not in your PATH?")
+        raise
 
 
 with concurrent.futures.ThreadPoolExecutor() as pool:
     fs = []
     for cur_args in todo:
         f = pool.submit(run_cmd, cur_args)
         fs.append(f)
 
--- a/dom/media/webrtc/CubebDeviceEnumerator.cpp
+++ b/dom/media/webrtc/CubebDeviceEnumerator.cpp
@@ -189,16 +189,19 @@ static RefPtr<AudioDeviceSet> GetDeviceC
     mozilla::mscom::EnsureMTA([&]() -> void {
 #  endif
       if (cubeb_enumerate_devices(context,
                                   aSide == Input ? CUBEB_DEVICE_TYPE_INPUT
                                                  : CUBEB_DEVICE_TYPE_OUTPUT,
                                   &collection) == CUBEB_OK) {
         for (unsigned int i = 0; i < collection.count; ++i) {
           auto device = collection.device[i];
+          if (device.max_channels == 0) {
+            continue;
+          }
           RefPtr<AudioDeviceInfo> info = new AudioDeviceInfo(
               device.devid, NS_ConvertUTF8toUTF16(device.friendly_name),
               NS_ConvertUTF8toUTF16(device.group_id),
               NS_ConvertUTF8toUTF16(device.vendor_name),
               ConvertCubebType(device.type), ConvertCubebState(device.state),
               ConvertCubebPreferred(device.preferred),
               ConvertCubebFormat(device.format),
               ConvertCubebFormat(device.default_format), device.max_channels,
--- a/dom/system/PathUtils.cpp
+++ b/dom/system/PathUtils.cpp
@@ -16,16 +16,17 @@
 #include "mozilla/Result.h"
 #include "mozilla/ResultExtensions.h"
 #include "mozilla/Span.h"
 #include "mozilla/dom/DOMParser.h"
 #include "mozilla/dom/PathUtilsBinding.h"
 #include "mozilla/dom/Promise.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsCOMPtr.h"
+#include "nsDirectoryServiceDefs.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsIFile.h"
 #include "nsIGlobalObject.h"
 #include "nsLocalFile.h"
 #include "nsNetUtil.h"
 #include "nsString.h"
 #include "xpcpublic.h"
 
@@ -354,19 +355,19 @@ already_AddRefed<Promise> PathUtils::Get
 already_AddRefed<Promise> PathUtils::GetTempDir(const GlobalObject& aGlobal,
                                                 ErrorResult& aErr) {
   auto guard = sDirCache.Lock();
   return DirectoryCache::Ensure(guard.ref())
       .GetDirectory(aGlobal, aErr, DirectoryCache::Directory::Temp);
 }
 
 PathUtils::DirectoryCache::DirectoryCache() {
-  mProfileDir.SetIsVoid(true);
-  mLocalProfileDir.SetIsVoid(true);
-  mTempDir.SetIsVoid(true);
+  for (auto& dir : mDirectories) {
+    dir.SetIsVoid(true);
+  }
 }
 
 PathUtils::DirectoryCache& PathUtils::DirectoryCache::Ensure(
     Maybe<PathUtils::DirectoryCache>& aCache) {
   if (aCache.isNothing()) {
     aCache.emplace();
 
     auto clearAtShutdown = []() {
@@ -409,70 +410,40 @@ already_AddRefed<Promise> PathUtils::Dir
     ResolveWithDirectory(promise, aRequestedDir);
   }
 
   return promise.forget();
 }
 
 void PathUtils::DirectoryCache::ResolveWithDirectory(
     Promise* aPromise, const Directory aRequestedDir) {
-  switch (aRequestedDir) {
-    case Directory::Profile:
-      MOZ_RELEASE_ASSERT(!mProfileDir.IsVoid());
-      aPromise->MaybeResolve(mProfileDir);
-      break;
-
-    case Directory::LocalProfile:
-      MOZ_RELEASE_ASSERT(!mLocalProfileDir.IsVoid());
-      aPromise->MaybeResolve(mLocalProfileDir);
-      break;
-
-    case Directory::Temp:
-      MOZ_RELEASE_ASSERT(!mTempDir.IsVoid());
-      aPromise->MaybeResolve(mTempDir);
-      break;
-
-    default:
-      MOZ_ASSERT_UNREACHABLE();
-  }
+  MOZ_RELEASE_ASSERT(aRequestedDir < Directory::Count);
+  MOZ_RELEASE_ASSERT(!mDirectories[aRequestedDir].IsVoid());
+  aPromise->MaybeResolve(mDirectories[aRequestedDir]);
 }
 
 already_AddRefed<PathUtils::DirectoryCache::PopulateDirectoriesPromise>
 PathUtils::DirectoryCache::PopulateDirectories(
     const PathUtils::DirectoryCache::Directory aRequestedDir) {
+  MOZ_RELEASE_ASSERT(aRequestedDir < Directory::Count);
+
   // If we have already resolved the requested directory, we can return
   // immediately.
-  if ((aRequestedDir == Directory::Temp && !mTempDir.IsVoid()) ||
-      (aRequestedDir == Directory::Profile && !mProfileDir.IsVoid()) ||
-      (aRequestedDir == Directory::LocalProfile &&
-       !mLocalProfileDir.IsVoid())) {
-    // We cannot have a state where mProfileDir is not populated but
-    // mLocalProfileDir is.
-    if (mProfileDir.IsVoid()) {
-      MOZ_RELEASE_ASSERT(mLocalProfileDir.IsVoid());
-    }
+  // Otherwise, if we have already fired off a request to populate the entry, so
+  // we can return the corresponding promise immediately. caller will queue a
+  // Thenable onto that promise to resolve/reject the request.
+  if (!mDirectories[aRequestedDir].IsVoid()) {
     return nullptr;
   }
+  if (!mPromises[aRequestedDir].IsEmpty()) {
+    return mPromises[aRequestedDir].Ensure(__func__);
+  }
 
-  // We have already fired off a request to populate the entry, so we can return
-  // the corresponding promise immediately. caller will queue a Thenable onto
-  // that promise to resolve/reject the request.
-  if (!mAllDirsPromise.IsEmpty()) {
-    return mAllDirsPromise.Ensure(__func__);
-  }
-  if (aRequestedDir != Directory::Temp && !mProfileDirsPromise.IsEmpty()) {
-    return mProfileDirsPromise.Ensure(__func__);
-  }
-
-  RefPtr<PopulateDirectoriesPromise> promise;
-  if (aRequestedDir == Directory::Temp) {
-    promise = mAllDirsPromise.Ensure(__func__);
-  } else {
-    promise = mProfileDirsPromise.Ensure(__func__);
-  }
+  RefPtr<PopulateDirectoriesPromise> promise =
+      mPromises[aRequestedDir].Ensure(__func__);
 
   if (NS_IsMainThread()) {
     nsresult rv = PopulateDirectoriesImpl(aRequestedDir);
     ResolvePopulateDirectoriesPromise(rv, aRequestedDir);
   } else {
     nsCOMPtr<nsIRunnable> runnable =
         NS_NewRunnableFunction(__func__, [aRequestedDir]() {
           auto cache = PathUtils::sDirCache.Lock();
@@ -482,75 +453,38 @@ PathUtils::DirectoryCache::PopulateDirec
     NS_DispatchToMainThread(runnable.forget());
   }
 
   return promise.forget();
 }
 
 void PathUtils::DirectoryCache::ResolvePopulateDirectoriesPromise(
     nsresult aRv, const PathUtils::DirectoryCache::Directory aRequestedDir) {
+  MOZ_RELEASE_ASSERT(aRequestedDir < Directory::Count);
+
   if (NS_SUCCEEDED(aRv)) {
-    if (aRequestedDir == Directory::Temp) {
-      mAllDirsPromise.Resolve(Ok{}, __func__);
-    } else {
-      mProfileDirsPromise.Resolve(Ok{}, __func__);
-    }
+    mPromises[aRequestedDir].Resolve(Ok{}, __func__);
   } else {
-    if (aRequestedDir == Directory::Temp) {
-      mAllDirsPromise.Reject(aRv, __func__);
-    } else {
-      mProfileDirsPromise.Reject(aRv, __func__);
-    }
+    mPromises[aRequestedDir].Reject(aRv, __func__);
   }
 }
 
 nsresult PathUtils::DirectoryCache::PopulateDirectoriesImpl(
     const PathUtils::DirectoryCache::Directory aRequestedDir) {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
+  MOZ_RELEASE_ASSERT(aRequestedDir < Directory::Count);
+
+  // We cannot have second request to populate any of these
+  // directories if the first request succeeded, so assert that the
+  // corresponding fields are void.
+  MOZ_RELEASE_ASSERT(mDirectories[aRequestedDir].IsVoid());
 
   nsCOMPtr<nsIFile> path;
 
-  // We only populate the temporary directory entry when specifically requested
-  // because the nsDirectoryService will do main thread IO to create the
-  // directory if it hasn't been created yet.
-  //
-  // Additionally, we cannot have second request to populate any of these
-  // directories if the first request succeeded, so assert that the
-  // corresponding fields are void.
-  if (aRequestedDir == Directory::Temp) {
-    MOZ_RELEASE_ASSERT(mTempDir.IsVoid());
-
-    MOZ_TRY(NS_GetSpecialDirectory(NS_APP_CONTENT_PROCESS_TEMP_DIR,
-                                   getter_AddRefs(path)));
-    MOZ_TRY(path->GetPath(mTempDir));
-  } else if (aRequestedDir == Directory::Profile) {
-    MOZ_RELEASE_ASSERT(mProfileDir.IsVoid());
-    MOZ_RELEASE_ASSERT(mLocalProfileDir.IsVoid());
-  } else {
-    MOZ_RELEASE_ASSERT(aRequestedDir == Directory::LocalProfile);
-    MOZ_RELEASE_ASSERT(mProfileDir.IsVoid());
-    MOZ_RELEASE_ASSERT(mLocalProfileDir.IsVoid());
-  }
-
-  if (mProfileDir.IsVoid()) {
-    MOZ_RELEASE_ASSERT(mLocalProfileDir.IsVoid());
-
-    nsString profileDir;
-    nsString localProfileDir;
-
-    MOZ_TRY(NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
-                                   getter_AddRefs(path)));
-    MOZ_TRY(path->GetPath(profileDir));
-
-    MOZ_TRY(NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR,
-                                   getter_AddRefs(path)));
-    MOZ_TRY(path->GetPath(localProfileDir));
-
-    // We either set both of these or neither.
-    mProfileDir = std::move(profileDir);
-    mLocalProfileDir = std::move(localProfileDir);
-  }
+  MOZ_TRY(NS_GetSpecialDirectory(kDirectoryNames[aRequestedDir],
+                                 getter_AddRefs(path)));
+  MOZ_TRY(path->GetPath(mDirectories[aRequestedDir]));
 
   return NS_OK;
 }
 
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/system/PathUtils.h
+++ b/dom/system/PathUtils.h
@@ -3,21 +3,23 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_PathUtils__
 #define mozilla_dom_PathUtils__
 
 #include "mozilla/DataMutex.h"
+#include "mozilla/EnumeratedArray.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/MozPromise.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/Result.h"
 #include "mozilla/dom/Promise.h"
+#include "nsAppDirectoryServiceDefs.h"
 #include "nsString.h"
 #include "nsTArray.h"
 
 namespace mozilla {
 class ErrorResult;
 
 namespace dom {
 
@@ -61,20 +63,18 @@ class PathUtils final {
 
   static void ToFileURI(const GlobalObject&, const nsAString& aPath,
                         nsCString& aResult, ErrorResult& aErr);
 
   static bool IsAbsolute(const GlobalObject&, const nsAString& aPath);
 
   static already_AddRefed<Promise> GetProfileDir(const GlobalObject& aGlobal,
                                                  ErrorResult& aErr);
-
   static already_AddRefed<Promise> GetLocalProfileDir(
       const GlobalObject& aGlobal, ErrorResult& aErr);
-
   static already_AddRefed<Promise> GetTempDir(const GlobalObject& aGlobal,
                                               ErrorResult& aErr);
 
  private:
   class DirectoryCache;
   friend class DirectoryCache;
 
   static StaticDataMutex<Maybe<DirectoryCache>> sDirCache;
@@ -96,16 +96,20 @@ class PathUtils::DirectoryCache final {
     /**
      * The user's local profile directory.
      */
     LocalProfile,
     /**
      * The temporary directory for the process.
      */
     Temp,
+    /**
+     * The number of Directory entries.
+     */
+    Count,
   };
 
   DirectoryCache();
   DirectoryCache(const DirectoryCache&) = delete;
   DirectoryCache(DirectoryCache&&) = delete;
   DirectoryCache& operator=(const DirectoryCache&) = delete;
   DirectoryCache& operator=(DirectoryCache&&) = delete;
 
@@ -191,27 +195,25 @@ class PathUtils::DirectoryCache final {
    * Can only be called once the cache entry for the requested directory is
    * populated.
    *
    * @param aPromise The JS promise to resolve.
    * @param aRequestedDir The requested directory cache entry.
    */
   void ResolveWithDirectory(Promise* aPromise, const Directory aRequestedDir);
 
-  /**
-   * A promise that is resolved when |mProfileDir| and |mLocalProfileDir| are
-   * populated.
-   */
-  MozPromiseHolder<PopulateDirectoriesPromise> mProfileDirsPromise;
-  nsString mProfileDir;
-  nsString mLocalProfileDir;
+  template <typename T>
+  using DirectoryArray = EnumeratedArray<Directory, Directory::Count, T>;
+
+  DirectoryArray<nsString> mDirectories;
+  DirectoryArray<MozPromiseHolder<PopulateDirectoriesPromise>> mPromises;
 
-  /**
-   * A promise that is resolved when *all* cache entries are populated.
-   */
-  MozPromiseHolder<PopulateDirectoriesPromise> mAllDirsPromise;
-  nsString mTempDir;
+  static constexpr DirectoryArray<const char*> kDirectoryNames{
+      NS_APP_USER_PROFILE_50_DIR,
+      NS_APP_USER_PROFILE_LOCAL_50_DIR,
+      NS_APP_CONTENT_PROCESS_TEMP_DIR,
+  };
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
 #endif
--- a/dom/webgpu/Device.cpp
+++ b/dom/webgpu/Device.cpp
@@ -28,18 +28,19 @@
 #include "ValidationError.h"
 #include "ipc/WebGPUChild.h"
 
 namespace mozilla {
 namespace webgpu {
 
 mozilla::LazyLogModule gWebGPULog("WebGPU");
 
-NS_IMPL_CYCLE_COLLECTION_INHERITED(Device, DOMEventTargetHelper, mBridge,
-                                   mQueue)
+GPU_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_INHERITED(Device, DOMEventTargetHelper,
+                                                 mBridge, mQueue, mFeatures,
+                                                 mLimits);
 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(Device, DOMEventTargetHelper)
 GPU_IMPL_JS_WRAP(Device)
 
 static void mapFreeCallback(void* aContents, void* aUserData) {
   Unused << aContents;
   Unused << aUserData;
 }
 
@@ -63,17 +64,17 @@ Device::Device(Adapter* const aParent, R
       mBridge(aParent->mBridge),
       mQueue(new class Queue(this, aParent->mBridge, aId)) {
   mBridge->RegisterDevice(mId, this);
 }
 
 Device::~Device() { Cleanup(); }
 
 void Device::Cleanup() {
-  if (mValid && mBridge && mBridge->IsOpen()) {
+  if (mValid && mBridge) {
     mValid = false;
     mBridge->UnregisterDevice(mId);
   }
 }
 
 void Device::GetLabel(nsAString& aValue) const { aValue = mLabel; }
 void Device::SetLabel(const nsAString& aLabel) { mLabel = aLabel; }
 
--- a/dom/webgpu/Device.h
+++ b/dom/webgpu/Device.h
@@ -5,16 +5,17 @@
 
 #ifndef GPU_DEVICE_H_
 #define GPU_DEVICE_H_
 
 #include "ObjectModel.h"
 #include "nsTHashSet.h"
 #include "mozilla/MozPromise.h"
 #include "mozilla/RefPtr.h"
+#include "mozilla/WeakPtr.h"
 #include "mozilla/webgpu/WebGPUTypes.h"
 #include "mozilla/webrender/WebRenderAPI.h"
 #include "mozilla/DOMEventTargetHelper.h"
 
 namespace mozilla {
 namespace dom {
 struct GPUExtensions;
 struct GPUFeatures;
@@ -72,17 +73,17 @@ class Sampler;
 class ShaderModule;
 class SupportedFeatures;
 class SupportedLimits;
 class Texture;
 class WebGPUChild;
 
 using MappingPromise = MozPromise<ipc::Shmem, ipc::ResponseRejectReason, true>;
 
-class Device final : public DOMEventTargetHelper {
+class Device final : public DOMEventTargetHelper, public SupportsWeakPtr {
  public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(Device, DOMEventTargetHelper)
   GPU_DECL_JS_WRAP(Device)
 
   const RawId mId;
   RefPtr<SupportedFeatures> mFeatures;
   RefPtr<SupportedLimits> mLimits;
--- a/dom/webgpu/ObjectModel.h
+++ b/dom/webgpu/ObjectModel.h
@@ -79,16 +79,28 @@ class ObjectBase : public nsWrapperCache
     NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER           \
     NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_PTR                    \
   NS_IMPL_CYCLE_COLLECTION_UNLINK_END                           \
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(T)                    \
     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(__VA_ARGS__)              \
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END                         \
   NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(T)
 
+#define GPU_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_INHERITED(T, P, ...) \
+  NS_IMPL_CYCLE_COLLECTION_CLASS(T)                                 \
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(T, P)             \
+    tmp->Cleanup();                                                 \
+    NS_IMPL_CYCLE_COLLECTION_UNLINK(__VA_ARGS__)                    \
+    NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER               \
+    NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_PTR                        \
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_END                               \
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(T, P)           \
+    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(__VA_ARGS__)                  \
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
 #define GPU_IMPL_CYCLE_COLLECTION(T, ...)            \
   NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(T, AddRef)    \
   NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(T, Release) \
   GPU_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(T, __VA_ARGS__)
 
 template <typename T>
 void ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& callback,
                                  nsTArray<RefPtr<const T>>& field,
--- a/dom/webgpu/ipc/WebGPUChild.cpp
+++ b/dom/webgpu/ipc/WebGPUChild.cpp
@@ -929,17 +929,17 @@ RefPtr<PipelinePromise> WebGPUChild::Dev
 }
 
 ipc::IPCResult WebGPUChild::RecvDeviceUncapturedError(
     RawId aDeviceId, const nsACString& aMessage) {
   auto targetIter = mDeviceMap.find(aDeviceId);
   if (!aDeviceId || targetIter == mDeviceMap.end()) {
     JsWarning(nullptr, aMessage);
   } else {
-    auto* target = targetIter->second;
+    auto* target = targetIter->second.get();
     MOZ_ASSERT(target);
     // We don't want to spam the errors to the console indefinitely
     if (target->CheckNewWarning(aMessage)) {
       JsWarning(target->GetOwnerGlobal(), aMessage);
 
       dom::GPUUncapturedErrorEventInit init;
       init.mError.SetAsGPUValidationError() =
           new ValidationError(target, aMessage);
@@ -980,13 +980,15 @@ void WebGPUChild::SwapChainPresent(wr::E
 }
 
 void WebGPUChild::RegisterDevice(RawId aId, Device* aDevice) {
   mDeviceMap.insert({aId, aDevice});
 }
 
 void WebGPUChild::UnregisterDevice(RawId aId) {
   mDeviceMap.erase(aId);
-  SendDeviceDestroy(aId);
+  if (IsOpen()) {
+    SendDeviceDestroy(aId);
+  }
 }
 
 }  // namespace webgpu
 }  // namespace mozilla
--- a/dom/webgpu/ipc/WebGPUChild.h
+++ b/dom/webgpu/ipc/WebGPUChild.h
@@ -128,17 +128,17 @@ class WebGPUChild final : public PWebGPU
   void ReleaseIPDLReference() {
     MOZ_ASSERT(mIPCOpen);
     mIPCOpen = false;
     Release();
   }
 
   ffi::WGPUClient* const mClient;
   bool mIPCOpen;
-  std::unordered_map<RawId, Device*> mDeviceMap;
+  std::unordered_map<RawId, WeakPtr<Device>> mDeviceMap;
 
  public:
   ipc::IPCResult RecvDeviceUncapturedError(RawId aDeviceId,
                                            const nsACString& aMessage);
   ipc::IPCResult RecvDropAction(const ipc::ByteBuf& aByteBuf);
 };
 
 }  // namespace webgpu
--- a/dom/webidl/MimeTypeArray.webidl
+++ b/dom/webidl/MimeTypeArray.webidl
@@ -2,16 +2,13 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
 [LegacyUnenumerableNamedProperties,
  Exposed=Window]
 interface MimeTypeArray {
-  [NeedsCallerType]
   readonly attribute unsigned long length;
 
-  [NeedsCallerType]
   getter MimeType? item(unsigned long index);
-  [NeedsCallerType]
   getter MimeType? namedItem(DOMString name);
 };
--- a/dom/webidl/WritableStream.webidl
+++ b/dom/webidl/WritableStream.webidl
@@ -4,16 +4,17 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/.
  *
  * The origin of this IDL file is
  * https://streams.spec.whatwg.org/#ws-class-definition
  */
 
 [Exposed=(Window,Worker,Worklet),
 //Transferable See Bug 1734240
+Pref="dom.streams.writable_streams.enabled"
 ]
 interface WritableStream {
   [Throws]
   constructor(optional object underlyingSink, optional QueuingStrategy strategy = {});
 
   readonly attribute boolean locked;
 
   [Throws]
--- a/dom/webidl/WritableStreamDefaultController.webidl
+++ b/dom/webidl/WritableStreamDefaultController.webidl
@@ -2,17 +2,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/.
  *
  * The origin of this IDL file is
  * https://streams.spec.whatwg.org/#ws-default-controller-class-definition
  */
 
-[Exposed=(Window,Worker,Worklet)]
+[Exposed=(Window,Worker,Worklet), Pref="dom.streams.writable_streams.enabled"]
 interface WritableStreamDefaultController {
   [Throws]
   void error(optional any e);
 };
 
 // TODO: AbortSignal is not exposed on Worklet
 [Exposed=(Window,Worker)]
 partial interface WritableStreamDefaultController {
--- a/dom/webidl/WritableStreamDefaultWriter.webidl
+++ b/dom/webidl/WritableStreamDefaultWriter.webidl
@@ -2,17 +2,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/.
  *
  * The origin of this IDL file is
  * https://streams.spec.whatwg.org/#default-writer-class-definition
  */
 
-[Exposed=(Window,Worker,Worklet)]
+[Exposed=(Window,Worker,Worklet), Pref="dom.streams.writable_streams.enabled"]
 interface WritableStreamDefaultWriter {
   [Throws]
   constructor(WritableStream stream);
 
   readonly attribute Promise<void> closed;
   [Throws] readonly attribute unrestricted double? desiredSize;
   readonly attribute Promise<void> ready;
 
--- a/dom/workers/test/browser_serviceworker_fetch_new_process.js
+++ b/dom/workers/test/browser_serviceworker_fetch_new_process.js
@@ -317,34 +317,15 @@ add_task(async function test() {
   // cloning.
   await do_test_sw("example.org", "privilegedmozilla", "fetch", fileBlob);
 
   // Same as the above but cloning the request before fetching it.
   await do_test_sw("example.org", "privilegedmozilla", "clone", fileBlob);
 
   // ## Fission Isolation
   if (Services.appinfo.fissionAutostart) {
-    const fissionUrl = "example.com";
-    const fissionRemoteType = `webIsolated=https://example.com`;
-
-    await do_test_sw(fissionUrl, fissionRemoteType, "synthetic", null);
-    await do_test_sw(fissionUrl, fissionRemoteType, "synthetic", fileBlob);
-
-    // ## ServiceWorker isolation via allow-list
+    // ## ServiceWorker isolation
     const isolateUrl = "example.com";
     const isolateRemoteType = `webServiceWorker=https://` + isolateUrl;
-    await SpecialPowers.pushPrefEnv({
-      set: [
-        [
-          "browser.tabs.remote.serviceWorkerIsolationList",
-          "https://" + isolateUrl,
-        ],
-        [
-          "browser.tabs.remote.separatePrivilegedMozillaWebContentProcess",
-          false,
-        ],
-        ["browser.tabs.remote.separatedMozillaDomains", ""],
-      ],
-    });
     await do_test_sw(isolateUrl, isolateRemoteType, "synthetic", null);
     await do_test_sw(isolateUrl, isolateRemoteType, "synthetic", fileBlob);
   }
 });
--- a/editor/libeditor/HTMLEditorDeleteHandler.cpp
+++ b/editor/libeditor/HTMLEditorDeleteHandler.cpp
@@ -3120,36 +3120,32 @@ bool HTMLEditor::AutoDeleteRangesHandler
   MOZ_ASSERT(!aRangesToDelete.IsCollapsed());
 
   mLeftContent = HTMLEditUtils::GetInclusiveAncestorElement(
       *aRangesToDelete.FirstRangeRef()->GetStartContainer()->AsContent(),
       HTMLEditUtils::ClosestEditableBlockElement);
   mRightContent = HTMLEditUtils::GetInclusiveAncestorElement(
       *aRangesToDelete.FirstRangeRef()->GetEndContainer()->AsContent(),
       HTMLEditUtils::ClosestEditableBlockElement);
-  // Note that mLeftContent and mRightContent can be nullptr if editing host
-  // is an inline element.
-  if (mLeftContent == mRightContent) {
-    MOZ_ASSERT_IF(!mLeftContent,
+  // Note that mLeftContent and/or mRightContent can be nullptr if editing host
+  // is an inline element.  If both editable ancestor block is exactly same
+  // one or one reaches an inline editing host, we can just delete the content
+  // in ranges.
+  if (mLeftContent == mRightContent || !mLeftContent || !mRightContent) {
+    MOZ_ASSERT_IF(!mLeftContent || !mRightContent,
                   aRangesToDelete.FirstRangeRef()
                           ->GetStartContainer()
                           ->AsContent()
                           ->GetEditingHost() == aRangesToDelete.FirstRangeRef()
                                                     ->GetEndContainer()
                                                     ->AsContent()
                                                     ->GetEditingHost());
     mMode = Mode::DeleteContentInRanges;
     return true;
   }
-  if (NS_WARN_IF(!mLeftContent) || NS_WARN_IF(!mRightContent)) {
-    return false;
-  }
-  NS_ASSERTION(
-      mLeftContent->GetEditingHost() == mRightContent->GetEditingHost(),
-      "Trying to delete across editing host boundaries");
 
   // If left block and right block are adjuscent siblings and they are same
   // type of elements, we can merge them after deleting the selected contents.
   // MOOSE: this could conceivably screw up a table.. fix me.
   if (mLeftContent->GetParentNode() == mRightContent->GetParentNode() &&
       HTMLEditUtils::CanContentsBeJoined(
           *mLeftContent, *mRightContent,
           aHTMLEditor.IsCSSEnabled() ? StyleDifference::CompareIfSpanElements
@@ -3224,17 +3220,16 @@ EditActionResult HTMLEditor::AutoDeleteR
                  ->GetEditingHost());
   MOZ_ASSERT(aRangesToDelete.FirstRangeRef()
                  ->GetStartContainer()
                  ->AsContent()
                  ->GetEditingHost() == aRangesToDelete.FirstRangeRef()
                                            ->GetEndContainer()
                                            ->AsContent()
                                            ->GetEditingHost());
-  MOZ_ASSERT(mLeftContent == mRightContent);
   MOZ_ASSERT_IF(mLeftContent, mLeftContent->IsElement());
   MOZ_ASSERT_IF(mLeftContent, aRangesToDelete.FirstRangeRef()
                                   ->GetStartContainer()
                                   ->IsInclusiveDescendantOf(mLeftContent));
   MOZ_ASSERT_IF(mRightContent, mRightContent->IsElement());
   MOZ_ASSERT_IF(mRightContent, aRangesToDelete.FirstRangeRef()
                                    ->GetEndContainer()
                                    ->IsInclusiveDescendantOf(mRightContent));
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -1285,17 +1285,17 @@ void GLContext::LoadMoreSymbols(const Sy
         const SymLoadStruct extSymbols[] = {
             { (PRFuncPtr*) &mSymbols.fBlendEquationSeparatei, {{ "glBlendEquationSeparateiOES" }} },
             { (PRFuncPtr*) &mSymbols.fBlendFuncSeparatei, {{ "glBlendFuncSeparateiOES" }} },
             { (PRFuncPtr*) &mSymbols.fColorMaski, {{ "glColorMaskiOES" }} },
             { (PRFuncPtr*) &mSymbols.fDisablei, {{ "glDisableiOES" }} },
             { (PRFuncPtr*) &mSymbols.fEnablei, {{ "glEnableiOES" }} },
             END_SYMBOLS
         };
-        fnLoadFeatureByCore(coreSymbols, extSymbols, GLFeature::draw_buffers);
+        fnLoadFeatureByCore(coreSymbols, extSymbols, GLFeature::draw_buffers_indexed);
     }
 
     if (IsSupported(GLFeature::get_integer_indexed)) {
         const SymLoadStruct coreSymbols[] = {
             { (PRFuncPtr*) &mSymbols.fGetIntegeri_v, {{ "glGetIntegeri_v" }} },
             END_SYMBOLS
         };
         const SymLoadStruct extSymbols[] ={
--- a/gfx/layers/BufferTexture.cpp
+++ b/gfx/layers/BufferTexture.cpp
@@ -90,16 +90,19 @@ class ShmemTextureData : public BufferTe
       : BufferTextureData(aDesc, aMoz2DBackend), mShmem(aShmem) {
     MOZ_ASSERT(mShmem.Size<uint8_t>());
   }
 
   virtual uint8_t* GetBuffer() override { return mShmem.get<uint8_t>(); }
 
   virtual size_t GetBufferSize() override { return mShmem.Size<uint8_t>(); }
 
+  bool CropYCbCrPlanes(const gfx::IntSize& aYSize,
+                       const gfx::IntSize& aCbCrSize) override;
+
  protected:
   mozilla::ipc::Shmem mShmem;
 };
 
 BufferTextureData* BufferTextureData::Create(
     gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
     gfx::BackendType aMoz2DBackend, LayersBackend aLayersBackend,
     TextureFlags aFlags, TextureAllocationFlags aAllocFlags,
@@ -501,10 +504,31 @@ TextureData* ShmemTextureData::CreateSim
                                   aLayersBackend, aFlags, aAllocFlags,
                                   aAllocator);
 }
 
 void ShmemTextureData::Deallocate(LayersIPCChannel* aAllocator) {
   aAllocator->DeallocShmem(mShmem);
 }
 
+bool ShmemTextureData::CropYCbCrPlanes(const gfx::IntSize& aYSize,
+                                       const gfx::IntSize& aCbCrSize) {
+  if (mDescriptor.type() != BufferDescriptor::TYCbCrDescriptor) {
+    return false;
+  }
+
+  const auto& current = mDescriptor.get_YCbCrDescriptor();
+  if (current.ySize() < aYSize || current.cbCrSize() < aCbCrSize) {
+    NS_WARNING("Cropped size should not exceed the original size!");
+    return false;
+  }
+
+  auto newDescritor = YCbCrDescriptor(
+      current.display(), aYSize, current.yStride(), aCbCrSize,
+      current.cbCrStride(), current.yOffset(), current.cbOffset(),
+      current.crOffset(), current.stereoMode(), current.colorDepth(),
+      current.yUVColorSpace(), current.colorRange());
+  mDescriptor = BufferDescriptor(newDescritor);
+  return true;
+}
+
 }  // namespace layers
 }  // namespace mozilla
--- a/gfx/layers/ImageContainer.h
+++ b/gfx/layers/ImageContainer.h
@@ -656,16 +656,22 @@ struct PlanarYCbCrData {
   gfx::YUVColorSpace mYUVColorSpace = gfx::YUVColorSpace::Default;
   gfx::ColorRange mColorRange = gfx::ColorRange::LIMITED;
 
   gfx::IntRect GetPictureRect() const {
     return gfx::IntRect(mPicX, mPicY, mPicSize.width, mPicSize.height);
   }
 
   static Maybe<PlanarYCbCrData> From(const SurfaceDescriptorBuffer&);
+
+  // We would use mPicSize, but that's not hooked up in WR for RawData
+  // ExternalImages, so we manually clip sizes later on. We should fix WR,
+  // but not in this patch. Do not use unless mPicSize doesn't work for you.
+  Maybe<gfx::IntSize> mCroppedYSize;
+  Maybe<gfx::IntSize> mCroppedCbCrSize;
 };
 
 // This type is currently only used for AVIF and therefore makes some
 // AVIF-specific assumptions (e.g., Alpha's bpc and stride is equal to Y's one)
 struct PlanarAlphaData {
   uint8_t* mChannel = nullptr;
   gfx::IntSize mSize = gfx::IntSize(0, 0);
   gfx::ColorDepth mDepth = gfx::ColorDepth::COLOR_8;
@@ -723,16 +729,21 @@ class PlanarYCbCrImage : public Image {
   virtual bool CopyData(const Data& aData) = 0;
 
   /**
    * This doesn't make a copy of the data buffers.
    */
   virtual bool AdoptData(const Data& aData);
 
   /**
+   * This will create an empty data buffers according to the input data's size.
+   */
+  virtual bool CreateEmptyBuffer(const Data& aData) { return false; }
+
+  /**
    * Ask this Image to not convert YUV to RGB during SetData, and make
    * the original data available through GetData. This is optional,
    * and not all PlanarYCbCrImages will support it.
    */
   virtual void SetDelayedConversion(bool aDelayed) {}
 
   /**
    * Grab the original YUV data. This is optional.
--- a/gfx/layers/client/TextureClient.cpp
+++ b/gfx/layers/client/TextureClient.cpp
@@ -845,16 +845,24 @@ bool TextureClient::BorrowMappedYCbCrDat
 }
 
 bool TextureClient::ToSurfaceDescriptor(SurfaceDescriptor& aOutDescriptor) {
   MOZ_ASSERT(IsValid());
 
   return mData ? mData->Serialize(aOutDescriptor) : false;
 }
 
+bool TextureClient::CropYCbCrPlanes(const gfx::IntSize& aYSize,
+                                    const gfx::IntSize& aCbCrSize) {
+  if (!mData) {
+    return false;
+  }
+  return mData->CropYCbCrPlanes(aYSize, aCbCrSize);
+}
+
 // static
 PTextureChild* TextureClient::CreateIPDLActor() {
   TextureChild* c = new TextureChild();
   c->AddIPDLReference();
   return c;
 }
 
 // static
--- a/gfx/layers/client/TextureClient.h
+++ b/gfx/layers/client/TextureClient.h
@@ -302,16 +302,27 @@ class TextureData {
 
   // The acquire fence is a fence that is used for waiting until rendering to
   // its AHardwareBuffer is completed.
   // It is used only on android.
   virtual mozilla::ipc::FileDescriptor GetAcquireFence() {
     return mozilla::ipc::FileDescriptor();
   }
 
+  /**
+   * Crop YCbCr planes to a smaller size. An use case is that we would need to
+   * allocate a larger size for planes in order to meet the special alignement
+   * requirement (eg. for ffmpeg video decoding), but crop planes to a correct
+   * range after allocation is done.
+   */
+  virtual bool CropYCbCrPlanes(const gfx::IntSize& aYSize,
+                               const gfx::IntSize& aCbCrSize) {
+    return false;
+  }
+
  protected:
   MOZ_COUNTED_DEFAULT_CTOR(TextureData)
 };
 
 /**
  * TextureClient is a thin abstraction over texture data that need to be shared
  * between the content process and the compositor process. It is the
  * content-side half of a TextureClient/TextureHost pair. A corresponding
@@ -465,16 +476,25 @@ class TextureClient : public AtomicRefCo
    * Copies a rectangle from this texture client to a position in aTarget.
    * It is assumed that the necessary locks are in place; so this should at
    * least have a read lock and aTarget should at least have a write lock.
    */
   bool CopyToTextureClient(TextureClient* aTarget, const gfx::IntRect* aRect,
                            const gfx::IntPoint* aPoint);
 
   /**
+   * Crop YCbCr planes to a smaller size. An use case is that we would need to
+   * allocate a larger size for planes in order to meet the special alignement
+   * requirement (eg. for ffmpeg video decoding), but crop planes to a correct
+   * range after allocation is done.
+   */
+  bool CropYCbCrPlanes(const gfx::IntSize& aYSize,
+                       const gfx::IntSize& aCbCrSize);
+
+  /**
    * Allocate and deallocate a TextureChild actor.
    *
    * TextureChild is an implementation detail of TextureClient that is not
    * exposed to the rest of the code base. CreateIPDLActor and DestroyIPDLActor
    * are for use with the managing IPDL protocols only (so that they can
    * implement AllocPextureChild and DeallocPTextureChild).
    */
   static PTextureChild* CreateIPDLActor();
--- a/gfx/layers/ipc/SharedPlanarYCbCrImage.cpp
+++ b/gfx/layers/ipc/SharedPlanarYCbCrImage.cpp
@@ -103,16 +103,21 @@ bool SharedPlanarYCbCrImage::CopyData(co
   return true;
 }
 
 bool SharedPlanarYCbCrImage::AdoptData(const Data& aData) {
   MOZ_ASSERT(false, "This shouldn't be used.");
   return false;
 }
 
+bool SharedPlanarYCbCrImage::CreateEmptyBuffer(const Data& aData) {
+  auto data = aData;
+  return Allocate(data);
+}
+
 bool SharedPlanarYCbCrImage::IsValid() const {
   return mTextureClient && mTextureClient->IsValid();
 }
 
 bool SharedPlanarYCbCrImage::Allocate(PlanarYCbCrData& aData) {
   MOZ_ASSERT(!mTextureClient, "This image already has allocated data");
 
   TextureFlags flags =
@@ -122,16 +127,28 @@ bool SharedPlanarYCbCrImage::Allocate(Pl
     mTextureClient = RecycleAllocator()->CreateOrRecycle(helper);
   }
 
   if (!mTextureClient) {
     NS_WARNING("SharedPlanarYCbCrImage::Allocate failed.");
     return false;
   }
 
+  gfx::IntSize imageYSize =
+      aData.mCroppedYSize ? *aData.mCroppedYSize : aData.mYSize;
+  gfx::IntSize imageCbCrSize =
+      aData.mCroppedCbCrSize ? *aData.mCroppedCbCrSize : aData.mCbCrSize;
+  if (aData.mCroppedYSize || aData.mCroppedCbCrSize) {
+    // If cropping fails, then reset Y&CbCr sizes to non-cropped sizes.
+    if (!mTextureClient->CropYCbCrPlanes(imageYSize, imageCbCrSize)) {
+      imageYSize = aData.mYSize;
+      imageCbCrSize = aData.mCbCrSize;
+    }
+  }
+
   MappedYCbCrTextureData mapped;
   // The locking here is sort of a lie. The SharedPlanarYCbCrImage just pulls
   // pointers out of the TextureClient and keeps them around, which works only
   // because the underlyin BufferTextureData is always mapped in memory even
   // outside of the lock/unlock interval. That's sad and new code should follow
   // this example.
   if (!mTextureClient->Lock(OpenMode::OPEN_READ) ||
       !mTextureClient->BorrowMappedYCbCrData(mapped)) {
@@ -141,18 +158,18 @@ bool SharedPlanarYCbCrImage::Allocate(Pl
   aData.mYChannel = mapped.y.data;
   aData.mCbChannel = mapped.cb.data;
   aData.mCrChannel = mapped.cr.data;
 
   // copy some of aData's values in mData (most of them)
   mData.mYChannel = aData.mYChannel;
   mData.mCbChannel = aData.mCbChannel;
   mData.mCrChannel = aData.mCrChannel;
-  mData.mYSize = aData.mYSize;
-  mData.mCbCrSize = aData.mCbCrSize;
+  mData.mYSize = imageYSize;
+  mData.mCbCrSize = imageCbCrSize;
   mData.mPicX = aData.mPicX;
   mData.mPicY = aData.mPicY;
   mData.mPicSize = aData.mPicSize;
   mData.mStereoMode = aData.mStereoMode;
   mData.mYUVColorSpace = aData.mYUVColorSpace;
   mData.mColorDepth = aData.mColorDepth;
   // those members are not always equal to aData's, due to potentially different
   // packing.
--- a/gfx/layers/ipc/SharedPlanarYCbCrImage.h
+++ b/gfx/layers/ipc/SharedPlanarYCbCrImage.h
@@ -33,30 +33,31 @@ class SharedPlanarYCbCrImage : public Pl
   virtual ~SharedPlanarYCbCrImage();
 
  public:
   TextureClient* GetTextureClient(KnowsCompositor* aKnowsCompositor) override;
 
   already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() override;
   bool CopyData(const PlanarYCbCrData& aData) override;
   bool AdoptData(const Data& aData) override;
-
-  bool Allocate(PlanarYCbCrData& aData);
+  bool CreateEmptyBuffer(const Data& aData) override;
 
   bool IsValid() const override;
 
   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override {
     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
   }
 
   size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override;
 
   TextureClientRecycleAllocator* RecycleAllocator();
 
  private:
+  bool Allocate(PlanarYCbCrData& aData);
+
   RefPtr<TextureClient> mTextureClient;
   RefPtr<ImageClient> mCompositable;
   RefPtr<TextureClientRecycleAllocator> mRecycleAllocator;
 };
 
 }  // namespace layers
 }  // namespace mozilla
 
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/auto-regress/bug1750496.js
@@ -0,0 +1,8 @@
+// |jit-test| --ion-offthread-compile=off; --ion-warmup-threshold=6; --baseline-warmup-threshold=0; --small-function-length=1024; --inlining-entry-threshold=1; --trial-inlining-warmup-threshold=5; 
+for (let v1 = 0; v1 < 4; v1++) {
+    const v28 = "aa".split('');
+    v28[0] = 1;
+    const v35 = "aa".split('');
+    assertEq(v35[0], "a");
+    assertEq(Object.is(v35, v28), false);
+}
--- a/js/src/jit/MIROps.yaml
+++ b/js/src/jit/MIROps.yaml
@@ -824,17 +824,16 @@
   gen_boilerplate: false
 
 - name: StringSplit
   operands:
     string: String
     separator: String
   result_type: Object
   possibly_calls: true
-  congruent_to: if_operands_equal
   # Although this instruction returns a new array, we don't have to mark
   # it as store instruction, see also MNewArray.
   alias_set: none
   can_recover: true
 
 - name: BoxNonStrictThis
   operands:
     def: Value
--- a/js/src/wasm/WasmIonCompile.cpp
+++ b/js/src/wasm/WasmIonCompile.cpp
@@ -2480,28 +2480,30 @@ class FunctionCompiler {
     // The loop body, if any, might be referencing recycled phis too.
     if (loopBody) {
       fixupRedundantPhis(loopBody);
     }
 
     // Pending jumps to an enclosing try-catch may reference the recycled phis.
     // We have to search above all enclosing try blocks, as a delegate may move
     // patches around.
+#ifdef ENABLE_WASM_EXCEPTIONS
     for (uint32_t depth = 0; depth < iter().controlStackDepth(); depth++) {
       if (iter().controlKind(depth) != LabelKind::Try) {
         continue;
       }
       Control& control = iter().controlItem(depth);
       for (MControlInstruction* patch : control.tryPadPatches) {
         MBasicBlock* block = patch->block();
         if (block->loopDepth() >= loopEntry->loopDepth()) {
           fixupRedundantPhis(block);
         }
       }
     }
+#endif
 
     // Discard redundant phis and add to the free list.
     for (MPhiIterator phi = loopEntry->phisBegin();
          phi != loopEntry->phisEnd();) {
       MPhi* entryDef = *phi++;
       if (!entryDef->isUnused()) {
         continue;
       }
--- a/js/xpconnect/src/XPCShellImpl.cpp
+++ b/js/xpconnect/src/XPCShellImpl.cpp
@@ -1498,16 +1498,23 @@ XPCShellDirProvider::GetFile(const char*
     *persistent = true;
     if (NS_FAILED(mGREDir->Clone(getter_AddRefs(file))) ||
         NS_FAILED(file->AppendNative("defaults"_ns)) ||
         NS_FAILED(file->AppendNative("pref"_ns)))
       return NS_ERROR_FAILURE;
     file.forget(result);
     return NS_OK;
   }
+#ifdef MOZ_SANDBOX
+  if (!strcmp(prop, NS_APP_CONTENT_PROCESS_TEMP_DIR)) {
+    // Forward to the OS Temp directory
+    *persistent = true;
+    return NS_GetSpecialDirectory(NS_OS_TEMP_DIR, result);
+  }
+#endif
 
   return NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 XPCShellDirProvider::GetFiles(const char* prop, nsISimpleEnumerator** result) {
   if (mGREDir && !strcmp(prop, "ChromeML")) {
     nsCOMArray<nsIFile> dirs;
--- a/layout/generic/nsFlexContainerFrame.cpp
+++ b/layout/generic/nsFlexContainerFrame.cpp
@@ -2509,16 +2509,24 @@ static bool FrameHasRelativeBSizeDepende
 }
 
 bool FlexItem::NeedsFinalReflow(const nscoord aAvailableBSizeForItem) const {
   MOZ_ASSERT(
       aAvailableBSizeForItem == NS_UNCONSTRAINEDSIZE ||
           aAvailableBSizeForItem > 0,
       "We can only handle unconstrained or positive available block-size.");
 
+  if (!StaticPrefs::layout_flexbox_item_final_reflow_optimization_enabled()) {
+    FLEX_LOG(
+        "[perf] Flex item %p needed a final reflow due to optimization being "
+        "disabled via the preference",
+        mFrame);
+    return true;
+  }
+
   // NOTE: even if aAvailableBSizeForItem == NS_UNCONSTRAINEDSIZE we can still
   // have continuations from an earlier constrained reflow.
   if (mFrame->GetPrevInFlow() || mFrame->GetNextInFlow()) {
     // This is an item has continuation(s). Reflow it.
     FLEX_LOG("[frag] Flex item %p needed a final reflow due to continuation(s)",
              mFrame);
     return true;
   }
@@ -2645,16 +2653,17 @@ bool FlexItem::NeedsFinalReflow(const ns
         mFrame);
     return true;
   }
 
   // If we get here, we can skip the final reflow! (The item's subtree isn't
   // dirty, and our current conditions are sufficiently similar to the most
   // recent "final reflow" that it should have left our subtree in the correct
   // state.)
+  FLEX_LOG("[perf] Flex item %p didn't need a final reflow", mFrame);
   return false;
 }
 
 // Keeps track of our position along a particular axis (where a '0' position
 // corresponds to the 'start' edge of that axis).
 // This class shouldn't be instantiated directly -- rather, it should only be
 // instantiated via its subclasses defined below.
 class MOZ_STACK_CLASS PositionTracker {
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -4,17 +4,16 @@
  * 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/. */
 
 /* rendering object to wrap rendering objects that should be scrollable */
 
 #include "nsGfxScrollFrame.h"
 
 #include "nsIXULRuntime.h"
-#include "ActiveLayerTracker.h"
 #include "base/compiler_specific.h"
 #include "DisplayItemClip.h"
 #include "Layers.h"
 #include "nsCOMPtr.h"
 #include "nsIContentViewer.h"
 #include "nsPresContext.h"
 #include "nsView.h"
 #include "nsViewportInfo.h"
@@ -5843,17 +5842,16 @@ void ScrollFrameHelper::FireScrollEvent(
   auto RestoreProcessingScrollEvent = mozilla::MakeScopeExit([&] {
     if (weakFrame.IsAlive()) {  // Otherwise `this` will be dead too.
       mProcessingScrollEvent = oldProcessing;
     }
   });
 
   mProcessingScrollEvent = true;
 
-  ActiveLayerTracker::SetCurrentScrollHandlerFrame(mOuter);
   WidgetGUIEvent event(true, eScroll, nullptr);
   nsEventStatus status = nsEventStatus_eIgnore;
   // Fire viewport scroll events at the document (where they
   // will bubble to the window)
   mozilla::layers::ScrollLinkedEffectDetector detector(
       content->GetComposedDoc());
   if (mIsRoot) {
     if (RefPtr<Document> doc = content->GetUncomposedDoc()) {
@@ -5862,17 +5860,16 @@ void ScrollFrameHelper::FireScrollEvent(
                                 &event, nullptr, &status);
     }
   } else {
     // scroll events fired at elements don't bubble (although scroll events
     // fired at documents do, to the window)
     event.mFlags.mBubbles = false;
     EventDispatcher::Dispatch(content, presContext, &event, nullptr, &status);
   }
-  ActiveLayerTracker::SetCurrentScrollHandlerFrame(nullptr);
 }
 
 void ScrollFrameHelper::PostScrollEvent(bool aDelayed) {
   if (mScrollEvent) {
     return;
   }
 
   // The ScrollEvent constructor registers itself with the refresh driver.
--- a/layout/painting/ActiveLayerTracker.cpp
+++ b/layout/painting/ActiveLayerTracker.cpp
@@ -13,17 +13,16 @@
 #include "mozilla/EffectSet.h"
 #include "mozilla/MotionPathUtils.h"
 #include "mozilla/PodOperations.h"
 #include "mozilla/StaticPtr.h"
 #include "gfx2DGlue.h"
 #include "nsExpirationTracker.h"
 #include "nsContainerFrame.h"
 #include "nsIContent.h"
-#include "nsIScrollableFrame.h"
 #include "nsRefreshDriver.h"
 #include "nsPIDOMWindow.h"
 #include "mozilla/dom/Document.h"
 #include "nsAnimationManager.h"
 #include "nsStyleTransformMatrix.h"
 #include "nsTransitionManager.h"
 #include "nsDisplayList.h"
 #include "nsDOMCSSDeclaration.h"
@@ -123,72 +122,50 @@ class LayerActivity {
   nsIFrame* mFrame;
   nsIContent* mContent;
 
   nsExpirationState mState;
 
   // Previous scale due to the CSS transform property.
   Maybe<Size> mPreviousTransformScale;
 
-  // The scroll frame during for which we most recently received a call to
-  // NotifyAnimatedFromScrollHandler.
-  WeakFrame mAnimatingScrollHandlerFrame;
-  // The set of activities that were triggered during
-  // mAnimatingScrollHandlerFrame's scroll event handler.
-  EnumSet<ActivityIndex> mScrollHandlerInducedActivity;
-
   // Number of restyle operations detected
   uint8_t mRestyleCounts[ACTIVITY_COUNT];
   bool mContentActive;
 };
 
 class LayerActivityTracker final
     : public nsExpirationTracker<LayerActivity, 4> {
  public:
   // 75-100ms is a good timeout period. We use 4 generations of 25ms each.
   enum { GENERATION_MS = 100 };
 
   explicit LayerActivityTracker(nsIEventTarget* aEventTarget)
       : nsExpirationTracker<LayerActivity, 4>(
-            GENERATION_MS, "LayerActivityTracker", aEventTarget),
-        mDestroying(false) {}
+            GENERATION_MS, "LayerActivityTracker", aEventTarget) {}
   ~LayerActivityTracker() override {
-    mDestroying = true;
     AgeAllGenerations();
   }
 
   void NotifyExpired(LayerActivity* aObject) override;
-
- public:
-  WeakFrame mCurrentScrollHandlerFrame;
-
- private:
-  bool mDestroying;
 };
 
 static StaticAutoPtr<LayerActivityTracker> gLayerActivityTracker;
 
 LayerActivity::~LayerActivity() {
   if (mFrame || mContent) {
     NS_ASSERTION(gLayerActivityTracker, "Should still have a tracker");
     gLayerActivityTracker->RemoveObject(this);
   }
 }
 
 // Frames with this property have NS_FRAME_HAS_LAYER_ACTIVITY_PROPERTY set
 NS_DECLARE_FRAME_PROPERTY_DELETABLE(LayerActivityProperty, LayerActivity)
 
 void LayerActivityTracker::NotifyExpired(LayerActivity* aObject) {
-  if (!mDestroying && aObject->mAnimatingScrollHandlerFrame.IsAlive()) {
-    // Reset the restyle counts, but let the layer activity survive.
-    PodArrayZero(aObject->mRestyleCounts);
-    MarkUsed(aObject);
-    return;
-  }
-
   RemoveObject(aObject);
 
   nsIFrame* f = aObject->mFrame;
   nsIContent* c = aObject->mContent;
   aObject->mFrame = nullptr;
   aObject->mContent = nullptr;
 
   MOZ_ASSERT((f == nullptr) != (c == nullptr),
@@ -342,37 +319,16 @@ void ActiveLayerTracker::NotifyAnimated(
     aDOMCSSDecl->GetPropertyValue(aProperty, oldValue);
     if (oldValue != aNewValue) {
       // We know this is animated, so just hack the mutation count.
       mutationCount = 0xFF;
     }
   }
 }
 
-/* static */
-void ActiveLayerTracker::NotifyAnimatedFromScrollHandler(
-    nsIFrame* aFrame, nsCSSPropertyID aProperty, nsIFrame* aScrollFrame) {
-  if (aFrame->PresContext() != aScrollFrame->PresContext()) {
-    // Don't allow cross-document dependencies.
-    return;
-  }
-  LayerActivity* layerActivity = GetLayerActivityForUpdate(aFrame);
-  LayerActivity::ActivityIndex activityIndex =
-      LayerActivity::GetActivityIndexForProperty(aProperty);
-
-  if (layerActivity->mAnimatingScrollHandlerFrame.GetFrame() != aScrollFrame) {
-    // Discard any activity of a different scroll frame. We only track the
-    // most recent scroll handler induced activity.
-    layerActivity->mScrollHandlerInducedActivity.clear();
-    layerActivity->mAnimatingScrollHandlerFrame = aScrollFrame;
-  }
-
-  layerActivity->mScrollHandlerInducedActivity += activityIndex;
-}
-
 static bool IsPresContextInScriptAnimationCallback(
     nsPresContext* aPresContext) {
   if (aPresContext->RefreshDriver()->IsInRefresh()) {
     return true;
   }
   // Treat timeouts/setintervals as scripted animation callbacks for our
   // purposes.
   nsPIDOMWindowInner* win = aPresContext->Document()->GetInnerWindow();
@@ -381,22 +337,16 @@ static bool IsPresContextInScriptAnimati
 
 /* static */
 void ActiveLayerTracker::NotifyInlineStyleRuleModified(
     nsIFrame* aFrame, nsCSSPropertyID aProperty, const nsACString& aNewValue,
     nsDOMCSSDeclaration* aDOMCSSDecl) {
   if (IsPresContextInScriptAnimationCallback(aFrame->PresContext())) {
     NotifyAnimated(aFrame, aProperty, aNewValue, aDOMCSSDecl);
   }
-  if (gLayerActivityTracker &&
-      gLayerActivityTracker->mCurrentScrollHandlerFrame.IsAlive()) {
-    NotifyAnimatedFromScrollHandler(
-        aFrame, aProperty,
-        gLayerActivityTracker->mCurrentScrollHandlerFrame.GetFrame());
-  }
 }
 
 /* static */
 void ActiveLayerTracker::NotifyNeedsRepaint(nsIFrame* aFrame) {
   LayerActivity* layerActivity = GetLayerActivityForUpdate(aFrame);
   if (IsPresContextInScriptAnimationCallback(aFrame->PresContext())) {
     // This is mirroring NotifyInlineStyleRuleModified's NotifyAnimated logic.
     // Just max out the restyle count if we're in an animation callback.
@@ -404,37 +354,16 @@ void ActiveLayerTracker::NotifyNeedsRepa
         0xFF;
   } else {
     IncrementMutationCount(
         &layerActivity
              ->mRestyleCounts[LayerActivity::ACTIVITY_TRIGGERED_REPAINT]);
   }
 }
 
-static bool CheckScrollInducedActivity(
-    LayerActivity* aLayerActivity, LayerActivity::ActivityIndex aActivityIndex,
-    nsDisplayListBuilder* aBuilder) {
-  if (!aLayerActivity->mScrollHandlerInducedActivity.contains(aActivityIndex) ||
-      !aLayerActivity->mAnimatingScrollHandlerFrame.IsAlive()) {
-    return false;
-  }
-
-  nsIScrollableFrame* scrollFrame =
-      do_QueryFrame(aLayerActivity->mAnimatingScrollHandlerFrame.GetFrame());
-  if (scrollFrame && scrollFrame->IsScrollingActiveNotMinimalDisplayPort()) {
-    return true;
-  }
-
-  // The scroll frame has been destroyed or has become inactive. Clear it from
-  // the layer activity so that it can expire.
-  aLayerActivity->mAnimatingScrollHandlerFrame = nullptr;
-  aLayerActivity->mScrollHandlerInducedActivity.clear();
-  return false;
-}
-
 /* static */
 bool ActiveLayerTracker::IsBackgroundPositionAnimated(
     nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) {
   LayerActivity* layerActivity = GetLayerActivity(aFrame);
   if (layerActivity) {
     LayerActivity::ActivityIndex activityIndex =
         LayerActivity::ActivityIndex::ACTIVITY_BACKGROUND_POSITION;
     if (layerActivity->mRestyleCounts[activityIndex] >= 2) {
@@ -443,19 +372,16 @@ bool ActiveLayerTracker::IsBackgroundPos
       // 'scale' (which includes the bounds changes of a rotation) is changing.
       // Marking a scaling transform as animating allows us to avoid resizing
       // the texture, even if we have to repaint the contents of that texture.
       if (layerActivity
               ->mRestyleCounts[LayerActivity::ACTIVITY_TRIGGERED_REPAINT] < 2) {
         return true;
       }
     }
-    if (CheckScrollInducedActivity(layerActivity, activityIndex, aBuilder)) {
-      return true;
-    }
   }
   return nsLayoutUtils::HasEffectiveAnimation(
       aFrame, nsCSSPropertyIDSet({eCSSProperty_background_position_x,
                                   eCSSProperty_background_position_y}));
 }
 
 static bool IsMotionPathAnimated(nsDisplayListBuilder* aBuilder,
                                  nsIFrame* aFrame) {
@@ -529,19 +455,16 @@ bool ActiveLayerTracker::IsStyleAnimated
       if (layerActivity
                   ->mRestyleCounts[LayerActivity::ACTIVITY_TRIGGERED_REPAINT] <
               2 ||
           (aPropertySet.Intersects(transformSet) &&
            IsScaleSubjectToAnimation(aFrame))) {
         return true;
       }
     }
-    if (CheckScrollInducedActivity(layerActivity, activityIndex, aBuilder)) {
-      return true;
-    }
   }
 
   if (nsLayoutUtils::HasEffectiveAnimation(aFrame, aPropertySet)) {
     return true;
   }
 
   if (!aPropertySet.Intersects(transformSet) ||
       !aFrame->Combines3DTransformWithAncestors()) {
@@ -593,20 +516,11 @@ void ActiveLayerTracker::NotifyContentCh
 
 /* static */
 bool ActiveLayerTracker::IsContentActive(nsIFrame* aFrame) {
   LayerActivity* layerActivity = GetLayerActivity(aFrame);
   return layerActivity && layerActivity->mContentActive;
 }
 
 /* static */
-void ActiveLayerTracker::SetCurrentScrollHandlerFrame(nsIFrame* aFrame) {
-  if (!gLayerActivityTracker) {
-    gLayerActivityTracker =
-        new LayerActivityTracker(GetMainThreadSerialEventTarget());
-  }
-  gLayerActivityTracker->mCurrentScrollHandlerFrame = aFrame;
-}
-
-/* static */
 void ActiveLayerTracker::Shutdown() { gLayerActivityTracker = nullptr; }
 
 }  // namespace mozilla
--- a/layout/painting/ActiveLayerTracker.h
+++ b/layout/painting/ActiveLayerTracker.h
@@ -58,23 +58,16 @@ class ActiveLayerTracker {
    * Any such marking will time out after a short period.
    * aNewValue and aDOMCSSDecl are used to determine whether the property's
    * value has changed.
    */
   static void NotifyAnimated(nsIFrame* aFrame, nsCSSPropertyID aProperty,
                              const nsACString& aNewValue,
                              nsDOMCSSDeclaration* aDOMCSSDecl);
   /**
-   * Notify aFrame as being known to have an animation of aProperty through an
-   * inline style modification during aScrollFrame's scroll event handler.
-   */
-  static void NotifyAnimatedFromScrollHandler(nsIFrame* aFrame,
-                                              nsCSSPropertyID aProperty,
-                                              nsIFrame* aScrollFrame);
-  /**
    * Notify that a property in the inline style rule of aFrame's element
    * has been modified.
    * This notification is incomplete --- not all modifications to inline
    * style will trigger this.
    * aNewValue and aDOMCSSDecl are used to determine whether the property's
    * value has changed.
    */
   static void NotifyInlineStyleRuleModified(nsIFrame* aFrame,
@@ -145,20 +138,13 @@ class ActiveLayerTracker {
    * Mark aFrame's content as being active. This marking will time out after
    * a short period.
    */
   static void NotifyContentChange(nsIFrame* aFrame);
   /**
    * Return true if this frame's content is still marked as active.
    */
   static bool IsContentActive(nsIFrame* aFrame);
-
-  /**
-   * Called before and after a scroll event handler is executed, with the
-   * scrollframe or nullptr, respectively. This acts as a hint to treat
-   * inline style changes during the handler differently.
-   */
-  static void SetCurrentScrollHandlerFrame(nsIFrame* aFrame);
 };
 
 }  // namespace mozilla
 
 #endif /* ACTIVELAYERTRACKER_H_ */
--- a/mach
+++ b/mach
@@ -72,16 +72,26 @@ def main(args):
             print(dedent("""
             We do not have specific instructions for your platform on how to
             install Python. You may find Pyenv (https://github.com/pyenv/pyenv)
             helpful, if your system package manager does not provide a way to
             install a recent enough Python 3.
             """).strip())
         sys.exit(1)
 
+    try:
+        import distutils
+    except ModuleNotFoundError:
+        print(dedent("""
+        Mach needs the Python "distutils" module, but it's not available on your 
+        machine. This error is most common on Debian Linux (or derivatives), and can be 
+        fixed by installing a package named something like `python3-distutils`.
+        """).strip())
+        sys.exit(1)
+
     # XCode python sets __PYVENV_LAUNCHER__, which overrides the executable
     # used when a python subprocess is created. This is an issue when we want
     # to run using our virtualenv python executables.
     # In future Python relases, __PYVENV_LAUNCHER__ will be cleared before
     # application code (mach) is started.
     # https://github.com/python/cpython/pull/9516
     os.environ.pop("__PYVENV_LAUNCHER__", None)
 
--- a/mobile/android/actors/GeckoViewPromptChild.jsm
+++ b/mobile/android/actors/GeckoViewPromptChild.jsm
@@ -15,16 +15,18 @@ const { Services } = ChromeUtils.import(
 const EXPORTED_SYMBOLS = ["GeckoViewPromptChild"];
 
 class GeckoViewPromptChild extends GeckoViewActorChild {
   handleEvent(event) {
     const { type } = event;
     debug`handleEvent: ${type}`;
 
     switch (type) {
+      case "mozshowdropdown": // fall-through
+      case "mozshowdropdown-sourcetouch": // fall-through
       case "click": // fall-through
       case "contextmenu": // fall-through
       case "DOMPopupBlocked":
         Services.prompt.wrappedJSObject.handleEvent(event);
     }
   }
 }
 
--- a/mobile/android/components/geckoview/GeckoViewPrompt.jsm
+++ b/mobile/android/components/geckoview/GeckoViewPrompt.jsm
@@ -24,17 +24,17 @@ class PromptFactory {
   constructor() {
     this.wrappedJSObject = this;
   }
 
   handleEvent(aEvent) {
     switch (aEvent.type) {
       case "mozshowdropdown":
       case "mozshowdropdown-sourcetouch":
-        this._handleSelect(aEvent);
+        this._handleSelect(aEvent.composedTarget);
         break;
       case "click":
         this._handleClick(aEvent);
         break;
       case "contextmenu":
         this._handleContextMenu(aEvent);
         break;
       case "DOMPopupBlocked":
@@ -42,31 +42,41 @@ class PromptFactory {
         break;
     }
   }
 
   // TODO(emilio): We should listen to MozOpenDateTimePicker instead, except
   // the Gecko widget isn't supported for stuff like <input type=week>
   _handleClick(aEvent) {
     const target = aEvent.composedTarget;
-    if (ChromeUtils.getClassName(target) !== "HTMLInputElement") {
+    const className = ChromeUtils.getClassName(target);
+    if (className !== "HTMLInputElement" && className !== "HTMLSelectElement") {
       return;
     }
 
     if (
       target.isContentEditable ||
       target.disabled ||
       target.readOnly ||
       !target.willValidate
     ) {
       // target.willValidate is false when any associated fieldset is disabled,
       // in which case this element is treated as disabled per spec.
       return;
     }
 
+    if (className === "HTMLSelectElement") {
+      if (target.multiple) {
+        this._handleSelect(target);
+        return;
+      }
+      // non-multiple select is handled by mozshowdropdown.
+      return;
+    }
+
     const type = target.type;
     if (
       type === "date" ||
       type === "month" ||
       type === "week" ||
       type === "time" ||
       type === "datetime-local"
     ) {
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/www/select-multiple.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<select multiple id="multiple">
+  <option>ABC</option>
+  <option>DEF</option>
+  <option>GHI</option>
+</select>
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/www/select.html
@@ -0,0 +1,6 @@
+<!doctype html>
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<select id="simple">
+  <option>ABC</option>
+  <option>DEF</option>
+</select>
--- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/BaseSessionTest.kt
+++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/BaseSessionTest.kt
@@ -38,16 +38,18 @@ open class BaseSessionTest(noErrorCollec
         const val CONTENT_CRASH_URL = "about:crashcontent"
         const val DOWNLOAD_HTML_PATH = "/assets/www/download.html"
         const val FORM_BLANK_HTML_PATH = "/assets/www/form_blank.html"
         const val FORMS_HTML_PATH = "/assets/www/forms.html"
         const val FORMS2_HTML_PATH = "/assets/www/forms2.html"
         const val FORMS3_HTML_PATH = "/assets/www/forms3.html"
         const val FORMS4_HTML_PATH = "/assets/www/forms4.html"
         const val FORMS5_HTML_PATH = "/assets/www/forms5.html"
+        const val SELECT_HTML_PATH = "/assets/www/select.html"
+        const val SELECT_MULTIPLE_HTML_PATH = "/assets/www/select-multiple.html"
         const val ADDRESS_FORM_HTML_PATH = "/assets/www/address_form.html"
         const val FORMS_AUTOCOMPLETE_HTML_PATH = "/assets/www/forms_autocomplete.html"
         const val FORMS_ID_VALUE_HTML_PATH = "/assets/www/forms_id_value.html"
         const val CC_FORM_HTML_PATH = "/assets/www/cc_form.html"
         const val HELLO_HTML_PATH = "/assets/www/hello.html"
         const val HELLO2_HTML_PATH = "/assets/www/hello2.html"
         const val HELLO_IFRAME_HTML_PATH = "/assets/www/iframe_hello.html"
         const val INPUTS_PATH = "/assets/www/inputs.html"
--- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/PromptDelegateTest.kt
+++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/PromptDelegateTest.kt
@@ -6,16 +6,17 @@ import org.mozilla.geckoview.GeckoSessio
 import org.mozilla.geckoview.GeckoSession.NavigationDelegate
 import org.mozilla.geckoview.GeckoSession.NavigationDelegate.LoadRequest
 import org.mozilla.geckoview.GeckoSession.ProgressDelegate
 import org.mozilla.geckoview.GeckoSession.PromptDelegate
 import org.mozilla.geckoview.GeckoSession.PromptDelegate.AuthPrompt
 import org.mozilla.geckoview.GeckoSession.PromptDelegate.PromptResponse
 import org.mozilla.geckoview.test.rule.GeckoSessionTestRule
 import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.AssertCalled
+import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.WithDisplay
 
 import androidx.test.filters.MediumTest
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import org.hamcrest.Matchers.*
 import org.junit.Assert
 import org.junit.Ignore
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -289,16 +290,62 @@ class PromptDelegateTest : BaseSessionTe
             }
         })
 
         sessionRule.waitForResult(promptResult2)
         sessionRule.waitForResult(result)
     }
 
     @Test
+    @WithDisplay(width = 100, height = 100)
+    fun selectTestSimple() {
+        mainSession.loadTestPath(SELECT_HTML_PATH)
+        sessionRule.waitForPageStop()
+
+        val result = GeckoResult<Void>()
+        sessionRule.delegateUntilTestEnd(object : PromptDelegate {
+            @AssertCalled(count = 1)
+            override fun onChoicePrompt(session: GeckoSession, prompt: PromptDelegate.ChoicePrompt): GeckoResult<PromptDelegate.PromptResponse>? {
+                assertThat("Should not be multiple", prompt.type, equalTo(PromptDelegate.ChoicePrompt.Type.SINGLE));
+                assertThat("There should be two choices", prompt.choices.size, equalTo(2));
+                assertThat("First choice is correct", prompt.choices[0].label, equalTo("ABC"));
+                assertThat("Second choice is correct", prompt.choices[1].label, equalTo("DEF"));
+                result.complete(null);
+                return null
+            }
+        })
+        mainSession.synthesizeTap(10, 10)
+        sessionRule.waitForResult(result)
+    }
+
+    @Test
+    @WithDisplay(width = 100, height = 100)
+    fun selectTestMultiple() {
+        mainSession.loadTestPath(SELECT_MULTIPLE_HTML_PATH)
+        sessionRule.waitForPageStop()
+
+        val result = GeckoResult<Void>()
+        sessionRule.delegateUntilTestEnd(object : PromptDelegate {
+            @AssertCalled(count = 1)
+            override fun onChoicePrompt(session: GeckoSession, prompt: PromptDelegate.ChoicePrompt): GeckoResult<PromptDelegate.PromptResponse>? {
+                assertThat("Should be multiple", prompt.type, equalTo(PromptDelegate.ChoicePrompt.Type.MULTIPLE));
+                assertThat("There should be three choices", prompt.choices.size, equalTo(3));
+                assertThat("First choice is correct", prompt.choices[0].label, equalTo("ABC"));
+                assertThat("Second choice is correct", prompt.choices[1].label, equalTo("DEF"));
+                assertThat("Third choice is correct", prompt.choices[2].label, equalTo("GHI"));
+                result.complete(null);
+                return null
+            }
+        })
+
+        mainSession.synthesizeTap(10, 10)
+        sessionRule.waitForResult(result)
+    }
+
+    @Test
     fun onBeforeUnloadTest() {
         sessionRule.setPrefsUntilTestEnd(mapOf(
                 "dom.require_user_interaction_for_beforeunload" to false
         ))
         mainSession.loadTestPath(BEFORE_UNLOAD)
         sessionRule.waitForPageStop()
 
         val result = GeckoResult<Void>()
@@ -362,33 +409,16 @@ class PromptDelegateTest : BaseSessionTe
             }
         })
 
         assertThat("Result should match",
                 mainSession.waitForJS("prompt('Prompt:', 'default')") as String,
                 equalTo("foo"))
     }
 
-    @Ignore // TODO: Figure out weird test env behavior here.
-    @Test fun choiceTest() {
-        sessionRule.setPrefsUntilTestEnd(mapOf("dom.disable_open_during_load" to false))
-
-        mainSession.loadTestPath(PROMPT_HTML_PATH)
-        mainSession.waitForPageStop()
-
-        mainSession.evaluateJS("document.getElementById('selectexample').click();")
-
-        sessionRule.waitUntilCalled(object : PromptDelegate {
-            @AssertCalled(count = 1)
-            override fun onChoicePrompt(session: GeckoSession, prompt: PromptDelegate.ChoicePrompt): GeckoResult<PromptDelegate.PromptResponse> {
-                return GeckoResult.fromValue(prompt.dismiss())
-            }
-        })
-    }
-
     @Test fun colorTest() {
         sessionRule.setPrefsUntilTestEnd(mapOf("dom.disable_open_during_load" to false))
 
         mainSession.loadTestPath(PROMPT_HTML_PATH)
         mainSession.waitForPageStop()
 
         sessionRule.delegateDuringNextWait(object : PromptDelegate {
             @AssertCalled(count = 1)
--- a/modules/libpref/init/StaticPrefList.yaml
+++ b/modules/libpref/init/StaticPrefList.yaml
@@ -1427,24 +1427,16 @@
 # existing content process over creating a new one. Enabling this pref should
 # reduce the number of processes allocated for non-first-party domains if
 # dom.ipc.processCount.webIsolated > 1.
 - name: browser.tabs.remote.subframesPreferUsed
   type: bool
   value: true
   mirror: always
 
-# Temporary pref which makes certain ServiceWorkers be isolated in special
-# processes instead of within a the normal web/webIsolated process based on
-# the URI.  Entries are separated by commas
-- name: browser.tabs.remote.serviceWorkerIsolationList
-  type: String
-  value: ""
-  mirror: never
-
 # When this pref is enabled, opaque response is only allowed to enter the
 # content process if it's a response for media (audio, image, video), CSS, or
 # JavaScript.
 - name: browser.opaqueResponseBlocking
   type: RelaxedAtomicBool
   value: @IS_NIGHTLY_BUILD@
   mirror: always
 
@@ -3599,16 +3591,21 @@
   mirror: always
 
 # DON'T enable, there are known issues!
 - name: dom.streams.byte_streams
   type: RelaxedAtomicBool
   value: false
   mirror: always
 
+- name: dom.streams.writable_streams.enabled
+  type: RelaxedAtomicBool
+  value: false
+  mirror: always
+
 # For area and anchor elements with target=_blank and no rel set to
 # opener/noopener.
 - name: dom.targetBlankNoOpener.enabled
   type: bool
   value: true
   mirror: always
 
 # Is support for Selection.GetRangesForInterval enabled?
@@ -6579,17 +6576,17 @@
 # processes. This is mirrored once, as the parent process will do this
 # allocation early on.
 - name: javascript.options.self_hosted.use_shared_memory
   type: bool
   value: true
   mirror: always  # LoadStartupJSPrefs
   do_not_use_directly: true
 
-# Streams API.
+# Streams API. This only applies to JS Streams
 - name: javascript.options.streams
   type: RelaxedAtomicBool
   value: true
   mirror: always
 
 # Writable Streams API.  (The pref above must also be set to expose this.)
 #
 # Writable streams are still EXTRAORDINARILY BETA and it is well-known that
@@ -7776,17 +7773,17 @@
 # Toggle retaining display lists between paints.
 - name: layout.display-list.retain.chrome
   type: RelaxedAtomicBool
   value: true
   mirror: always
 
 - name: layout.display-list.retain.sc
   type: RelaxedAtomicBool
-  value: false
+  value: @IS_NIGHTLY_BUILD@
   mirror: always
 
 # Set the maximum number of modified frames allowed before doing a full
 # display list rebuild.
 - name: layout.display-list.rebuild-frame-limit
   type: RelaxedAtomicUint32
   value: 500
   mirror: always
@@ -7825,16 +7822,24 @@
   mirror: always
 
 # Are dynamic reflow roots enabled?
 - name: layout.dynamic-reflow-roots.enabled
   type: bool
   value: @IS_EARLY_BETA_OR_EARLIER@
   mirror: always
 
+# Enables the mechanism to optimize away flex item's final reflow.
+# Warning: Disabling the pref will impact the performance. This is useful only for
+# debugging flexbox issues.
+- name: layout.flexbox.item-final-reflow-optimization.enabled
+  type: bool
+  value: true
+  mirror: always
+
 # Enables the <input type=search> custom layout frame with a clear icon.
 # Still needs tests and a web-exposed way to remove that icon, see bug 1654288.
 - name: layout.forms.input-type-search.enabled
   type: bool
   value: false
   mirror: always
 
 # Enables the Reveal Password button inside a <input type=password>.
@@ -8272,31 +8277,31 @@
 - name: media.cubeb.sandbox
   type: bool
   mirror: always
 #if defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID)
   value: true
 #elif defined(XP_WIN) && !defined(_ARM64_)
   value: true
 #elif defined(XP_MACOSX)
-  value: true
+  value: false
 #else
   value: false
 #endif
 
 # Whether cubeb sandbox (AudioIPC) is v2
 - name: media.cubeb.sandbox_v2
   type: bool
   mirror: always
 #if defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID)
   value: false
 #elif defined(XP_WIN) && !defined(_ARM64_)
   value: true
 #elif defined(XP_MACOSX)
-  value: true
+  value: false
 #else
   value: false
 #endif
 
 # Whether or not to pass AUDCLNT_STREAMOPTIONS_RAW when initializing audio
 # streams when using WASAPI.
 # 0 - don't use RAW streams
 # 1 - use RAW streams for input streams only
@@ -8717,16 +8722,22 @@
 
 #ifdef MOZ_OMX
 -   name: media.omx.enabled
     type: bool
     value: false
     mirror: always
 #endif
 
+# Allow ffmpeg decoder to decode directly onto shmem buffer
+- name: media.ffmpeg.customized-buffer-allocation
+  type: RelaxedAtomicBool
+  value: @IS_NIGHTLY_BUILD@
+  mirror: always
+
 #ifdef MOZ_FFMPEG
 -   name: media.ffmpeg.enabled
     type: RelaxedAtomicBool
   #if defined(XP_MACOSX)
     value: false
   #else
     value: true
   #endif
--- a/netwerk/dns/effective_tld_names.dat
+++ b/netwerk/dns/effective_tld_names.dat
@@ -3760,21 +3760,20 @@ emb.kw
 gov.kw
 ind.kw
 net.kw
 org.kw
 
 // ky : http://www.icta.ky/da_ky_reg_dom.php
 // Confirmed by registry <kysupport@perimeterusa.com> 2008-06-17
 ky
+com.ky
 edu.ky
-gov.ky
-com.ky
+net.ky
 org.ky
-net.ky
 
 // kz : https://en.wikipedia.org/wiki/.kz
 // see also: http://www.nic.kz/rules/index.jsp
 kz
 org.kz
 edu.kz
 net.kz
 gov.kz
--- a/python/mach/mach/site.py
+++ b/python/mach/mach/site.py
@@ -12,16 +12,17 @@ import enum
 import functools
 import json
 import os
 import platform
 import shutil
 import subprocess
 import sys
 from collections import OrderedDict
+from distutils import dist
 from pathlib import Path
 import tempfile
 from contextlib import contextmanager
 from typing import Optional, Callable
 
 from mach.requirements import (
     MachEnvRequirements,
     UnexpectedFlexibleRequirementException,
@@ -697,23 +698,16 @@ class PythonVirtualenv:
         else:
             self.bin_path = os.path.join(prefix, "bin")
             self.python_path = os.path.join(self.bin_path, "python")
         self.activate_path = os.path.join(self.bin_path, "activate_this.py")
         self.prefix = prefix
 
     @functools.lru_cache(maxsize=None)
     def site_packages_dir(self):
-        # Defer "distutils" import until this function is called so that
-        # "mach bootstrap" doesn't fail due to Linux distro python-distutils
-        # package not being installed.
-        # By the time this function is called, "distutils" must be installed
-        # because it's needed by the "virtualenv" package.
-        from distutils import dist
-
         normalized_venv_root = os.path.normpath(self.prefix)
 
         distribution = dist.Distribution({"script_args": "--no-user-cfg"})
         installer = distribution.get_command_obj("install")
         installer.prefix = normalized_venv_root
         installer.finalize_options()
 
         # Path to virtualenv's "site-packages" directory
--- a/python/mozboot/mozboot/archlinux.py
+++ b/python/mozboot/mozboot/archlinux.py
@@ -66,17 +66,17 @@ class ArchlinuxBootstrapper(LinuxBootstr
     def install_browser_packages(self, mozconfig_builder, artifact_mode=False):
         # TODO: Figure out what not to install for artifact mode
         self.aur_install(*self.BROWSER_AUR_PACKAGES)
         self.pacman_install(*self.BROWSER_PACKAGES)
 
     def install_browser_artifact_mode_packages(self, mozconfig_builder):
         self.install_browser_packages(mozconfig_builder, artifact_mode=True)
 
-    def ensure_nasm_packages(self, state_dir: Path, checkout_root: Path):
+    def ensure_nasm_packages(self):
         # installed via install_browser_packages
         pass
 
     def install_mobile_android_packages(self, mozconfig_builder, artifact_mode=False):
         # Multi-part process:
         # 1. System packages.
         # 2. Android SDK. Android NDK only if we are not in artifact mode. Android packages.
 
--- a/security/manager/ssl/StaticHPKPins.h
+++ b/security/manager/ssl/StaticHPKPins.h
@@ -1129,9 +1129,9 @@ static const TransportSecurityPreload kP
   { "za.search.yahoo.com", false, true, false, -1, &kPinset_yahoo },
   { "zh.search.yahoo.com", false, true, false, -1, &kPinset_yahoo },
 };
 
 // Pinning Preload List Length = 495;
 
 static const int32_t kUnknownId = -1;
 
-static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1651167487536000);
+static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1651488633623000);
--- a/services/settings/dumps/security-state/intermediates.json
+++ b/services/settings/dumps/security-state/intermediates.json
@@ -1,11 +1,47 @@
 {
   "data": [
     {
+      "schema": 1642777079288,
+      "derHash": "8Hu73gdvm0DFfMS+/t6Xyh9Tua4UfwNdKEy/U/NDL7g=",
+      "subject": "CN=CFCA OV OCA,O=China Financial Certification Authority,C=CN",
+      "subjectDN": "MFUxCzAJBgNVBAYTAkNOMTAwLgYDVQQKDCdDaGluYSBGaW5hbmNpYWwgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxFDASBgNVBAMMC0NGQ0EgT1YgT0NB",
+      "whitelist": false,
+      "attachment": {
+        "hash": "bc37658d1568750ae385b92be54ede64df31899d30f67211fd1e015c9dc7803b",
+        "size": 1963,
+        "filename": "uc0FPx73f1ObHGmGZOcevy371Uo9asVjdabpiS0lKgQ=.pem",
+        "location": "security-state-staging/intermediates/8a832b56-3f0a-4b5f-8966-8a1da5242863.pem",
+        "mimetype": "application/x-pem-file"
+      },
+      "pubKeyHash": "uc0FPx73f1ObHGmGZOcevy371Uo9asVjdabpiS0lKgQ=",
+      "crlite_enrolled": false,
+      "id": "b2b776e5-2b2f-4c8e-819d-026b8d3855d2",
+      "last_modified": 1643014635935
+    },
+    {
+      "schema": 1642604268417,
+      "derHash": "4g3/JvPDlj83AL8uEI8fnp3uJZwk+K/Q27tvzZobxZo=",
+      "subject": "CN=E-SAFER EXTENDED SSL CA  [Run by the Issuer],O=E-SAFER CONSULTORIA EM TECNOLOGIA DA INFORMACAO LTDA,C=BR",
+      "subjectDN": "MIGDMQswCQYDVQQGEwJCUjE9MDsGA1UEChM0RS1TQUZFUiBDT05TVUxUT1JJQSBFTSBURUNOT0xPR0lBIERBIElORk9STUFDQU8gTFREQTE1MDMGA1UEAwwsRS1TQUZFUiBFWFRFTkRFRCBTU0wgQ0EgIFtSdW4gYnkgdGhlIElzc3Vlcl0=",
+      "whitelist": false,
+      "attachment": {
+        "hash": "e3d91b124334fcf0c2f4314cdcac7ea72151da2fe3a6f07852655dd32fabee0f",
+        "size": 2316,
+        "filename": "plm_9YONwHj1IGtrwIqZq6BKWKFZfTMWD6vczJ53lUg=.pem",
+        "location": "security-state-staging/intermediates/8fa22e55-bb73-4e52-bfa7-8f445b589de2.pem",
+        "mimetype": "application/x-pem-file"
+      },
+      "pubKeyHash": "plm/9YONwHj1IGtrwIqZq6BKWKFZfTMWD6vczJ53lUg=",
+      "crlite_enrolled": true,
+      "id": "c647c617-859f-4bb3-8422-a6325636759f",
+      "last_modified": 1642777078159
+    },
+    {
       "schema": 1642600408228,
       "derHash": "eu3lN07Kn/NMVyfBDbmCIi11MVd5dEm4g2T3Du+gwkw=",
       "subject": "CN=Alibaba Cloud GCC R3 AlphaSSL CA 2021,O=Alibaba Cloud Computing Co.\\, Ltd.,C=CN",
       "subjectDN": "MGkxCzAJBgNVBAYTAkNOMSowKAYDVQQKEyFBbGliYWJhIENsb3VkIENvbXB1dGluZyBDby4sIEx0ZC4xLjAsBgNVBAMTJUFsaWJhYmEgQ2xvdWQgR0NDIFIzIEFscGhhU1NMIENBIDIwMjE=",
       "whitelist": false,
       "attachment": {
         "hash": "433ece3434c90b6bc0e4051245e9b8830f6689098ff3fae65e6394c641b077ca",
         "size": 1735,
@@ -4280,34 +4316,16 @@
         "mimetype": "application/x-pem-file"
       },
       "pubKeyHash": "mDRXN3fhAPbsEZzZ6KgUrT9K8v0Uy1/lYQrkjelgldw=",
       "crlite_enrolled": true,
       "id": "d6e5312b-851e-4527-a9d9-ab6b24c9674e",
       "last_modified": 1641308271745
     },
     {
-      "schema": 1641305381440,
-      "derHash": "8Hu73gdvm0DFfMS+/t6Xyh9Tua4UfwNdKEy/U/NDL7g=",
-      "subject": "CN=CFCA OV OCA,O=China Financial Certification Authority,C=CN",
-      "subjectDN": "MFUxCzAJBgNVBAYTAkNOMTAwLgYDVQQKDCdDaGluYSBGaW5hbmNpYWwgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxFDASBgNVBAMMC0NGQ0EgT1YgT0NB",
-      "whitelist": false,
-      "attachment": {
-        "hash": "bc37658d1568750ae385b92be54ede64df31899d30f67211fd1e015c9dc7803b",
-        "size": 1963,
-        "filename": "uc0FPx73f1ObHGmGZOcevy371Uo9asVjdabpiS0lKgQ=.pem",
-        "location": "security-state-staging/intermediates/8a832b56-3f0a-4b5f-8966-8a1da5242863.pem",
-        "mimetype": "application/x-pem-file"
-      },
-      "pubKeyHash": "uc0FPx73f1ObHGmGZOcevy371Uo9asVjdabpiS0lKgQ=",
-      "crlite_enrolled": true,
-      "id": "b2b776e5-2b2f-4c8e-819d-026b8d3855d2",
-      "last_modified": 1641308271735
-    },
-    {
       "schema": 1640873410856,
       "derHash": "fsrKSjWFo7QOJVdEFVEtVrV5mbdTAXhW8qsV+h8h9tA=",
       "subject": "CN=NETLOCK Trust Qualified EV CA 3,O=NETLOCK Ltd.,L=Budapest,C=HU",
       "subjectDN": "MGExCzAJBgNVBAYTAkhVMREwDwYDVQQHDAhCdWRhcGVzdDEVMBMGA1UECgwMTkVUTE9DSyBMdGQuMSgwJgYDVQQDDB9ORVRMT0NLIFRydXN0IFF1YWxpZmllZCBFViBDQSAz",
       "whitelist": false,
       "attachment": {
         "hash": "5c9cd98f81f0d60b8f3c40b8be94209d25226eb7ebf1c126947da32ed3d21dbc",
         "size": 2215,
@@ -7322,34 +7340,16 @@
         "mimetype": "application/x-pem-file"
       },
       "pubKeyHash": "Npod1XsOwiAqsSReK1vco3WHhvi/2dcU/nupbYHmSmo=",
       "crlite_enrolled": false,
       "id": "e09736ec-c1cc-43e4-bfa1-7a6a72fb8a8b",
       "last_modified": 1635368388833
     },
     {
-      "schema": 1635365180434,
-      "derHash": "4g3/JvPDlj83AL8uEI8fnp3uJZwk+K/Q27tvzZobxZo=",
-      "subject": "CN=E-SAFER EXTENDED SSL CA  [Run by the Issuer],O=E-SAFER CONSULTORIA EM TECNOLOGIA DA INFORMACAO LTDA,C=BR",
-      "subjectDN": "MIGDMQswCQYDVQQGEwJCUjE9MDsGA1UEChM0RS1TQUZFUiBDT05TVUxUT1JJQSBFTSBURUNOT0xPR0lBIERBIElORk9STUFDQU8gTFREQTE1MDMGA1UEAwwsRS1TQUZFUiBFWFRFTkRFRCBTU0wgQ0EgIFtSdW4gYnkgdGhlIElzc3Vlcl0=",
-      "whitelist": false,
-      "attachment": {
-        "hash": "e3d91b124334fcf0c2f4314cdcac7ea72151da2fe3a6f07852655dd32fabee0f",
-        "size": 2316,
-        "filename": "plm_9YONwHj1IGtrwIqZq6BKWKFZfTMWD6vczJ53lUg=.pem",
-        "location": "security-state-staging/intermediates/8fa22e55-bb73-4e52-bfa7-8f445b589de2.pem",
-        "mimetype": "application/x-pem-file"
-      },
-      "pubKeyHash": "plm/9YONwHj1IGtrwIqZq6BKWKFZfTMWD6vczJ53lUg=",
-      "crlite_enrolled": false,
-      "id": "c647c617-859f-4bb3-8422-a6325636759f",
-      "last_modified": 1635368388819
-    },
-    {
       "schema": 1635365182151,
       "derHash": "xjHKi5oSWUVaZh4sU4jNBppwS85U+l76cGvwsJYtj/M=",
       "subject": "CN=E-SAFER ORGANIZATION SSL CA  [Run by the Issuer],O=E-SAFER CONSULTORIA EM TECNOLOGIA DA INFORMACAO LTDA,C=BR",
       "subjectDN": "MIGHMQswCQYDVQQGEwJCUjE9MDsGA1UEChM0RS1TQUZFUiBDT05TVUxUT1JJQSBFTSBURUNOT0xPR0lBIERBIElORk9STUFDQU8gTFREQTE5MDcGA1UEAwwwRS1TQUZFUiBPUkdBTklaQVRJT04gU1NMIENBICBbUnVuIGJ5IHRoZSBJc3N1ZXJd",
       "whitelist": false,
       "attachment": {
         "hash": "c6ffec6c7f22be452547a0bd6a8debed72719992ae9682d83a85eb0e869c9398",
         "size": 2333,
--- a/taskcluster/docker/periodic-updates/scripts/getHSTSPreloadList.js
+++ b/taskcluster/docker/periodic-updates/scripts/getHSTSPreloadList.js
@@ -439,17 +439,17 @@ function filterForcedInclusions(inHosts,
 function output(statuses) {
   dump("INFO: Writing output to " + OUTPUT + "\n");
   try {
     var { FileUtils } = ChromeUtils.import(
       "resource://gre/modules/FileUtils.jsm"
     );
 
     let file = new FileUtils.File(
-      PathUtils.join(Services.dirsvc.get("CurWorkD", Ci.nsIfile).path, OUTPUT)
+      PathUtils.join(Services.dirsvc.get("CurWorkD", Ci.nsIFile).path, OUTPUT)
     );
     let fos = FileUtils.openSafeFileOutputStream(file);
     writeTo(HEADER, fos);
     writeTo(getExpirationTimeString(), fos);
 
     writeTo(GPERF_DELIM, fos);
 
     for (let status of statuses) {
--- a/testing/web-platform/meta/webaudio/the-audio-api/the-audioparam-interface/k-rate-dynamics-compressor-connections.html.ini
+++ b/testing/web-platform/meta/webaudio/the-audio-api/the-audioparam-interface/k-rate-dynamics-compressor-connections.html.ini
@@ -8,8 +8,11 @@
   [X k-rate threshold AudioParam with input does not equal [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0...\] with an element-wise tolerance of {"absoluteThreshold":0,"relativeThreshold":0}.\n\tIndex\tActual\t\t\tExpected\t\tAbsError\t\tRelError\t\tTest threshold\n\t[2432\]\t-1.2464844621717930e-2\t-1.2467551045119762e-2\t2.7064234018325806e-6\t2.1707738689323192e-4\t0.0000000000000000e+0\n\t[2433\]\t-1.2221718207001686e-2\t-1.2226036749780178e-2\t4.3185427784919739e-6\t3.5322507750270101e-4\t0.0000000000000000e+0\n\t[2434\]\t-1.1524772271513939e-2\t-1.1529812589287758e-2\t5.0403177738189697e-6\t4.3715522128277108e-4\t0.0000000000000000e+0\n\t[2435\]\t-1.1310782283544540e-2\t-1.1316668242216110e-2\t5.8859586715698242e-6\t5.2011409591496468e-4\t0.0000000000000000e+0\n\t[2436\]\t-1.1019663885235786e-2\t-1.1026103049516678e-2\t6.4391642808914185e-6\t5.8399275355708520e-4\t0.0000000000000000e+0\n\t...and 9558 more errors.\n\tMax AbsError of 1.7780810594558716e-5 at index of 2523.\n\t[2523\]\t2.0277552306652069e-2\t2.0295333117246628e-2\t1.7780810594558716e-5\t8.7610341214103488e-4\t0.0000000000000000e+0\n\tMax RelError of 3.2533393817767632e-2 at index of 2579.\n\t[2579\]\t-2.8552167350426316e-6\t-2.9512302717193961e-6\t9.6013536676764488e-8\t3.2533393817767632e-2\t0.0000000000000000e+0\n]
     expected: FAIL
 
   [# AUDIT TASK RUNNER FINISHED: 1 out of 5 tasks were failed.]
     expected: FAIL
 
   [X k-rate threshold AudioParam with input does not equal [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0...\] with an element-wise tolerance of {"absoluteThreshold":0,"relativeThreshold":0}.\n\tIndex\tActual\t\t\tExpected\t\tAbsError\t\tRelError\t\tTest threshold\n\t[2432\]\t-1.2464846484363079e-2\t-1.2467551976442337e-2\t2.7054920792579651e-6\t2.1700267096299585e-4\t0.0000000000000000e+0\n\t[2433\]\t-1.2221721000969410e-2\t-1.2226040475070477e-2\t4.3194741010665894e-6\t3.5330114519694408e-4\t0.0000000000000000e+0\n\t[2434\]\t-1.1524774134159088e-2\t-1.1529818177223206e-2\t5.0440430641174316e-6\t4.3747810993947684e-4\t0.0000000000000000e+0\n\t[2435\]\t-1.1310786008834839e-2\t-1.1316673830151558e-2\t5.8878213167190552e-6\t5.2027843208062160e-4\t0.0000000000000000e+0\n\t[2436\]\t-1.1019666679203510e-2\t-1.1026107706129551e-2\t6.4410269260406494e-6\t5.8416143735472506e-4\t0.0000000000000000e+0\n\t...and 9560 more errors.\n\tMax AbsError of 1.7777085304260254e-5 at index of 2523.\n\t[2523\]\t2.0277557894587517e-2\t2.0295334979891777e-2\t1.7777085304260254e-5\t8.7591977771608322e-4\t0.0000000000000000e+0\n\tMax RelError of 3.2567427641635026e-2 at index of 2579.\n\t[2579\]\t-2.8551585273817182e-6\t-2.9512739274650812e-6\t9.6115400083363056e-8\t3.2567427641635026e-2\t0.0000000000000000e+0\n]
     expected: FAIL
+
+  [X k-rate threshold AudioParam with input does not equal [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0...\] with an element-wise tolerance of {"absoluteThreshold":0,"relativeThreshold":0}.\n\tIndex\tActual\t\t\tExpected\t\tAbsError\t\tRelError\t\tTest threshold\n\t[2432\]\t-1.2464846484363079e-2\t-1.2467552907764912e-2\t2.7064234018325806e-6\t2.1707735446199663e-4\t0.0000000000000000e+0\n\t[2433\]\t-1.2221718207001686e-2\t-1.2226037681102753e-2\t4.3194741010665894e-6\t3.5330122593544839e-4\t0.0000000000000000e+0\n\t[2434\]\t-1.1524772271513939e-2\t-1.1529815383255482e-2\t5.0431117415428162e-6\t4.3739744080090176e-4\t0.0000000000000000e+0\n\t[2435\]\t-1.1310783214867115e-2\t-1.1316671036183834e-2\t5.8878213167190552e-6\t5.2027856053192512e-4\t0.0000000000000000e+0\n\t[2436\]\t-1.1019662022590637e-2\t-1.1026103049516678e-2\t6.4410269260406494e-6\t5.8416168406144066e-4\t0.0000000000000000e+0\n\t...and 9561 more errors.\n\tMax AbsError of 1.7780810594558716e-5 at index of 2523.\n\t[2523\]\t2.0277552306652069e-2\t2.0295333117246628e-2\t1.7780810594558716e-5\t8.7610341214103488e-4\t0.0000000000000000e+0\n\tMax RelError of 3.2612447363490234e-2 at index of 2579.\n\t[2579\]\t-2.8549693524837494e-6\t-2.9512157198041677e-6\t9.6246367320418358e-8\t3.2612447363490234e-2\t0.0000000000000000e+0\n]
+    expected: FAIL
--- a/testing/web-platform/meta/webaudio/the-audio-api/the-oscillatornode-interface/osc-basic-waveform.html.ini
+++ b/testing/web-platform/meta/webaudio/the-audio-api/the-oscillatornode-interface/osc-basic-waveform.html.ini
@@ -1,12 +1,11 @@
 [osc-basic-waveform.html]
   expected:
-    if os == "linux" and fission: ["OK", "FAIL"]
-
+    if (os == "linux") and fission: [OK, FAIL]
   [< [Test 0\] 2 out of 2 assertions were failed.]
     expected: FAIL
 
   [X Custom: 100 Hz does not equal [1,1.0141456127166748,1.0280853509902954,1.0418163537979126,1.0553359985351562,1.0686413049697876,1.081729769706726,1.094598650932312,1.1072453260421753,1.1196671724319458,1.1318618059158325,1.1438266038894653,1.1555593013763428,1.1670573949813843,1.1783186197280884,1.189340591430664...\] with an element-wise tolerance of {"absoluteThreshold":0.0000018478,"relativeThreshold":0}.\n\tIndex\tActual\t\t\tExpected\t\tAbsError\t\tRelError\t\tTest threshold\n\t[132\]\t6.4742153882980347e-1\t6.4741957187652588e-1\t1.9669532775878906e-6\t3.0381430575024735e-6\t1.8477999999999999e-6\n\t[134\]\t6.1133599281311035e-1\t6.1133408546447754e-1\t1.9073486328125000e-6\t3.1199775673612907e-6\t1.8477999999999999e-6\n\t[135\]\t5.9310543537139893e-1\t5.9310334920883179e-1\t2.0861625671386719e-6\t3.5173677065245060e-6\t1.8477999999999999e-6\n\t[136\]\t5.7475429773330688e-1\t5.7475227117538452e-1\t2.0265579223632813e-6\t3.5259676629357436e-6\t1.8477999999999999e-6\n\t[137\]\t5.5628657341003418e-1\t5.5628448724746704e-1\t2.0861625671386719e-6\t3.7501721061127269e-6\t1.8477999999999999e-6\n\t...and 97 more errors.\n\tMax AbsError of 4.5299530029296875e-6 at index of 197.\n\t[197\]\t-6.1586797237396240e-1\t-6.1587250232696533e-1\t4.5299530029296875e-6\t7.3553421947141031e-6\t1.8477999999999999e-6\n\tMax RelError of 4.6893454976520917e-4 at index of 165.\n\t[165\]\t7.5594307854771614e-3\t7.5558875687420368e-3\t3.5432167351245880e-6\t4.6893454976520917e-4\t1.8477999999999999e-6\n]
     expected: FAIL
 
   [< [Test 5\] 1 out of 2 assertions were failed.]
     expected: FAIL
@@ -38,8 +37,17 @@
   [< [Test 1\] 2 out of 2 assertions were failed.]
     expected: FAIL
 
   [X Sine: SNR (db) is not greater than or equal to 118.91. Got 112.67669857220825.]
     expected: FAIL
 
   [X Sine: SNR (db) is not greater than or equal to 130.95. Got 112.67669857220825.]
     expected: FAIL
+
+  [X Custom: 100 Hz does not equal [1,1.0141456127166748,1.0280853509902954,1.0418163537979126,1.0553359985351562,1.0686413049697876,1.081729769706726,1.094598650932312,1.1072453260421753,1.1196671724319458,1.1318618059158325,1.1438266038894653,1.1555593013763428,1.1670573949813843,1.1783186197280884,1.189340591430664...\] with an element-wise tolerance of {"absoluteThreshold":0.0000018478,"relativeThreshold":0}.\n\tIndex\tActual\t\t\tExpected\t\tAbsError\t\tRelError\t\tTest threshold\n\t[132\]\t6.4742147922515869e-1\t6.4741957187652588e-1\t1.9073486328125000e-6\t2.9460781163660346e-6\t1.8477999999999999e-6\n\t[133\]\t6.2944263219833374e-1\t6.2944072484970093e-1\t1.9073486328125000e-6\t3.0302275615673585e-6\t1.8477999999999999e-6\n\t[135\]\t5.9310543537139893e-1\t5.9310334920883179e-1\t2.0861625671386719e-6\t3.5173677065245060e-6\t1.8477999999999999e-6\n\t[136\]\t5.7475423812866211e-1\t5.7475227117538452e-1\t1.9669532775878906e-6\t3.4222627316729277e-6\t1.8477999999999999e-6\n\t[137\]\t5.5628657341003418e-1\t5.5628448724746704e-1\t2.0861625671386719e-6\t3.7501721061127269e-6\t1.8477999999999999e-6\n\t...and 97 more errors.\n\tMax AbsError of 4.5299530029296875e-6 at index of 197.\n\t[197\]\t-6.1586797237396240e-1\t-6.1587250232696533e-1\t4.5299530029296875e-6\t7.3553421947141031e-6\t1.8477999999999999e-6\n\tMax RelError of 4.6918106549645650e-4 at index of 165.\n\t[165\]\t7.5594326481223106e-3\t7.5558875687420368e-3\t3.5450793802738190e-6\t4.6918106549645650e-4\t1.8477999999999999e-6\n]
+    expected: FAIL
+
+  [X Custom: SNR (db) is not greater than or equal to 122.43. Got 112.76579764697749.]
+    expected: FAIL
+
+  [X Custom: SNR (db) is not greater than or equal to 138.76. Got 132.76794522938812.]
+    expected: FAIL
--- a/testing/web-platform/meta/webaudio/the-audio-api/the-pannernode-interface/panner-rolloff-clamping.html.ini
+++ b/testing/web-platform/meta/webaudio/the-audio-api/the-pannernode-interface/panner-rolloff-clamping.html.ini
@@ -2,8 +2,11 @@
   [X Panner distanceModel: "linear", rolloffFactor: 2 expected to be equal to the array [0,0.009438800625503063,0.02810869924724102,0.03424321487545967,0.049286145716905594,0.06121714785695076,0.07480449229478836,0.08126655966043472,0.10326723754405975,0.10609924048185349,0.12179718166589737,0.1385885775089264,0.14151552319526672,0.16106881201267242,0.1722015142440796,0.18278823792934418...\] but differs in 2047 places:\n\tIndex\tActual\t\t\tExpected\n\t[1\]\t1.8877600496125524e-6\t9.4388006255030632e-3\n\t[2\]\t5.6217400015157182e-6\t2.8108699247241020e-2\n\t[3\]\t6.8486433519865386e-6\t3.4243214875459671e-2\n\t[4\]\t9.8572290880838409e-6\t4.9286145716905594e-2\n\t...and 2043 more errors.]
     expected: FAIL
 
   [< [linear-clamp-high\] 1 out of 1 assertions were failed.]
     expected: FAIL
 
   [# AUDIT TASK RUNNER FINISHED: 1 out of 1 tasks were failed.]
     expected: FAIL
+
+  [X Panner distanceModel: "linear", rolloffFactor: 2 expected to be equal to the array [0,0.009438806213438511,0.028108695521950722,0.03424323722720146,0.04928614944219589,0.06121715530753136,0.07480449974536896,0.08126655966043472,0.10326726734638214,0.10609924048185349,0.12179719656705856,0.13858859241008759,0.1415155529975891,0.1610688418149948,0.1722015142440796,0.18278825283050537...\] but differs in 2047 places:\n\tIndex\tActual\t\t\tExpected\n\t[1\]\t1.8877611864809296e-6\t9.4388062134385109e-3\n\t[2\]\t5.6217390920210164e-6\t2.8108695521950722e-2\n\t[3\]\t6.8486474447126966e-6\t3.4243237227201462e-2\n\t[4\]\t9.8572299975785427e-6\t4.9286149442195892e-2\n\t...and 2043 more errors.]
+    expected: FAIL
--- a/testing/web-platform/tests/editing/data/delete.js
+++ b/testing/web-platform/tests/editing/data/delete.js
@@ -1,8 +1,10 @@
+class MyCustomElement extends HTMLElement {};
+customElements.define("custom-element", MyCustomElement);
 // For documentation of the format, see README in this directory.
 var browserTests = [
 ["foo[]bar",
     [["delete",""]],
     "fo[]bar",
     [true],
     {"delete":[false,false,"",false,false,""]}],
 ["<span>foo</span>{}<span>bar</span>",
@@ -2741,9 +2743,33 @@ var browserTests = [
      "<p contenteditable=\"false\"><span contenteditable=\"\">af<br></span></p>"],
     [true],
     {"delete":[false,false,"",false,false,""]}],
 ["<p contenteditable=\"false\"><unknown-element contenteditable>[abc]</unknown-element></p>",
     [["delete",""]],
     "<p contenteditable=\"false\"><unknown-element contenteditable=\"\"></unknown-element></p>",
     [true],
     {"delete":[false,false,"",false,false,""]}],
+["<div contenteditable=\"false\"><custom-element contenteditable=\"\"><p>[ab</p><p>c]d</p></custom-element></div>",
+    [["delete",""]],
+    ["<div contenteditable=\"false\"><custom-element contenteditable=\"\"><p>d</p></custom-element></div>",
+     "<div contenteditable=\"false\"><custom-element contenteditable=\"\"><p>d<br></p></custom-element></div>"],
+    [true],
+    {"delete":[false,false,"",false,false,""]}],
+["<div contenteditable=\"false\"><custom-element contenteditable=\"\"><p>a[b</p><p>cd]</p></custom-element></div>",
+    [["delete",""]],
+    ["<div contenteditable=\"false\"><custom-element contenteditable=\"\"><p>a</p></custom-element></div>",
+     "<div contenteditable=\"false\"><custom-element contenteditable=\"\"><p>a<br></p></custom-element></div>"],
+    [true],
+    {"delete":[false,false,"",false,false,""]}],
+["<div contenteditable=\"false\"><custom-element contenteditable=\"\"><p><b>[ab</b></p><p><i>c]d</i></p></custom-element></div>",
+    [["delete",""]],
+    ["<div contenteditable=\"false\"><custom-element contenteditable=\"\"><p><i>d</i></p></custom-element></div>",
+     "<div contenteditable=\"false\"><custom-element contenteditable=\"\"><p><i>d</i><br></p></custom-element></div>"],
+    [true],
+    {"delete":[false,false,"",false,false,""]}],
+["<div contenteditable=\"false\"><custom-element contenteditable=\"\"><p><b>a[b</b></p><p><i>cd]</i></p></custom-element></div>",
+    [["delete",""]],
+    ["<div contenteditable=\"false\"><custom-element contenteditable=\"\"><p><b>a</b></p></custom-element></div>",
+     "<div contenteditable=\"false\"><custom-element contenteditable=\"\"><p><b>a</b><br></p></custom-element></div>"],
+    [true],
+    {"delete":[false,false,"",false,false,""]}],
 ]
--- a/testing/web-platform/tests/editing/data/forwarddelete.js
+++ b/testing/web-platform/tests/editing/data/forwarddelete.js
@@ -1,8 +1,10 @@
+class MyCustomElement extends HTMLElement {};
+customElements.define("custom-element", MyCustomElement);
 // For documentation of the format, see README in this directory.
 var browserTests = [
 ["foo[]",
     [["forwarddelete",""]],
     "foo{}",
     [true],
     {"forwarddelete":[false,false,"",false,false,""]}],
 ["<span>foo[]</span>",
@@ -2622,9 +2624,33 @@ var browserTests = [
      "<p contenteditable=\"false\"><span contenteditable=\"\">a<br>f</span></p>"],
     [true],
     {"forwarddelete":[false,false,"",false,false,""]}],
 ["<p contenteditable=\"false\"><unknown-element contenteditable>[abc]</unknown-element></p>",
     [["forwarddelete",""]],
     "<p contenteditable=\"false\"><unknown-element contenteditable=\"\"></unknown-element></p>",
     [true],
     {"forwarddelete":[false,false,"",false,false,""]}],
+["<div contenteditable=\"false\"><custom-element contenteditable=\"\"><p>[ab</p><p>c]d</p></custom-element></div>",
+    [["forwarddelete",""]],
+    ["<div contenteditable=\"false\"><custom-element contenteditable=\"\"><p>d</p></custom-element></div>",
+     "<div contenteditable=\"false\"><custom-element contenteditable=\"\"><p>d<br></p></custom-element></div>"],
+    [true],
+    {"forwarddelete":[false,false,"",false,false,""]}],
+["<div contenteditable=\"false\"><custom-element contenteditable=\"\"><p>a[b</p><p>cd]</p></custom-element></div>",
+    [["forwarddelete",""]],
+    ["<div contenteditable=\"false\"><custom-element contenteditable=\"\"><p>a</p></custom-element></div>",
+     "<div contenteditable=\"false\"><custom-element contenteditable=\"\"><p>a<br></p></custom-element></div>"],
+    [true],
+    {"forwarddelete":[false,false,"",false,false,""]}],
+["<div contenteditable=\"false\"><custom-element contenteditable=\"\"><p><b>[ab</b></p><p><i>c]d</i></p></custom-element></div>",
+    [["forwarddelete",""]],
+    ["<div contenteditable=\"false\"><custom-element contenteditable=\"\"><p><i>d</i></p></custom-element></div>",
+     "<div contenteditable=\"false\"><custom-element contenteditable=\"\"><p><i>d</i><br></p></custom-element></div>"],
+    [true],
+    {"forwarddelete":[false,false,"",false,false,""]}],
+["<div contenteditable=\"false\"><custom-element contenteditable=\"\"><p><b>a[b</b></p><p><i>cd]</i></p></custom-element></div>",
+    [["forwarddelete",""]],
+    ["<div contenteditable=\"false\"><custom-element contenteditable=\"\"><p><b>a</b></p></custom-element></div>",
+     "<div contenteditable=\"false\"><custom-element contenteditable=\"\"><p><b>a</b><br></p></custom-element></div>"],
+    [true],
+    {"forwarddelete":[false,false,"",false,false,""]}],
 ]
--- a/toolkit/actors/PictureInPictureChild.jsm
+++ b/toolkit/actors/PictureInPictureChild.jsm
@@ -1399,19 +1399,19 @@ class PictureInPictureChild extends JSWi
     let originatingDocumentURI = originatingDoc.documentURI;
 
     let overrides = gSiteOverrides.find(([matcher]) => {
       return matcher.matches(originatingDocumentURI);
     });
 
     // gSiteOverrides is a list of tuples where the first element is the MatchPattern
     // for a supported site and the second is the actual overrides object for it.
-    // TODO: Remove DEBUG and NIGHTLY_BUILD checks (see Bug 1750998).
+    // TODO: Remove NIGHTLY_BUILD check (see Bug 1751793).
     let wrapperPath =
-      AppConstants.NIGHTLY_BUILD && !AppConstants.DEBUG && overrides
+      AppConstants.NIGHTLY_BUILD && overrides
         ? overrides[1].videoWrapperScriptPath
         : null;
     this.videoWrapper = new PictureInPictureChildVideoWrapper(
       wrapperPath,
       originatingVideo
     );
   }
 
--- a/toolkit/components/glean/bindings/private/Counter.cpp
+++ b/toolkit/components/glean/bindings/private/Counter.cpp
@@ -14,24 +14,26 @@
 #include "nsIClassInfoImpl.h"
 
 namespace mozilla::glean {
 
 namespace impl {
 
 void CounterMetric::Add(int32_t aAmount) const {
   auto scalarId = ScalarIdForMetric(mId);
-  if (scalarId) {
-    Telemetry::ScalarAdd(scalarId.extract(), aAmount);
-  } else if (IsSubmetricId(mId)) {
-    auto lock = GetLabeledMirrorLock();
-    auto tuple = lock.ref()->MaybeGet(mId);
-    if (tuple && aAmount > 0) {
-      Telemetry::ScalarSet(Get<0>(tuple.ref()), Get<1>(tuple.ref()),
-                           (uint32_t)aAmount);
+  if (aAmount >= 0) {
+    if (scalarId) {
+      Telemetry::ScalarAdd(scalarId.extract(), aAmount);
+    } else if (IsSubmetricId(mId)) {
+      auto lock = GetLabeledMirrorLock();
+      auto tuple = lock.ref()->MaybeGet(mId);
+      if (tuple && aAmount > 0) {
+        Telemetry::ScalarSet(Get<0>(tuple.ref()), Get<1>(tuple.ref()),
+                             (uint32_t)aAmount);
+      }
     }
   }
   fog_counter_add(mId, aAmount);
 }
 
 Result<Maybe<int32_t>, nsCString> CounterMetric::TestGetValue(
     const nsACString& aPingName) const {
   nsCString err;
--- a/toolkit/components/glean/bindings/private/Denominator.cpp
+++ b/toolkit/components/glean/bindings/private/Denominator.cpp
@@ -14,17 +14,17 @@
 #include "nsIClassInfoImpl.h"
 
 namespace mozilla::glean {
 
 namespace impl {
 
 void DenominatorMetric::Add(int32_t aAmount) const {
   auto scalarId = ScalarIdForMetric(mId);
-  if (scalarId) {
+  if (scalarId && aAmount >= 0) {
     Telemetry::ScalarAdd(scalarId.extract(), aAmount);
   }
   fog_denominator_add(mId, aAmount);
 }
 
 Result<Maybe<int32_t>, nsCString> DenominatorMetric::TestGetValue(
     const nsACString& aPingName) const {
   nsCString err;
--- a/toolkit/components/glean/bindings/private/Numerator.cpp
+++ b/toolkit/components/glean/bindings/private/Numerator.cpp
@@ -14,17 +14,17 @@
 #include "nsIClassInfoImpl.h"
 
 namespace mozilla::glean {
 
 namespace impl {
 
 void NumeratorMetric::AddToNumerator(int32_t aAmount) const {
   auto scalarId = ScalarIdForMetric(mId);
-  if (scalarId) {
+  if (scalarId && aAmount >= 0) {
     Telemetry::ScalarAdd(scalarId.extract(), u"numerator"_ns, aAmount);
   }
   fog_numerator_add_to_numerator(mId, aAmount);
 }
 
 Result<Maybe<std::pair<int32_t, int32_t>>, nsCString>
 NumeratorMetric::TestGetValue(const nsACString& aPingName) const {
   nsCString err;
--- a/toolkit/components/glean/bindings/private/Quantity.cpp
+++ b/toolkit/components/glean/bindings/private/Quantity.cpp
@@ -14,20 +14,24 @@
 #include "nsString.h"
 
 namespace mozilla::glean {
 
 namespace impl {
 
 void QuantityMetric::Set(int64_t aValue) const {
   auto scalarId = ScalarIdForMetric(mId);
-  if (scalarId) {
-    Telemetry::ScalarSet(scalarId.extract(), static_cast<uint32_t>(aValue));
+  if (scalarId && aValue >= 0) {
+    uint32_t theValue = static_cast<uint32_t>(aValue);
+    if (aValue > std::numeric_limits<uint32_t>::max()) {
+      theValue = std::numeric_limits<uint32_t>::max();
+    }
+    Telemetry::ScalarSet(scalarId.extract(), theValue);
   }
-  fog_quantity_set(mId, int(aValue));
+  fog_quantity_set(mId, aValue);
 }
 
 Result<Maybe<int64_t>, nsCString> QuantityMetric::TestGetValue(
     const nsACString& aPingName) const {
   nsCString err;
   if (fog_quantity_test_get_error(mId, &aPingName, &err)) {
     return Err(err);
   }
--- a/toolkit/components/glean/bindings/private/Rate.cpp
+++ b/toolkit/components/glean/bindings/private/Rate.cpp
@@ -14,25 +14,25 @@
 #include "nsIClassInfoImpl.h"
 
 namespace mozilla::glean {
 
 namespace impl {
 
 void RateMetric::AddToNumerator(int32_t aAmount) const {
   auto scalarId = ScalarIdForMetric(mId);
-  if (scalarId) {
+  if (scalarId && aAmount >= 0) {
     Telemetry::ScalarAdd(scalarId.extract(), u"numerator"_ns, aAmount);
   }
   fog_rate_add_to_numerator(mId, aAmount);
 }
 
 void RateMetric::AddToDenominator(int32_t aAmount) const {
   auto scalarId = ScalarIdForMetric(mId);
-  if (scalarId) {
+  if (scalarId && aAmount >= 0) {
     Telemetry::ScalarAdd(scalarId.extract(), u"denominator"_ns, aAmount);
   }
   fog_rate_add_to_denominator(mId, aAmount);
 }
 
 Result<Maybe<std::pair<int32_t, int32_t>>, nsCString> RateMetric::TestGetValue(
     const nsACString& aPingName) const {
   nsCString err;
--- a/toolkit/components/glean/bindings/private/Timespan.cpp
+++ b/toolkit/components/glean/bindings/private/Timespan.cpp
@@ -31,19 +31,24 @@ void TimespanMetric::Start() const {
 
 void TimespanMetric::Stop() const {
   auto optScalarId = ScalarIdForMetric(mId);
   if (optScalarId) {
     auto scalarId = optScalarId.extract();
     auto lock = GetTimesToStartsLock();
     auto optStart = lock.ref()->Extract(scalarId);
     if (!NS_WARN_IF(!optStart)) {
-      uint32_t delta = static_cast<uint32_t>(
-          (TimeStamp::Now() - optStart.extract()).ToMilliseconds());
-      Telemetry::ScalarSet(scalarId, delta);
+      double delta = (TimeStamp::Now() - optStart.extract()).ToMilliseconds();
+      uint32_t theDelta = static_cast<uint32_t>(delta);
+      if (delta > std::numeric_limits<uint32_t>::max()) {
+        theDelta = std::numeric_limits<uint32_t>::max();
+      } else if (MOZ_UNLIKELY(delta < 0)) {
+        theDelta = 0;
+      }
+      Telemetry::ScalarSet(scalarId, theDelta);
     }
   }
   fog_timespan_stop(mId);
 }
 
 void TimespanMetric::Cancel() const {
   auto optScalarId = ScalarIdForMetric(mId);
   if (optScalarId) {
--- a/toolkit/components/glean/docs/user/gifft.md
+++ b/toolkit/components/glean/docs/user/gifft.md
@@ -155,8 +155,46 @@ Telemetry Events must be enabled before 
 `Telemetry.setEventRecordingEnabled(category, enable);`.
 If the Telemetry Event isn't enabled,
 recording to the Glean event will still work,
 and the event will be Summarized in Telemetry as all disabled events are.
 
 See
 [the Telemetry Event docs](/toolkit/components/telemetry/collection/events.rst)
 for details on how disabled Telemetry Events behave.
+
+### Numeric Values
+
+The arguments and storage formats for Glean's numeric types
+(`counter`, `labeled_counter`, `quantity`, `rate`, and `timespan`)
+are different from Telemetry's numeric type
+(Scalar of kind `uint`).
+
+This results in a few notable differences.
+
+#### Saturation and Overflow
+
+`counter`, `labeled_counter`, and `rate` metrics are stored as 32-bit signed values.
+`quantity` metrics are stored as 64-bit signed values.
+All of these Glean numeric metric types saturate at their maximum representable value.
+
+Scalars of kind `uint` are stored as 32-bit unsigned values.
+They will overflow if they exceed the value $2^{32} - 1$.
+
+If a Glean numeric type saturates, it will record an error of type `invalid_overflow`.
+In your analyses please check for these errors.
+
+#### Quantity Value Over-size
+
+Values greater than $2^{32} - 1$ passed to a `quantity` metric's
+`set()` method will be clamped to $2^{32} - 1$ before being passed to the metric's Telemetry mirror.
+
+#### Negative Values
+
+Values less than 0 passed to any numeric metric type's API will not be passed on to the Telemetry mirror.
+This avoids small negative numbers being cast into a stunningly large numbers,
+and keeps the Telemetry mirror's value closer to that of the Glean metric.
+
+#### Long Time Spans
+
+If the number of milliseconds between calls to a
+`timespan` metric's `start()` and `stop()` methods exceeds $2^{32} - 1$,
+the value passed to the metric's Telemetry mirror will be clamped to $2^{32} - 1$.
--- a/toolkit/components/glean/ipc/FOGIPC.cpp
+++ b/toolkit/components/glean/ipc/FOGIPC.cpp
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "FOGIPC.h"
 
+#include <limits>
 #include "mozilla/glean/fog_ffi_generated.h"
 #include "mozilla/glean/GleanMetrics.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/gfx/GPUChild.h"
 #include "mozilla/gfx/GPUParent.h"
 #include "mozilla/gfx/GPUProcessManager.h"
@@ -35,38 +36,43 @@ using FlushFOGDataPromise = mozilla::dom
 
 namespace mozilla {
 namespace glean {
 
 static void RecordPowerMetrics() {
   static uint64_t previousCpuTime = 0, previousGpuTime = 0;
 
   uint64_t cpuTime;
-  if (NS_FAILED(GetCpuTimeSinceProcessStartInMs(&cpuTime))) {
-    return;
-  }
+  if (NS_SUCCEEDED(GetCpuTimeSinceProcessStartInMs(&cpuTime)) &&
+      cpuTime > previousCpuTime) {
+    uint64_t newCpuTime = cpuTime - previousCpuTime;
+    previousCpuTime += newCpuTime;
 
-  uint64_t newCpuTime = cpuTime - previousCpuTime;
-  previousCpuTime += newCpuTime;
-
-  if (newCpuTime) {
     // The counters are reset at least once a day. Assuming all cores are used
     // continuously, an int32 can hold the data for 24.85 cores.
     // This should be fine for now, but may overflow in the future.
-    power::total_cpu_time_ms.Add(int32_t(newCpuTime));
+    // Bug 1751277 tracks a newer, bigger counter.
+    int32_t nNewCpuTime = int32_t(newCpuTime);
+    if (newCpuTime > std::numeric_limits<int32_t>::max()) {
+      nNewCpuTime = std::numeric_limits<int32_t>::max();
+    }
+    power::total_cpu_time_ms.Add(nNewCpuTime);
   }
 
   uint64_t gpuTime;
-  if (NS_SUCCEEDED(GetGpuTimeSinceProcessStartInMs(&gpuTime))) {
+  if (NS_SUCCEEDED(GetGpuTimeSinceProcessStartInMs(&gpuTime)) &&
+      gpuTime > previousGpuTime) {
     uint64_t newGpuTime = gpuTime - previousGpuTime;
     previousGpuTime += newGpuTime;
 
-    if (newGpuTime) {
-      power::total_gpu_time_ms.Add(int32_t(newGpuTime));
+    int32_t nNewGpuTime = int32_t(newGpuTime);
+    if (newGpuTime > std::numeric_limits<int32_t>::max()) {
+      nNewGpuTime = std::numeric_limits<int32_t>::max();
     }
+    power::total_gpu_time_ms.Add(int32_t(nNewGpuTime));
   }
 }
 
 /**
  * Flush your data ASAP, either because the parent process is asking you to
  * or because the process is about to shutdown.
  *
  * @param aResolver - The function you need to call with the bincoded,
--- a/toolkit/components/glean/tests/xpcshell/test_GIFFT.js
+++ b/toolkit/components/glean/tests/xpcshell/test_GIFFT.js
@@ -15,21 +15,23 @@ const { TelemetryTestUtils } = ChromeUti
 const Telemetry = Services.telemetry;
 
 function sleep(ms) {
   /* eslint-disable mozilla/no-arbitrary-setTimeout */
   return new Promise(resolve => setTimeout(resolve, ms));
 }
 
 function scalarValue(aScalarName) {
-  return Telemetry.getSnapshotForScalars().parent[aScalarName];
+  let snapshot = Telemetry.getSnapshotForScalars();
+  return "parent" in snapshot ? snapshot.parent[aScalarName] : undefined;
 }
 
 function keyedScalarValue(aScalarName) {
-  return Telemetry.getSnapshotForKeyedScalars().parent[aScalarName];
+  let snapshot = Telemetry.getSnapshotForKeyedScalars();
+  return "parent" in snapshot ? snapshot.parent[aScalarName] : undefined;
 }
 
 add_task(function test_setup() {
   // FOG needs a profile directory to put its data in.
   do_get_profile();
 
   Services.prefs.setBoolPref(
     "toolkit.telemetry.testing.overrideProductsCheck",
@@ -339,8 +341,118 @@ add_task(function test_gifft_rate() {
     { numerator: 22, denominator: 7 },
     Glean.testOnlyIpc.irate.testGetValue()
   );
   Assert.deepEqual(
     { numerator: 22, denominator: 7 },
     keyedScalarValue("telemetry.test.mirror_for_rate")
   );
 });
+
+add_task(
+  {
+    // bug 1670259 to see if we can implement `testResetFOG` on Android.
+    skip_if: () => AppConstants.platform == "android",
+  },
+  function test_gifft_numeric_limits() {
+    // Glean and Telemetry don't share the same storage sizes or signedness.
+    // Check the edges.
+
+    // 0) Reset everything
+    Services.fog.testResetFOG();
+    Services.telemetry.getSnapshotForHistograms("main", true /* aClearStore */);
+    Services.telemetry.getSnapshotForScalars("main", true /* aClearStore */);
+    Services.telemetry.getSnapshotForKeyedScalars(
+      "main",
+      true /* aClearStore */
+    );
+
+    // 1) Counter: i32 (saturates), mirrored to uint Scalar: u32 (overflows)
+    // 1.1) Negative parameters refused.
+    Glean.testOnlyIpc.aCounter.add(-20);
+    // Unfortunately we can't check what the error was, due to API design.
+    // (chutten blames chutten for his shortsightedness)
+    Assert.throws(
+      () => Glean.testOnlyIpc.aCounter.testGetValue(),
+      /NS_ERROR_LOSS_OF_SIGNIFICANT_DATA/,
+      "Can't get the value when you're error'd"
+    );
+    Assert.equal(undefined, scalarValue("telemetry.test.unsigned_int_kind"));
+    // Clear the error state
+    Services.fog.testResetFOG();
+
+    // 1.2) Values that sum larger than u32::max saturate (counter) and overflow (Scalar)
+    // Sums to 2^32 + 1
+    Glean.testOnlyIpc.aCounter.add(Math.pow(2, 31) - 1);
+    Glean.testOnlyIpc.aCounter.add(1);
+    Glean.testOnlyIpc.aCounter.add(Math.pow(2, 31) - 1);
+    Glean.testOnlyIpc.aCounter.add(2);
+    // Glean doesn't actually throw on saturation (bug 1751469),
+    // so we can just check the saturation value.
+    Assert.equal(
+      Math.pow(2, 31) - 1,
+      Glean.testOnlyIpc.aCounter.testGetValue()
+    );
+    // Telemetry will have wrapped around to 1
+    Assert.equal(1, scalarValue("telemetry.test.unsigned_int_kind"));
+
+    // 2) Quantity: i64 (saturates), mirrored to uint Scalar: u32 (overflows)
+    // 2.1) Negative parameters refused.
+    Glean.testOnly.meaningOfLife.set(-42);
+    // Glean will error on this.
+    Assert.throws(
+      () => Glean.testOnly.meaningOfLife.testGetValue(),
+      /NS_ERROR_LOSS_OF_SIGNIFICANT_DATA/,
+      "Can't get the value when you're error'd"
+    );
+    // GIFFT doesn't tell Telemetry about the weird value at all.
+    Assert.equal(undefined, scalarValue("telemetry.test.mirror_for_quantity"));
+    // Clear the error state
+    Services.fog.testResetFOG();
+
+    // 2.2) A parameter larger than u32::max is passed to Glean unchanged,
+    //      but is clamped to u32::max before being passed to Telemetry.
+    Glean.testOnly.meaningOfLife.set(Math.pow(2, 32));
+    Assert.equal(Math.pow(2, 32), Glean.testOnly.meaningOfLife.testGetValue());
+    Assert.equal(
+      Math.pow(2, 32) - 1,
+      scalarValue("telemetry.test.mirror_for_quantity")
+    );
+
+    // 3) Rate: two i32 (saturates), mirrored to keyed uint Scalar: u32s (overflow)
+    // 3.1) Negative parameters refused.
+    Glean.testOnlyIpc.irate.addToNumerator(-22);
+    Glean.testOnlyIpc.irate.addToDenominator(7);
+    Assert.throws(
+      () => Glean.testOnlyIpc.irate.testGetValue(),
+      /NS_ERROR_LOSS_OF_SIGNIFICANT_DATA/,
+      "Can't get the value when you're error'd"
+    );
+    Assert.deepEqual(
+      { denominator: 7 },
+      keyedScalarValue("telemetry.test.mirror_for_rate")
+    );
+    // Clear the error state
+    Services.fog.testResetFOG();
+    // Clear the partial Telemetry value
+    Services.telemetry.getSnapshotForKeyedScalars(
+      "main",
+      true /* aClearStore */
+    );
+
+    // Now the denominator:
+    Glean.testOnlyIpc.irate.addToNumerator(22);
+    Glean.testOnlyIpc.irate.addToDenominator(-7);
+    Assert.throws(
+      () => Glean.testOnlyIpc.irate.testGetValue(),
+      /NS_ERROR_LOSS_OF_SIGNIFICANT_DATA/,
+      "Can't get the value when you're error'd"
+    );
+    Assert.deepEqual(
+      { numerator: 22 },
+      keyedScalarValue("telemetry.test.mirror_for_rate")
+    );
+
+    // 4) Timespan
+    // ( Can't overflow time without finding a way to get TimeStamp to think
+    // we're 2^32 milliseconds later without waiting a month )
+  }
+);
--- a/toolkit/components/normandy/test/browser/browser_ClientEnvironment.js
+++ b/toolkit/components/normandy/test/browser/browser_ClientEnvironment.js
@@ -67,22 +67,27 @@ add_task(async function testDistribution
   // distribution id defaults to "default"
   is(
     ClientEnvironment.distribution,
     "default",
     "distribution has a default value"
   );
 
   // distribution id is read from a preference
-  await SpecialPowers.pushPrefEnv({ set: [["distribution.id", "funnelcake"]] });
+  Services.prefs
+    .getDefaultBranch(null)
+    .setStringPref("distribution.id", "funnelcake");
   is(
     ClientEnvironment.distribution,
     "funnelcake",
     "distribution is read from preferences"
   );
+  Services.prefs
+    .getDefaultBranch(null)
+    .setStringPref("distribution.id", "default");
 });
 
 const mockClassify = { country: "FR", request_time: new Date(2017, 1, 1) };
 add_task(
   ClientEnvironment.withMockClassify(
     mockClassify,
     async function testCountryRequestTime() {
       // Test that country and request_time pull their data from the server.
--- a/toolkit/components/telemetry/app/TelemetryEnvironment.jsm
+++ b/toolkit/components/telemetry/app/TelemetryEnvironment.jsm
@@ -1795,32 +1795,32 @@ EnvironmentCache.prototype = {
     }
   },
 
   /**
    * Get the partner data in object form.
    * @return Object containing the partner data.
    */
   _getPartner() {
+    let defaults = Services.prefs.getDefaultBranch(null);
     let partnerData = {
-      distributionId: Services.prefs.getStringPref(PREF_DISTRIBUTION_ID, null),
-      distributionVersion: Services.prefs.getStringPref(
+      distributionId: defaults.getStringPref(PREF_DISTRIBUTION_ID, null),
+      distributionVersion: defaults.getCharPref(
         PREF_DISTRIBUTION_VERSION,
         null
       ),
-      partnerId: Services.prefs.getStringPref(PREF_PARTNER_ID, null),
-      distributor: Services.prefs.getStringPref(PREF_DISTRIBUTOR, null),
-      distributorChannel: Services.prefs.getStringPref(
-        PREF_DISTRIBUTOR_CHANNEL,
-        null
-      ),
+      partnerId: defaults.getCharPref(PREF_PARTNER_ID, null),
+      distributor: defaults.getCharPref(PREF_DISTRIBUTOR, null),
+      distributorChannel: defaults.getCharPref(PREF_DISTRIBUTOR_CHANNEL, null),
     };
 
     // Get the PREF_APP_PARTNER_BRANCH branch and append its children to partner data.
-    let partnerBranch = Services.prefs.getBranch(PREF_APP_PARTNER_BRANCH);
+    let partnerBranch = Services.prefs.getDefaultBranch(
+      PREF_APP_PARTNER_BRANCH
+    );
     partnerData.partnerNames = partnerBranch.getChildList("");
 
     return partnerData;
   },
 
   _cpuData: null,
   /**
    * Get the CPU information.
--- a/toolkit/components/telemetry/tests/unit/TelemetryEnvironmentTesting.jsm
+++ b/toolkit/components/telemetry/tests/unit/TelemetryEnvironmentTesting.jsm
@@ -9,17 +9,17 @@ XPCOMUtils.defineLazyModuleGetters(this,
   AddonManager: "resource://gre/modules/AddonManager.jsm",
   AppConstants: "resource://gre/modules/AppConstants.jsm",
   Assert: "resource://testing-common/Assert.jsm",
   // AttributionCode is only needed for Firefox
   AttributionCode: "resource:///modules/AttributionCode.jsm",
   CommonUtils: "resource://services-common/utils.js",
   MockRegistrar: "resource://testing-common/MockRegistrar.jsm",
   OS: "resource://gre/modules/osfile.jsm",
-  Preferences: "resource://gre/modules/Preferences.jsm",
+  Services: "resource://gre/modules/Services.jsm",
 });
 
 var EXPORTED_SYMBOLS = ["TelemetryEnvironmentTesting"];
 
 const gIsWindows = AppConstants.platform == "win";
 const gIsMac = AppConstants.platform == "macosx";
 const gIsAndroid = AppConstants.platform == "android";
 const gIsLinux = AppConstants.platform == "linux";
@@ -142,17 +142,19 @@ var TelemetryEnvironmentTesting = {
     prefsToSpoof["distribution.version"] = DISTRIBUTION_VERSION;
     prefsToSpoof["app.distributor"] = DISTRIBUTOR_NAME;
     prefsToSpoof["app.distributor.channel"] = DISTRIBUTOR_CHANNEL;
     prefsToSpoof["app.partner.test"] = PARTNER_NAME;
     prefsToSpoof["mozilla.partner.id"] = PARTNER_ID;
 
     // Spoof the preferences.
     for (let pref in prefsToSpoof) {
-      Preferences.set(pref, prefsToSpoof[pref]);
+      Services.prefs
+        .getDefaultBranch(null)
+        .setStringPref(pref, prefsToSpoof[pref]);
     }
   },
 
   async spoofAttributionData() {
     if (gIsWindows || gIsMac) {
       AttributionCode._clearCache();
       await AttributionCode.writeAttributionFile(ATTRIBUTION_CODE);
     }
--- a/toolkit/components/utils/ClientEnvironment.jsm
+++ b/toolkit/components/utils/ClientEnvironment.jsm
@@ -27,17 +27,19 @@ var EXPORTED_SYMBOLS = ["ClientEnvironme
  * so avoid adding non-getter functions as attributes, as filter expressions
  * cannot execute functions.
  *
  * Also note that, because filter expressions implicitly resolve promises, you
  * can add getter functions that return promises for async data.
  */
 class ClientEnvironmentBase {
   static get distribution() {
-    return Services.prefs.getCharPref("distribution.id", "default");
+    return Services.prefs
+      .getDefaultBranch(null)
+      .getCharPref("distribution.id", "default");
   }
 
   static get telemetry() {
     return (async () => {
       const pings = await TelemetryArchive.promiseArchivedPingList();
 
       // get most recent ping per type
       const mostRecentPings = {};
--- a/toolkit/modules/E10SUtils.jsm
+++ b/toolkit/modules/E10SUtils.jsm
@@ -39,26 +39,16 @@ XPCOMUtils.defineLazyPreferenceGetter(
 );
 
 XPCOMUtils.defineLazyPreferenceGetter(
   this,
   "useCrossOriginOpenerPolicy",
   "browser.tabs.remote.useCrossOriginOpenerPolicy",
   false
 );
-// Preference containing the list (comma separated) of origins that will
-// have ServiceWorkers isolated in special processes
-XPCOMUtils.defineLazyPreferenceGetter(
-  this,
-  "serviceWorkerIsolationList",
-  "browser.tabs.remote.serviceWorkerIsolationList",
-  "",
-  false,
-  val => val.split(",")
-);
 XPCOMUtils.defineLazyServiceGetter(
   this,
   "serializationHelper",
   "@mozilla.org/network/serialization-helper;1",
   "nsISerializationHelper"
 );
 XPCOMUtils.defineLazyServiceGetter(
   this,
@@ -245,20 +235,17 @@ function validatedWebRemoteType(
         `${WEB_REMOTE_COOP_COEP_TYPE_PREFIX}${targetPrincipal.siteOrigin}`
       )
     ) {
       return aPreferredRemoteType;
     }
 
     if (
       aIsWorker &&
-      aWorkerType === Ci.nsIE10SUtils.REMOTE_WORKER_TYPE_SERVICE &&
-      serviceWorkerIsolationList.some(function(val) {
-        return targetPrincipal.siteOriginNoSuffix == val;
-      })
+      aWorkerType === Ci.nsIE10SUtils.REMOTE_WORKER_TYPE_SERVICE
     ) {
       return `${SERVICEWORKER_REMOTE_TYPE}=${targetPrincipal.siteOrigin}`;
     }
     return `${FISSION_WEB_REMOTE_TYPE}=${targetPrincipal.siteOrigin}`;
     // else fall through and probably return WEB_REMOTE_TYPE
   }
 
   if (!aPreferredRemoteType) {
@@ -284,16 +271,17 @@ var E10SUtils = {
   WEB_REMOTE_TYPE,
   WEB_REMOTE_COOP_COEP_TYPE_PREFIX,
   FILE_REMOTE_TYPE,
   EXTENSION_REMOTE_TYPE,
   PRIVILEGEDABOUT_REMOTE_TYPE,
   PRIVILEGEDMOZILLA_REMOTE_TYPE,
   LARGE_ALLOCATION_REMOTE_TYPE,
   FISSION_WEB_REMOTE_TYPE,
+  SERVICEWORKER_REMOTE_TYPE,
 
   /**
    * @param aURI The URI of the about page
    * @return The instance of the nsIAboutModule related to this uri
    */
   getAboutModule(aURL) {
     // Needs to match NS_GetAboutModuleName
     let moduleName = aURL.pathQueryRef.replace(/[#?].*/, "").toLowerCase();
--- a/toolkit/modules/tests/xpcshell/test_E10SUtils_workers_remote_types.js
+++ b/toolkit/modules/tests/xpcshell/test_E10SUtils_workers_remote_types.js
@@ -39,16 +39,17 @@ const {
   DEFAULT_REMOTE_TYPE,
   EXTENSION_REMOTE_TYPE,
   FILE_REMOTE_TYPE,
   FISSION_WEB_REMOTE_TYPE,
   LARGE_ALLOCATION_REMOTE_TYPE,
   NOT_REMOTE,
   PRIVILEGEDABOUT_REMOTE_TYPE,
   PRIVILEGEDMOZILLA_REMOTE_TYPE,
+  SERVICEWORKER_REMOTE_TYPE,
   WEB_REMOTE_COOP_COEP_TYPE_PREFIX,
   WEB_REMOTE_TYPE,
 } = E10SUtils;
 
 const {
   REMOTE_WORKER_TYPE_SHARED,
   REMOTE_WORKER_TYPE_SERVICE,
 } = Ci.nsIE10SUtils;
@@ -72,17 +73,17 @@ add_task(function test_get_remote_type_f
   }
 
   // ServiceWorker test cases:
   // - e10s + fission disabled:
   //   - extension principal + any preferred remote type => extension remote type
   //   - content principal + any preferred remote type => web remote type
   // - fission enabled:
   //   - extension principal + any preferred remote type => extension remote type
-  //   - content principal + any preferred remote type => webIsolated=siteOrigin remote type
+  //   - content principal + any preferred remote type => webServiceWorker=siteOrigin remote type
   function* getTestCase(fission = false) {
     const TEST_PRINCIPALS = [
       principalSecureCom,
       principalSecureOrg,
       principalExtension,
     ];
 
     const PREFERRED_REMOTE_TYPES = [
@@ -124,17 +125,17 @@ add_task(function test_get_remote_type_f
       expected,
       msg
     );
   }
 
   // Test cases for e10s mode + fission enabled.
   for (const testCase of getTestCase(true)) {
     const [msg, principal, ...args] = testCase;
-    let expected = `${FISSION_WEB_REMOTE_TYPE}=${principal.siteOrigin}`;
+    let expected = `${SERVICEWORKER_REMOTE_TYPE}=${principal.siteOrigin}`;
 
     if (principal == principalExtension) {
       expected = WebExtensionPolicy.useRemoteWebExtensions
         ? E10SUtils.EXTENSION_REMOTE_TYPE
         : E10SUtils.NOT_REMOTE;
     }
 
     equal(
--- a/widget/cocoa/nsChildView.mm
+++ b/widget/cocoa/nsChildView.mm
@@ -4478,20 +4478,28 @@ static CFTypeRefPtr<CFURLRef> GetPasteLo
 
   NS_OBJC_END_TRY_IGNORE_BLOCK;
 }
 
 #pragma mark -
 
 // Support for the "Services" menu. We currently only support sending strings
 // and HTML to system services.
-
+// This method can be called on any thread (see bug 1751687). We can only usefully
+// handle it on the main thread.
 - (id)validRequestorForSendType:(NSString*)sendType returnType:(NSString*)returnType {
   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
 
+  if (!NS_IsMainThread()) {
+    // We don't have any thread-safe ways of checking whether we can send
+    // or receive content. Just say no. In normal cases, we expect this
+    // method to be called on the main thread.
+    return [super validRequestorForSendType:sendType returnType:returnType];
+  }
+
   // sendType contains the type of data that the service would like this
   // application to send to it.  sendType is nil if the service is not
   // requesting any data.
   //
   // returnType contains the type of data the the service would like to
   // return to this application (e.g., to overwrite the selection).
   // returnType is nil if the service will not return any data.
   //
--- a/widget/gtk/nsLookAndFeel.cpp
+++ b/widget/gtk/nsLookAndFeel.cpp
@@ -1295,23 +1295,27 @@ Maybe<ColorScheme> nsLookAndFeel::Comput
   while (variant && g_variant_is_of_type(variant, G_VARIANT_TYPE_VARIANT)) {
     // Unbox the return value.
     variant = dont_AddRef(g_variant_get_variant(variant));
   }
   if (!variant || !g_variant_is_of_type(variant, G_VARIANT_TYPE_UINT32)) {
     MOZ_ASSERT(false, "Unexpected color-scheme query return value");
     return Nothing();
   }
-  if (g_variant_get_uint32(variant) == 1) {
-    return Some(ColorScheme::Dark);
+  switch (g_variant_get_uint32(variant)) {
+    default:
+      MOZ_FALLTHROUGH_ASSERT("Unexpected color-scheme query return value");
+    case 0:
+      break;
+    case 1:
+      return Some(ColorScheme::Dark);
+    case 2:
+      return Some(ColorScheme::Light);
   }
-  // If we get a valid, non-dark value from DBus, even if it's "no preference",
-  // then we need to return a light color scheme, so that we properly override
-  // it on changes.
-  return Some(ColorScheme::Light);
+  return Nothing();
 }
 
 void nsLookAndFeel::Initialize() {
   LOGLNF("nsLookAndFeel::Initialize");
   MOZ_DIAGNOSTIC_ASSERT(!mInitialized);
   MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread(),
                         "LookAndFeel init should be done on the main thread");