Merge inbound to m-c. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Wed, 29 Mar 2017 09:41:54 -0400
changeset 400820 6ea713ccc9abea93126423fefb855d0e051c95e2
parent 400738 c4392f466eaab690d9b0ef7bacf72fa5e6573007 (current diff)
parent 400819 8b964027442044f5fe933e2fc92eb6704e706798 (diff)
child 400829 4c7c05a49f3ce40aa0969beccb397380a8907f4a
child 400838 c4b366a4ce1baae72efc819942a7444736db70b4
child 400890 8efb75a46b6204eb5c4f1f3e1f3b20226b0981ab
push id1490
push usermtabara@mozilla.com
push dateMon, 31 Jul 2017 14:08:16 +0000
treeherdermozilla-release@70e32e6bf15e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone55.0a1
first release with
nightly linux32
6ea713ccc9ab / 55.0a1 / 20170329142008 / files
nightly linux64
6ea713ccc9ab / 55.0a1 / 20170329142008 / files
nightly mac
6ea713ccc9ab / 55.0a1 / 20170329071901 / files
nightly win32
6ea713ccc9ab / 55.0a1 / 20170329071901 / files
nightly win64
6ea713ccc9ab / 55.0a1 / 20170329071901 / 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 inbound to m-c. a=merge
dom/base/SiteSpecificUserAgent.js
dom/base/SiteSpecificUserAgent.manifest
dom/base/nsISiteSpecificUserAgent.idl
js/xpconnect/src/XPCLocale.cpp
layout/tools/reftest/reftest.jsm
mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
mobile/android/chrome/content/browser.js
mobile/android/components/SiteSpecificUserAgent.js
modules/libpref/init/all.js
security/nss/gtests/ssl_gtest/databuffer.h
security/nss/gtests/ssl_gtest/tls_parser.cc
security/nss/gtests/ssl_gtest/tls_parser.h
taskcluster/ci/test/test-platforms.yml
taskcluster/ci/test/test-sets.yml
taskcluster/ci/test/tests.yml
toolkit/components/telemetry/Histograms.json
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,9 +17,9 @@
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 # Are you updating CLOBBER because you think it's needed for your WebIDL
 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
 # don't change CLOBBER for WebIDL changes any more.
 
-Bug 1343682 - backing out a previous version didn't stop the failures from it, so it appears to need a clobber both out and in
+Bug 1265818 - deleted an exported header and added a new .idl to regenerate that same header; and this unfortunately produces an untracked generated header in your source dir whenever you build (probably due to a dangling symlink), unless you clobber
--- a/accessible/base/NotificationController.cpp
+++ b/accessible/base/NotificationController.cpp
@@ -862,17 +862,17 @@ NotificationController::WillRefresh(mozi
       uint64_t id = reinterpret_cast<uintptr_t>(parent->UniqueID());
       MOZ_DIAGNOSTIC_ASSERT(id);
       DocAccessibleChild* ipcDoc = childDoc->IPCDoc();
       if (ipcDoc) {
         parentIPCDoc->SendBindChildDoc(ipcDoc, id);
         continue;
       }
 
-      ipcDoc = new DocAccessibleChild(childDoc);
+      ipcDoc = new DocAccessibleChild(childDoc, parentIPCDoc->Manager());
       childDoc->SetIPCDoc(ipcDoc);
 
 #if defined(XP_WIN)
       parentIPCDoc->ConstructChildDocInParentProcess(ipcDoc, id,
                                                      AccessibleWrap::GetChildIDFor(childDoc));
 #else
       nsCOMPtr<nsITabChild> tabChild =
         do_GetInterface(mDocument->DocumentNode()->GetDocShell());
--- a/accessible/generic/DocAccessible.cpp
+++ b/accessible/generic/DocAccessible.cpp
@@ -1477,17 +1477,17 @@ DocAccessible::NotifyOfLoading(bool aIsR
 void
 DocAccessible::DoInitialUpdate()
 {
   if (nsCoreUtils::IsTabDocument(mDocumentNode)) {
     mDocFlags |= eTabDocument;
     if (IPCAccessibilityActive()) {
       nsIDocShell* docShell = mDocumentNode->GetDocShell();
       if (RefPtr<dom::TabChild> tabChild = dom::TabChild::GetFrom(docShell)) {
-        DocAccessibleChild* ipcDoc = new DocAccessibleChild(this);
+        DocAccessibleChild* ipcDoc = new DocAccessibleChild(this, tabChild);
         SetIPCDoc(ipcDoc);
 
 #if defined(XP_WIN)
         IAccessibleHolder holder(CreateHolderFromAccessible(this));
         int32_t childID = AccessibleWrap::GetChildIDFor(this);
 #else
         int32_t holder = 0, childID = 0;
 #endif
--- a/accessible/ipc/other/DocAccessibleChild.h
+++ b/accessible/ipc/other/DocAccessibleChild.h
@@ -21,20 +21,21 @@ class TableCellAccessible;
 
 /*
  * These objects handle content side communication for an accessible document,
  * and their lifetime is the same as the document they represent.
  */
 class DocAccessibleChild : public DocAccessibleChildBase
 {
 public:
-  explicit DocAccessibleChild(DocAccessible* aDoc)
+  DocAccessibleChild(DocAccessible* aDoc, IProtocol* aManager)
     : DocAccessibleChildBase(aDoc)
   {
     MOZ_COUNT_CTOR_INHERITED(DocAccessibleChild, DocAccessibleChildBase);
+    SetManager(aManager);
   }
 
   ~DocAccessibleChild()
   {
     MOZ_COUNT_DTOR_INHERITED(DocAccessibleChild, DocAccessibleChildBase);
   }
 
   /*
--- a/accessible/ipc/win/DocAccessibleChild.cpp
+++ b/accessible/ipc/win/DocAccessibleChild.cpp
@@ -11,26 +11,28 @@
 #include "mozilla/ClearOnShutdown.h"
 #include "RootAccessible.h"
 
 namespace mozilla {
 namespace a11y {
 
 static StaticAutoPtr<PlatformChild> sPlatformChild;
 
-DocAccessibleChild::DocAccessibleChild(DocAccessible* aDoc)
+DocAccessibleChild::DocAccessibleChild(DocAccessible* aDoc, IProtocol* aManager)
   : DocAccessibleChildBase(aDoc)
   , mEmulatedWindowHandle(nullptr)
   , mIsRemoteConstructed(false)
 {
   MOZ_COUNT_CTOR_INHERITED(DocAccessibleChild, DocAccessibleChildBase);
   if (!sPlatformChild) {
     sPlatformChild = new PlatformChild();
     ClearOnShutdown(&sPlatformChild, ShutdownPhase::Shutdown);
   }
+
+  SetManager(aManager);
 }
 
 DocAccessibleChild::~DocAccessibleChild()
 {
   MOZ_COUNT_DTOR_INHERITED(DocAccessibleChild, DocAccessibleChildBase);
 }
 
 void
--- a/accessible/ipc/win/DocAccessibleChild.h
+++ b/accessible/ipc/win/DocAccessibleChild.h
@@ -17,17 +17,17 @@ namespace a11y {
 
 /*
  * These objects handle content side communication for an accessible document,
  * and their lifetime is the same as the document they represent.
  */
 class DocAccessibleChild : public DocAccessibleChildBase
 {
 public:
-  explicit DocAccessibleChild(DocAccessible* aDoc);
+  DocAccessibleChild(DocAccessible* aDoc, IProtocol* aManager);
   ~DocAccessibleChild();
 
   virtual void Shutdown() override;
 
   virtual ipc::IPCResult
   RecvParentCOMProxy(const IAccessibleHolder& aParentCOMProxy) override;
   virtual ipc::IPCResult
     RecvEmulatedWindow(const WindowsHandle& aEmulatedWindowHandle,
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -356,18 +356,16 @@
 @RESPATH@/components/NetworkGeolocationProvider.manifest
 @RESPATH@/components/NetworkGeolocationProvider.js
 @RESPATH@/components/TVSimulatorService.js
 @RESPATH@/components/TVSimulatorService.manifest
 #ifdef MOZ_WEBRTC
 @RESPATH@/components/PeerConnection.js
 @RESPATH@/components/PeerConnection.manifest
 #endif
-@RESPATH@/components/SiteSpecificUserAgent.js
-@RESPATH@/components/SiteSpecificUserAgent.manifest
 @RESPATH@/components/storage-json.js
 @RESPATH@/components/crypto-SDR.js
 @RESPATH@/components/Downloads.manifest
 @RESPATH@/components/DownloadLegacy.js
 @RESPATH@/components/nsSidebar.manifest
 @RESPATH@/components/nsSidebar.js
 @RESPATH@/components/nsAsyncShutdown.manifest
 @RESPATH@/components/nsAsyncShutdown.js
--- a/browser/extensions/e10srollout/bootstrap.js
+++ b/browser/extensions/e10srollout/bootstrap.js
@@ -13,17 +13,17 @@ Cu.import("resource://gre/modules/Update
  // The amount of people to be part of e10s
 const TEST_THRESHOLD = {
   "beta": 0.5,  // 50%
   "release": 1.0,  // 100%
   "esr": 1.0,  // 100%
 };
 
 const ADDON_ROLLOUT_POLICY = {
-  "beta": "51alladdons", // Any WebExtension or addon except with mpc = false
+  "beta": "50allmpc",
   "release": "50allmpc",
   "esr": "esrA", // WebExtensions and Addons with mpc=true
 };
 
 const PREF_COHORT_SAMPLE       = "e10s.rollout.cohortSample";
 const PREF_COHORT_NAME         = "e10s.rollout.cohort";
 const PREF_E10S_OPTED_IN       = "browser.tabs.remote.autostart";
 const PREF_E10S_FORCE_ENABLED  = "browser.tabs.remote.force-enable";
@@ -79,17 +79,18 @@ function defineCohort() {
                     );
   } else {
     Preferences.reset(PREF_E10S_ADDON_POLICY);
   }
 
   let userOptedOut = optedOut();
   let userOptedIn = optedIn();
   let disqualified = (Services.appinfo.multiprocessBlockPolicy != 0);
-  let testGroup = (getUserSample() < TEST_THRESHOLD[updateChannel]);
+  let testThreshold = TEST_THRESHOLD[updateChannel];
+  let testGroup = (getUserSample() < testThreshold);
   let hasNonExemptAddon = Preferences.get(PREF_E10S_HAS_NONEXEMPT_ADDON, false);
   let temporaryDisqualification = getTemporaryDisqualification();
   let temporaryQualification = getTemporaryQualification();
 
   let cohortPrefix = "";
   if (disqualified) {
     cohortPrefix = "disqualified-";
   } else if (hasNonExemptAddon) {
@@ -106,17 +107,21 @@ function defineCohort() {
     // still be denied by the backend, which is useful so that the E10S_STATUS
     // telemetry probe can be correctly set.
 
     // For these volatile disqualification reasons, however, we must not try
     // to activate e10s because the backend doesn't know about it. E10S_STATUS
     // here will be accumulated as "2 - Disabled", which is fine too.
     setCohort(`temp-disqualified-${temporaryDisqualification}`);
     Preferences.reset(PREF_TOGGLE_E10S);
-  } else if (!disqualified && temporaryQualification != "") {
+  } else if (!disqualified && testThreshold < 1.0 &&
+             temporaryQualification != "") {
+    // Users who are qualified for e10s and on channels where some population
+    // would not receive e10s can be pushed into e10s anyway via a temporary
+    // qualification which overrides the user sample value when non-empty.
     setCohort(`temp-qualified-${temporaryQualification}`);
     Preferences.set(PREF_TOGGLE_E10S, true);
   } else if (testGroup) {
     setCohort(`${cohortPrefix}test`);
     Preferences.set(PREF_TOGGLE_E10S, true);
   } else {
     setCohort(`${cohortPrefix}control`);
     Preferences.reset(PREF_TOGGLE_E10S);
--- a/browser/extensions/e10srollout/install.rdf.in
+++ b/browser/extensions/e10srollout/install.rdf.in
@@ -5,17 +5,17 @@
 
 #filter substitution
 
 <RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
      xmlns:em="http://www.mozilla.org/2004/em-rdf#">
 
   <Description about="urn:mozilla:install-manifest">
     <em:id>e10srollout@mozilla.org</em:id>
-    <em:version>1.12</em:version>
+    <em:version>1.14</em:version>
     <em:type>2</em:type>
     <em:bootstrap>true</em:bootstrap>
     <em:multiprocessCompatible>true</em:multiprocessCompatible>
 
     <!-- Target Application this theme can install into,
         with minimum and maximum supported versions. -->
     <em:targetApplication>
       <Description>
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -382,18 +382,16 @@
 @RESPATH@/browser/components/browser-newtab.xpt
 @RESPATH@/browser/components/aboutNewTabService.js
 @RESPATH@/browser/components/NewTabComponents.manifest
 @RESPATH@/components/Downloads.manifest
 @RESPATH@/components/DownloadLegacy.js
 @RESPATH@/components/BrowserPageThumbs.manifest
 @RESPATH@/components/crashmonitor.manifest
 @RESPATH@/components/nsCrashMonitor.js
-@RESPATH@/components/SiteSpecificUserAgent.js
-@RESPATH@/components/SiteSpecificUserAgent.manifest
 @RESPATH@/components/toolkitsearch.manifest
 @RESPATH@/components/nsSearchService.js
 @RESPATH@/components/nsSearchSuggestions.js
 @RESPATH@/components/nsSidebar.js
 @RESPATH@/components/passwordmgr.manifest
 @RESPATH@/components/nsLoginInfo.js
 @RESPATH@/components/nsLoginManager.js
 @RESPATH@/components/nsLoginManagerPrompter.js
--- a/build/mach_bootstrap.py
+++ b/build/mach_bootstrap.py
@@ -114,16 +114,17 @@ MACH_MODULES = [
     'python/compare-locales/mach_commands.py',
     'python/mozboot/mozboot/mach_commands.py',
     'python/mozbuild/mozbuild/mach_commands.py',
     'python/mozbuild/mozbuild/backend/mach_commands.py',
     'python/mozbuild/mozbuild/compilation/codecomplete.py',
     'python/mozbuild/mozbuild/frontend/mach_commands.py',
     'services/common/tests/mach_commands.py',
     'taskcluster/mach_commands.py',
+    'testing/awsy/mach_commands.py',
     'testing/firefox-ui/mach_commands.py',
     'testing/mach_commands.py',
     'testing/marionette/mach_commands.py',
     'testing/mochitest/mach_commands.py',
     'testing/mozharness/mach_commands.py',
     'testing/talos/mach_commands.py',
     'testing/web-platform/mach_commands.py',
     'testing/xpcshell/mach_commands.py',
--- a/devtools/client/shared/test/helper_inplace_editor.js
+++ b/devtools/client/shared/test/helper_inplace_editor.js
@@ -47,17 +47,17 @@ function createSpan(doc) {
   info("Creating a new span element");
   let div = doc.createElementNS(HTML_NS, "div");
   let span = doc.createElementNS(HTML_NS, "span");
   span.setAttribute("tabindex", "0");
   span.style.fontSize = "11px";
   span.style.display = "inline-block";
   span.style.width = "100px";
   span.style.border = "1px solid red";
-  span.style.fontFamily = "monospace";
+  span.style.fontFamily = "Courier New";
 
   div.style.height = "100%";
   div.style.position = "absolute";
   div.appendChild(span);
 
   let parent = doc.querySelector("window") || doc.body;
   parent.appendChild(div);
   return span;
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -45,17 +45,16 @@
 #include "mozilla/dom/StorageManager.h"
 #include "mozilla/dom/TCPSocket.h"
 #include "mozilla/dom/URLSearchParams.h"
 #include "mozilla/dom/VRDisplay.h"
 #include "mozilla/dom/VRServiceTest.h"
 #include "mozilla/dom/WebAuthentication.h"
 #include "mozilla/dom/workers/RuntimeService.h"
 #include "mozilla/Hal.h"
-#include "nsISiteSpecificUserAgent.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/SSE.h"
 #include "mozilla/StaticPtr.h"
 #include "Connection.h"
 #include "mozilla/dom/Event.h" // for nsIDOMEvent::InternalDOMEvent()
 #include "nsGlobalWindow.h"
 #include "nsIIdleObserver.h"
 #include "nsIPermissionManager.h"
@@ -309,39 +308,33 @@ Navigator::Invalidate()
 //*****************************************************************************
 //    Navigator::nsIDOMNavigator
 //*****************************************************************************
 
 void
 Navigator::GetUserAgent(nsAString& aUserAgent, CallerType aCallerType,
                         ErrorResult& aRv) const
 {
-  nsCOMPtr<nsIURI> codebaseURI;
   nsCOMPtr<nsPIDOMWindowInner> window;
 
   if (mWindow) {
     window = mWindow;
     nsIDocShell* docshell = window->GetDocShell();
     nsString customUserAgent;
     if (docshell) {
       docshell->GetCustomUserAgent(customUserAgent);
 
       if (!customUserAgent.IsEmpty()) {
         aUserAgent = customUserAgent;
         return;
       }
-
-      nsIDocument* doc = mWindow->GetExtantDoc();
-      if (doc) {
-        doc->NodePrincipal()->GetURI(getter_AddRefs(codebaseURI));
-      }
     }
   }
 
-  nsresult rv = GetUserAgent(window, codebaseURI,
+  nsresult rv = GetUserAgent(window,
                              aCallerType == CallerType::System,
                              aUserAgent);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     aRv.Throw(rv);
   }
 }
 
 NS_IMETHODIMP
@@ -1880,17 +1873,17 @@ Navigator::AppName(nsAString& aAppName, 
 
 void
 Navigator::ClearUserAgentCache()
 {
   NavigatorBinding::ClearCachedUserAgentValue(this);
 }
 
 nsresult
-Navigator::GetUserAgent(nsPIDOMWindowInner* aWindow, nsIURI* aURI,
+Navigator::GetUserAgent(nsPIDOMWindowInner* aWindow,
                         bool aIsCallerChrome,
                         nsAString& aUserAgent)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (!aIsCallerChrome) {
     const nsAdoptingString& override =
       mozilla::Preferences::GetString("general.useragent.override");
@@ -1911,29 +1904,40 @@ Navigator::GetUserAgent(nsPIDOMWindowInn
   nsAutoCString ua;
   rv = service->GetUserAgent(ua);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   CopyASCIItoUTF16(ua, aUserAgent);
 
-  if (!aWindow || !aURI) {
+  if (!aWindow) {
     return NS_OK;
   }
 
   MOZ_ASSERT(aWindow->GetDocShell());
 
-  nsCOMPtr<nsISiteSpecificUserAgent> siteSpecificUA =
-    do_GetService("@mozilla.org/dom/site-specific-user-agent;1");
-  if (!siteSpecificUA) {
+  // Copy the User-Agent header from the document channel which has already been
+  // subject to UA overrides.
+  nsCOMPtr<nsIDocument> doc = aWindow->GetExtantDoc();
+  if (!doc) {
     return NS_OK;
   }
-
-  return siteSpecificUA->GetUserAgentForURIAndWindow(aURI, aWindow, aUserAgent);
+  nsCOMPtr<nsIHttpChannel> httpChannel =
+    do_QueryInterface(doc->GetChannel());
+  if (httpChannel) {
+    nsAutoCString userAgent;
+    rv = httpChannel->GetRequestHeader(NS_LITERAL_CSTRING("User-Agent"),
+                                       userAgent);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+    CopyASCIItoUTF16(userAgent, aUserAgent);
+  }
+  return NS_OK;
 }
 
 static nsCString
 ToCString(const nsString& aString)
 {
   nsCString str("'");
   str.Append(NS_ConvertUTF16toUTF8(aString));
   str.AppendLiteral("'");
--- a/dom/base/Navigator.h
+++ b/dom/base/Navigator.h
@@ -153,17 +153,16 @@ public:
 
   static nsresult GetPlatform(nsAString& aPlatform,
                               bool aUsePrefOverriddenValue);
 
   static nsresult GetAppVersion(nsAString& aAppVersion,
                                 bool aUsePrefOverriddenValue);
 
   static nsresult GetUserAgent(nsPIDOMWindowInner* aWindow,
-                               nsIURI* aURI,
                                bool aIsCallerChrome,
                                nsAString& aUserAgent);
 
   // Clears the user agent cache by calling:
   // NavigatorBinding::ClearCachedUserAgentValue(this);
   void ClearUserAgentCache();
 
   bool Vibrate(uint32_t aDuration);
deleted file mode 100644
--- a/dom/base/SiteSpecificUserAgent.js
+++ /dev/null
@@ -1,88 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-const Cu = Components.utils;
-const Cc = Components.classes;
-const Ci = Components.interfaces;
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-
-const MAX_CACHE_SIZE      = 250;
-const PREF_UPDATE         = "general.useragent.updates.";
-const PREF_OVERRIDE       = "general.useragent.override.";
-const XPCOM_SHUTDOWN      = "xpcom-shutdown";
-const HTTP_PROTO_HANDLER = Cc["@mozilla.org/network/protocol;1?name=http"]
-                             .getService(Ci.nsIHttpProtocolHandler);
-
-XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
-  "@mozilla.org/childprocessmessagemanager;1",
-  "nsISyncMessageSender");
-
-function SiteSpecificUserAgent() {
-  this.inParent = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime)
-    .processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
-
-  if (this.inParent) {
-    Cu.import("resource://gre/modules/UserAgentOverrides.jsm");
-  } else {
-    Cu.import("resource://gre/modules/Services.jsm");
-    Services.prefs.addObserver(PREF_OVERRIDE, this, false);
-    Services.prefs.addObserver(PREF_UPDATE, this, false);
-    Services.obs.addObserver(this, XPCOM_SHUTDOWN, false);
-    this.userAgentCache = new Map;
-  }
-}
-
-SiteSpecificUserAgent.prototype = {
-  getUserAgentForURIAndWindow: function ssua_getUserAgentForURIAndWindow(aURI, aWindow) {
-    if (this.inParent) {
-      return UserAgentOverrides.getOverrideForURI(aURI) || HTTP_PROTO_HANDLER.userAgent;
-    }
-
-    let host = aURI.asciiHost;
-    let cachedResult = this.userAgentCache.get(host);
-    if (cachedResult) {
-      return cachedResult;
-    }
-
-    let data = { uri: aURI.spec };
-    let result = cpmm.sendRpcMessage("Useragent:GetOverride", data)[0] || HTTP_PROTO_HANDLER.userAgent;
-
-    if (this.userAgentCache.size >= MAX_CACHE_SIZE) {
-      this.userAgentCache.clear();
-    }
-
-    this.userAgentCache.set(host, result);
-    return result;
-  },
-
-  invalidateCache: function() {
-    this.userAgentCache.clear();
-  },
-
-  clean: function() {
-    this.userAgentCache.clear();
-    if (!this.inParent) {
-      Services.obs.removeObserver(this, XPCOM_SHUTDOWN);
-      Services.prefs.removeObserver(PREF_OVERRIDE, this);
-      Services.prefs.removeObserver(PREF_UPDATE, this);
-    }
-  },
-
-  observe: function(subject, topic, data) {
-    switch (topic) {
-      case "nsPref:changed":
-        this.invalidateCache();
-        break;
-      case XPCOM_SHUTDOWN:
-        this.clean();
-        break;
-    }
-  },
-
-  classID: Components.ID("{506c680f-3d1c-4954-b351-2c80afbc37d3}"),
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsISiteSpecificUserAgent])
-};
-
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory([SiteSpecificUserAgent]);
deleted file mode 100644
--- a/dom/base/SiteSpecificUserAgent.manifest
+++ /dev/null
@@ -1,2 +0,0 @@
-component {506c680f-3d1c-4954-b351-2c80afbc37d3} SiteSpecificUserAgent.js
-contract @mozilla.org/dom/site-specific-user-agent;1 {506c680f-3d1c-4954-b351-2c80afbc37d3}
--- a/dom/base/StructuredCloneHolder.cpp
+++ b/dom/base/StructuredCloneHolder.cpp
@@ -303,16 +303,17 @@ StructuredCloneHolder::Read(nsISupports*
     JS_ClearPendingException(aCx);
     aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
     return;
   }
 
   // If we are tranferring something, we cannot call 'Read()' more than once.
   if (mSupportsTransferring) {
     mBlobImplArray.Clear();
+    mWasmModuleArray.Clear();
     mClonedSurfaces.Clear();
     Clear();
   }
 }
 
 void
 StructuredCloneHolder::ReadFromBuffer(nsISupports* aParent,
                                       JSContext* aCx,
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -29,17 +29,16 @@ XPIDL_SOURCES += [
     'nsIScriptChannel.idl',
     'nsIScriptLoaderObserver.idl',
     'nsISelection.idl',
     'nsISelectionController.idl',
     'nsISelectionDisplay.idl',
     'nsISelectionListener.idl',
     'nsISelectionPrivate.idl',
     'nsISimpleContentPolicy.idl',
-    'nsISiteSpecificUserAgent.idl',
     'nsISlowScriptDebug.idl',
 ]
 
 XPIDL_MODULE = 'dom'
 
 EXPORTS += [
     'AutocompleteFieldList.h',
     'Crypto.h',
@@ -413,23 +412,16 @@ EXTRA_COMPONENTS += [
     'messageWakeupService.js',
     'messageWakeupService.manifest',
     'ProcessSelector.js',
     'ProcessSelector.manifest',
     'SlowScriptDebug.js',
     'SlowScriptDebug.manifest',
 ]
 
-# Firefox for Android provides an alternate version of this component
-if CONFIG['MOZ_BUILD_APP'] != 'mobile/android':
-    EXTRA_COMPONENTS += [
-        'SiteSpecificUserAgent.js',
-        'SiteSpecificUserAgent.manifest',
-    ]
-
 EXTRA_JS_MODULES += [
     'DOMRequestHelper.jsm',
     'IndexedDBHelper.jsm',
 ]
 
 LOCAL_INCLUDES += [
     '../battery',
     '../events',
deleted file mode 100644
--- a/dom/base/nsISiteSpecificUserAgent.idl
+++ /dev/null
@@ -1,28 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "nsISupports.idl"
-
-interface nsIURI;
-interface mozIDOMWindow;
-
-/**
- * nsISiteSpecificUserAgent provides you with site/window-specific User Agent strings.
- */
-
-[scriptable, uuid(0f0ace30-9ab1-4175-9d60-fd26c0324adc)]
-interface nsISiteSpecificUserAgent : nsISupports
-{
-  /**
-   * Get the User Agent string for a given URI.
-   *
-   * @param aURI is the URI of the page the UA string is used for.
-   *
-   * @param aWindow is the window this UA is being requested for
-   *
-   * @returns the User Agent string for the given URI. If no override applies,
-   * the default User Agent string is used.
-   */
-  AString getUserAgentForURIAndWindow(in nsIURI aURI, in mozIDOMWindow aWindow);
-};
--- a/dom/events/test/test_continuous_wheel_events.html
+++ b/dom/events/test/test_continuous_wheel_events.html
@@ -4,17 +4,17 @@
   <title>Test for D3E WheelEvent</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
   <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
 <p id="display"></p>
-<div id="scrollable" style="font-family:monospace; font-size: 18px; line-height: 1; overflow: auto; width: 200px; height: 200px;">
+<div id="scrollable" style="font-family:'Courier New'; font-size: 18px; line-height: 1; overflow: auto; width: 200px; height: 200px;">
   <div id="scrolled" style="font-size: 64px; width: 5000px; height: 5000px;">
     Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
     Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
     Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
     Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
     Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
     Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
     Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
@@ -39,16 +39,17 @@
     Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
   </div>
 </div>
 <div id="content" style="display: none">
 
 </div>
 <pre id="test">
 <script type="application/javascript">
+"use strict";
 
 SimpleTest.waitForExplicitFinish();
 SimpleTest.waitForFocus(runTests, window);
 
 var gScrollableElement = document.getElementById("scrollable");
 var gScrolledElement = document.getElementById("scrolled");
 
 var gLineHeight = 0;
--- a/dom/file/MemoryBlobImpl.cpp
+++ b/dom/file/MemoryBlobImpl.cpp
@@ -2,95 +2,52 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "MemoryBlobImpl.h"
 #include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/SHA1.h"
-#include "nsIIPCSerializableInputStream.h"
 #include "nsPrintfCString.h"
 
 namespace mozilla {
 namespace dom {
 
-// XXXkhuey the input stream that we pass out of a File
-// can outlive the actual File object.  Thus, we must
-// ensure that the buffer underlying the stream we get
-// from NS_NewByteInputStream is held alive as long as the
-// stream is.  We do that by passing back this class instead.
-class DataOwnerAdapter final : public nsIInputStream,
-                               public nsISeekableStream,
-                               public nsIIPCSerializableInputStream
-{
-  typedef MemoryBlobImpl::DataOwner DataOwner;
-public:
-  static nsresult Create(DataOwner* aDataOwner,
-                         uint32_t aStart,
-                         uint32_t aLength,
-                         nsIInputStream** _retval);
-
-  NS_DECL_THREADSAFE_ISUPPORTS
-
-  // These are mandatory.
-  NS_FORWARD_NSIINPUTSTREAM(mStream->)
-  NS_FORWARD_NSISEEKABLESTREAM(mSeekableStream->)
+NS_IMPL_ADDREF(MemoryBlobImpl::DataOwnerAdapter)
+NS_IMPL_RELEASE(MemoryBlobImpl::DataOwnerAdapter)
 
-  // This is optional. We use a conditional QI to keep it from being called
-  // if the underlying stream doesn't support it.
-  NS_FORWARD_NSIIPCSERIALIZABLEINPUTSTREAM(mSerializableInputStream->)
-
-private:
-  ~DataOwnerAdapter() {}
-
-  DataOwnerAdapter(DataOwner* aDataOwner,
-                   nsIInputStream* aStream)
-    : mDataOwner(aDataOwner), mStream(aStream),
-      mSeekableStream(do_QueryInterface(aStream)),
-      mSerializableInputStream(do_QueryInterface(aStream))
-  {
-    MOZ_ASSERT(mSeekableStream, "Somebody gave us the wrong stream!");
-  }
-
-  RefPtr<DataOwner> mDataOwner;
-  nsCOMPtr<nsIInputStream> mStream;
-  nsCOMPtr<nsISeekableStream> mSeekableStream;
-  nsCOMPtr<nsIIPCSerializableInputStream> mSerializableInputStream;
-};
-
-NS_IMPL_ADDREF(DataOwnerAdapter)
-NS_IMPL_RELEASE(DataOwnerAdapter)
-
-NS_INTERFACE_MAP_BEGIN(DataOwnerAdapter)
+NS_INTERFACE_MAP_BEGIN(MemoryBlobImpl::DataOwnerAdapter)
   NS_INTERFACE_MAP_ENTRY(nsIInputStream)
   NS_INTERFACE_MAP_ENTRY(nsISeekableStream)
+  NS_INTERFACE_MAP_ENTRY(nsICloneableInputStream)
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIIPCSerializableInputStream,
                                      mSerializableInputStream)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream)
 NS_INTERFACE_MAP_END
 
-nsresult DataOwnerAdapter::Create(DataOwner* aDataOwner,
-                                  uint32_t aStart,
-                                  uint32_t aLength,
-                                  nsIInputStream** _retval)
+nsresult MemoryBlobImpl::DataOwnerAdapter::Create(DataOwner* aDataOwner,
+                                                  uint32_t aStart,
+                                                  uint32_t aLength,
+                                                  nsIInputStream** _retval)
 {
   nsresult rv;
   MOZ_ASSERT(aDataOwner, "Uh ...");
 
   nsCOMPtr<nsIInputStream> stream;
 
   rv = NS_NewByteInputStream(getter_AddRefs(stream),
                              static_cast<const char*>(aDataOwner->mData) +
                              aStart,
                              (int32_t)aLength,
                              NS_ASSIGNMENT_DEPEND);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  NS_ADDREF(*_retval = new DataOwnerAdapter(aDataOwner, stream));
+  NS_ADDREF(*_retval =
+              new MemoryBlobImpl::DataOwnerAdapter(aDataOwner, stream));
 
   return NS_OK;
 }
 
 NS_IMPL_ISUPPORTS_INHERITED0(MemoryBlobImpl, BlobImpl)
 
 already_AddRefed<BlobImpl>
 MemoryBlobImpl::CreateSlice(uint64_t aStart, uint64_t aLength,
@@ -105,17 +62,18 @@ MemoryBlobImpl::CreateSlice(uint64_t aSt
 void
 MemoryBlobImpl::GetInternalStream(nsIInputStream** aStream, ErrorResult& aRv)
 {
   if (mLength > INT32_MAX) {
     aRv.Throw(NS_ERROR_FAILURE);
     return;
   }
 
-  aRv = DataOwnerAdapter::Create(mDataOwner, mStart, mLength, aStream);
+  aRv = MemoryBlobImpl::DataOwnerAdapter::Create(mDataOwner, mStart, mLength,
+                                                 aStream);
 }
 
 /* static */ StaticMutex
 MemoryBlobImpl::DataOwner::sDataOwnerMutex;
 
 /* static */ StaticAutoPtr<LinkedList<MemoryBlobImpl::DataOwner>>
 MemoryBlobImpl::DataOwner::sDataOwners;
 
--- a/dom/file/MemoryBlobImpl.h
+++ b/dom/file/MemoryBlobImpl.h
@@ -6,17 +6,21 @@
 
 #ifndef mozilla_dom_MemoryBlobImpl_h
 #define mozilla_dom_MemoryBlobImpl_h
 
 #include "mozilla/dom/BaseBlobImpl.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/StaticMutex.h"
 #include "mozilla/StaticPtr.h"
+#include "nsICloneableInputStream.h"
+#include "nsIInputStream.h"
+#include "nsIIPCSerializableInputStream.h"
 #include "nsIMemoryReporter.h"
+#include "nsISeekableStream.h"
 
 namespace mozilla {
 namespace dom {
 
 class MemoryBlobImpl final : public BaseBlobImpl
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
@@ -89,16 +93,60 @@ public:
     static mozilla::StaticMutex sDataOwnerMutex;
     static mozilla::StaticAutoPtr<mozilla::LinkedList<DataOwner> > sDataOwners;
     static bool sMemoryReporterRegistered;
 
     void* mData;
     uint64_t mLength;
   };
 
+  class DataOwnerAdapter final : public nsIInputStream
+                               , public nsISeekableStream
+                               , public nsIIPCSerializableInputStream
+                               , public nsICloneableInputStream
+  {
+    typedef MemoryBlobImpl::DataOwner DataOwner;
+  public:
+    static nsresult Create(DataOwner* aDataOwner,
+                           uint32_t aStart,
+                           uint32_t aLength,
+                           nsIInputStream** _retval);
+
+    NS_DECL_THREADSAFE_ISUPPORTS
+
+    // These are mandatory.
+    NS_FORWARD_NSIINPUTSTREAM(mStream->)
+    NS_FORWARD_NSISEEKABLESTREAM(mSeekableStream->)
+    NS_FORWARD_NSICLONEABLEINPUTSTREAM(mCloneableInputStream->)
+
+    // This is optional. We use a conditional QI to keep it from being called
+    // if the underlying stream doesn't support it.
+    NS_FORWARD_NSIIPCSERIALIZABLEINPUTSTREAM(mSerializableInputStream->)
+
+  private:
+    ~DataOwnerAdapter() {}
+
+    DataOwnerAdapter(DataOwner* aDataOwner,
+                     nsIInputStream* aStream)
+      : mDataOwner(aDataOwner)
+      , mStream(aStream)
+      , mSeekableStream(do_QueryInterface(aStream))
+      , mSerializableInputStream(do_QueryInterface(aStream))
+      , mCloneableInputStream(do_QueryInterface(aStream))
+    {
+      MOZ_ASSERT(mSeekableStream, "Somebody gave us the wrong stream!");
+    }
+
+    RefPtr<DataOwner> mDataOwner;
+    nsCOMPtr<nsIInputStream> mStream;
+    nsCOMPtr<nsISeekableStream> mSeekableStream;
+    nsCOMPtr<nsIIPCSerializableInputStream> mSerializableInputStream;
+    nsCOMPtr<nsICloneableInputStream> mCloneableInputStream;
+  };
+
 private:
   // Create slice
   MemoryBlobImpl(const MemoryBlobImpl* aOther, uint64_t aStart,
                  uint64_t aLength, const nsAString& aContentType)
     : BaseBlobImpl(aContentType, aOther->mStart + aStart, aLength)
     , mDataOwner(aOther->mDataOwner)
   {
     MOZ_ASSERT(mDataOwner && mDataOwner->mData, "must have data");
--- a/dom/file/ipc/Blob.cpp
+++ b/dom/file/ipc/Blob.cpp
@@ -20,16 +20,18 @@
 #include "mozilla/Mutex.h"
 #include "mozilla/Unused.h"
 #include "mozilla/dom/BaseBlobImpl.h"
 #include "mozilla/dom/nsIContentParent.h"
 #include "mozilla/dom/nsIContentChild.h"
 #include "mozilla/dom/PBlobStreamChild.h"
 #include "mozilla/dom/PBlobStreamParent.h"
 #include "mozilla/dom/indexedDB/FileSnapshot.h"
+#include "mozilla/dom/ipc/MemoryStreamChild.h"
+#include "mozilla/dom/ipc/MemoryStreamParent.h"
 #include "mozilla/dom/IndexedDatabaseManager.h"
 #include "mozilla/ipc/InputStreamUtils.h"
 #include "mozilla/ipc/IPCStreamUtils.h"
 #include "mozilla/ipc/BackgroundChild.h"
 #include "mozilla/ipc/PBackgroundChild.h"
 #include "mozilla/ipc/PBackgroundParent.h"
 #include "mozilla/ipc/PFileDescriptorSetParent.h"
 #include "MultipartBlobImpl.h"
@@ -612,16 +614,118 @@ struct MOZ_STACK_CLASS CreateBlobImplMet
 
   bool
   IsFile() const
   {
     return !mName.IsVoid();
   }
 };
 
+template<class M>
+PMemoryStreamChild*
+SerializeInputStreamInChunks(nsIInputStream* aInputStream, uint64_t aLength,
+                             M* aManager)
+{
+  MOZ_ASSERT(aInputStream);
+
+  PMemoryStreamChild* child = aManager->SendPMemoryStreamConstructor(aLength);
+  MOZ_ASSERT(child);
+
+  const uint64_t kMaxChunk = 1024 * 1024;
+
+  while (aLength) {
+    FallibleTArray<uint8_t> buffer;
+
+    uint64_t size = XPCOM_MIN(aLength, kMaxChunk);
+    if (NS_WARN_IF(!buffer.SetLength(size, fallible))) {
+      return nullptr;
+    }
+
+    uint32_t read;
+    nsresult rv = aInputStream->Read(reinterpret_cast<char*>(buffer.Elements()),
+                                     size, &read);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return nullptr;
+    }
+
+    if (NS_WARN_IF(read == 0)) {
+      // We were not expecting a read==0 here.
+      return nullptr;
+    }
+
+    MOZ_ASSERT(read <= size);
+    aLength -= read;
+
+    if (NS_WARN_IF(!buffer.SetLength(read, fallible))) {
+      return nullptr;
+    }
+
+    child->SendAddChunk(buffer);
+  }
+
+  return child;
+}
+
+void
+DeleteStreamMemoryFromBlobDataStream(BlobDataStream& aStream)
+{
+  PMemoryStreamChild* actor = aStream.streamChild();
+  if (actor) {
+    actor->Send__delete__(actor);
+  }
+}
+
+void
+DeleteStreamMemoryFromBlobData(BlobData& aBlobData)
+{
+  switch (aBlobData.type()) {
+    case BlobData::TBlobDataStream:
+      DeleteStreamMemoryFromBlobDataStream(aBlobData.get_BlobDataStream());
+      return;
+
+    case BlobData::TArrayOfBlobData: {
+      nsTArray<BlobData>& arrayBlobData = aBlobData.get_ArrayOfBlobData();
+      for (uint32_t i = 0; i < arrayBlobData.Length(); ++i) {
+        DeleteStreamMemoryFromBlobData(arrayBlobData[i]);
+      }
+      return;
+    }
+
+    default:
+      // Nothing to do here.
+      return;
+  }
+}
+
+void
+DeleteStreamMemoryFromOptionalBlobData(OptionalBlobData& aParams)
+{
+  if (aParams.type() == OptionalBlobData::Tvoid_t) {
+    return;
+  }
+
+  DeleteStreamMemoryFromBlobData(aParams.get_BlobData());
+}
+
+void
+DeleteStreamMemory(AnyBlobConstructorParams& aParams)
+{
+  if (aParams.type() == AnyBlobConstructorParams::TFileBlobConstructorParams) {
+    FileBlobConstructorParams& fileParams = aParams.get_FileBlobConstructorParams();
+    DeleteStreamMemoryFromOptionalBlobData(fileParams.optionalBlobData());
+    return;
+  }
+
+  if (aParams.type() == AnyBlobConstructorParams::TNormalBlobConstructorParams) {
+    NormalBlobConstructorParams& normalParams = aParams.get_NormalBlobConstructorParams();
+    DeleteStreamMemoryFromOptionalBlobData(normalParams.optionalBlobData());
+    return;
+  }
+}
+
 } // namespace
 
 already_AddRefed<BlobImpl>
 CreateBlobImpl(const nsID& aKnownBlobIDData,
                const CreateBlobImplMetadata& aMetadata)
 {
   MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
   MOZ_ASSERT(aMetadata.mHasRecursed);
@@ -640,17 +744,21 @@ CreateBlobImpl(const nsID& aKnownBlobIDD
 }
 
 already_AddRefed<BlobImpl>
 CreateBlobImpl(const BlobDataStream& aStream,
                const CreateBlobImplMetadata& aMetadata)
 {
   MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
 
-  nsCOMPtr<nsIInputStream> inputStream = DeserializeIPCStream(aStream.stream());
+  MemoryStreamParent* actor =
+    static_cast<MemoryStreamParent*>(aStream.streamParent());
+
+  nsCOMPtr<nsIInputStream> inputStream;
+  actor->GetStream(getter_AddRefs(inputStream));
   if (!inputStream) {
     ASSERT_UNLESS_FUZZING();
     return nullptr;
   }
 
   uint64_t length = aStream.length();
 
   RefPtr<BlobImpl> blobImpl;
@@ -832,20 +940,19 @@ CreateBlobImpl(const ParentBlobConstruct
   }
 
   RefPtr<BlobImpl> blobImpl =
     CreateBlobImplFromBlobData(aBlobData, metadata);
   return blobImpl.forget();
 }
 
 template <class ChildManagerType>
-void
+bool
 BlobDataFromBlobImpl(ChildManagerType* aManager, BlobImpl* aBlobImpl,
-                     BlobData& aBlobData,
-                     nsTArray<UniquePtr<AutoIPCStream>>& aIPCStreams)
+                     BlobData& aBlobData)
 {
   MOZ_ASSERT(gProcessType != GeckoProcessType_Default);
   MOZ_ASSERT(aBlobImpl);
 
   const nsTArray<RefPtr<BlobImpl>>* subBlobs = aBlobImpl->GetSubBlobImpls();
 
   if (subBlobs) {
     MOZ_ASSERT(subBlobs->Length());
@@ -853,45 +960,50 @@ BlobDataFromBlobImpl(ChildManagerType* a
     aBlobData = nsTArray<BlobData>();
 
     nsTArray<BlobData>& subBlobDatas = aBlobData.get_ArrayOfBlobData();
     subBlobDatas.SetLength(subBlobs->Length());
 
     for (uint32_t count = subBlobs->Length(), index = 0;
          index < count;
          index++) {
-      BlobDataFromBlobImpl(aManager, subBlobs->ElementAt(index),
-                           subBlobDatas[index], aIPCStreams);
+      if (!BlobDataFromBlobImpl(aManager, subBlobs->ElementAt(index),
+                                subBlobDatas[index])) {
+        return false;
+      }
     }
 
-    return;
+    return true;
   }
 
   nsCOMPtr<nsIRemoteBlob> remoteBlob = do_QueryInterface(aBlobImpl);
   if (remoteBlob) {
     BlobChild* actor = remoteBlob->GetBlobChild();
     MOZ_ASSERT(actor);
 
     aBlobData = actor->ParentID();
-    return;
+    return true;
   }
 
   ErrorResult rv;
   uint64_t length = aBlobImpl->GetSize(rv);
   MOZ_ALWAYS_TRUE(!rv.Failed());
 
   nsCOMPtr<nsIInputStream> inputStream;
   aBlobImpl->GetInternalStream(getter_AddRefs(inputStream), rv);
   MOZ_ALWAYS_TRUE(!rv.Failed());
 
-  UniquePtr<AutoIPCStream> autoStream(new AutoIPCStream());
-  autoStream->Serialize(inputStream, aManager);
-  aBlobData = BlobDataStream(autoStream->TakeValue(), length);
-
-  aIPCStreams.AppendElement(Move(autoStream));
+  PMemoryStreamChild* streamActor =
+    SerializeInputStreamInChunks(inputStream, length, aManager);
+  if (!streamActor) {
+    return false;
+  }
+
+  aBlobData = BlobDataStream(nullptr, streamActor, length);
+  return true;
 }
 
 RemoteInputStream::RemoteInputStream(BlobImpl* aBlobImpl,
                                      uint64_t aStart,
                                      uint64_t aLength)
   : mMonitor("RemoteInputStream.mMonitor")
   , mActor(nullptr)
   , mBlobImpl(aBlobImpl)
@@ -3472,29 +3584,30 @@ BlobChild::GetOrCreateFromImpl(ChildMana
   if (NS_WARN_IF(NS_FAILED(aBlobImpl->SetMutable(false)))) {
     return nullptr;
   }
 
   MOZ_ASSERT(!aBlobImpl->IsSizeUnknown());
   MOZ_ASSERT(!aBlobImpl->IsDateUnknown());
 
   AnyBlobConstructorParams blobParams;
-  nsTArray<UniquePtr<AutoIPCStream>> autoIPCStreams;
 
   if (gProcessType == GeckoProcessType_Default) {
     RefPtr<BlobImpl> sameProcessImpl = aBlobImpl;
     auto addRefedBlobImpl =
       reinterpret_cast<intptr_t>(sameProcessImpl.forget().take());
 
     blobParams = SameProcessBlobConstructorParams(addRefedBlobImpl);
   } else {
     // BlobData is going to be populate here and it _must_ be send via IPC in
     // order to avoid leaks.
     BlobData blobData;
-    BlobDataFromBlobImpl(aManager, aBlobImpl, blobData, autoIPCStreams);
+    if (NS_WARN_IF(!BlobDataFromBlobImpl(aManager, aBlobImpl, blobData))) {
+      return nullptr;
+    }
 
     nsString contentType;
     aBlobImpl->GetType(contentType);
 
     ErrorResult rv;
     uint64_t length = aBlobImpl->GetSize(rv);
     MOZ_ASSERT(!rv.Failed());
 
@@ -3519,17 +3632,18 @@ BlobChild::GetOrCreateFromImpl(ChildMana
   auto* actor = new BlobChild(aManager, aBlobImpl);
 
   ParentBlobConstructorParams params(blobParams);
 
   if (NS_WARN_IF(!aManager->SendPBlobConstructor(actor, params))) {
     return nullptr;
   }
 
-  autoIPCStreams.Clear();
+  DeleteStreamMemory(params.blobParams());
+
   return actor;
 }
 
 // static
 template <class ChildManagerType>
 BlobChild*
 BlobChild::CreateFromParams(ChildManagerType* aManager,
                             const ChildBlobConstructorParams& aParams)
new file mode 100644
--- /dev/null
+++ b/dom/file/ipc/MemoryStreamChild.h
@@ -0,0 +1,22 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_ipc_MemoryStreamChild_h
+#define mozilla_dom_ipc_MemoryStreamChild_h
+
+#include "mozilla/ipc/PMemoryStreamChild.h"
+
+namespace mozilla {
+namespace dom {
+
+class MemoryStreamChild final : public mozilla::ipc::PMemoryStreamChild
+{
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_ipc_MemoryStreamChild_h
new file mode 100644
--- /dev/null
+++ b/dom/file/ipc/MemoryStreamParent.cpp
@@ -0,0 +1,78 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "MemoryStreamParent.h"
+#include "nsIInputStream.h"
+
+namespace mozilla {
+namespace dom {
+
+MemoryStreamParent::MemoryStreamParent(uint64_t aSize)
+  : mSize(aSize)
+  , mCurSize(0)
+{}
+
+mozilla::ipc::IPCResult
+MemoryStreamParent::RecvAddChunk(nsTArray<unsigned char>&& aData)
+{
+  MOZ_ASSERT(mSize);
+
+  uint64_t dataLength = aData.Length();
+
+  if (!dataLength || mSize < (mCurSize + dataLength)) {
+    return IPC_FAIL_NO_REASON(this);
+  }
+
+  void* buffer = malloc(dataLength);
+  if (NS_WARN_IF(!buffer)) {
+    return IPC_FAIL_NO_REASON(this);
+  }
+
+  memcpy(buffer, aData.Elements(), dataLength);
+  mData.AppendElement(new MemoryBlobImpl::DataOwner(buffer, dataLength));
+
+  mCurSize += dataLength;
+  return IPC_OK();
+}
+
+void
+MemoryStreamParent::ActorDestroy(IProtocol::ActorDestroyReason)
+{
+}
+
+void
+MemoryStreamParent::GetStream(nsIInputStream** aInputStream)
+{
+  if (mCurSize != mSize) {
+    *aInputStream = nullptr;
+    return;
+  }
+
+  nsCOMPtr<nsIMultiplexInputStream> stream =
+    do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1");
+  if (NS_WARN_IF(!stream)) {
+    *aInputStream = nullptr;
+    return;
+  }
+
+  for (uint32_t i = 0; i < mData.Length(); ++i) {
+    nsCOMPtr<nsIInputStream> dataStream;
+    nsresult rv =
+      MemoryBlobImpl::DataOwnerAdapter::Create(mData[i], 0, mData[i]->mLength,
+                                               getter_AddRefs(dataStream));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      *aInputStream = nullptr;
+      return;
+    }
+
+    stream->AppendStream(dataStream);
+  }
+
+  stream.forget(aInputStream);
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/file/ipc/MemoryStreamParent.h
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_ipc_MemoryStreamParent_h
+#define mozilla_dom_ipc_MemoryStreamParent_h
+
+#include "mozilla/ipc/PMemoryStreamParent.h"
+#include "mozilla/dom/MemoryBlobImpl.h"
+
+namespace mozilla {
+namespace dom {
+
+class MemoryStreamParent final : public mozilla::ipc::PMemoryStreamParent
+{
+public:
+  explicit MemoryStreamParent(uint64_t aSize);
+
+  mozilla::ipc::IPCResult
+  RecvAddChunk(nsTArray<unsigned char>&& aData) override;
+
+  void
+  ActorDestroy(IProtocol::ActorDestroyReason) override;
+
+  void
+  GetStream(nsIInputStream** aInputStream);
+
+private:
+  uint64_t mSize;
+  uint64_t mCurSize;
+
+  nsTArray<RefPtr<MemoryBlobImpl::DataOwner>> mData;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_ipc_MemoryStreamParent_h
--- a/dom/file/ipc/PBlob.ipdl
+++ b/dom/file/ipc/PBlob.ipdl
@@ -4,16 +4,17 @@
 
 include protocol PBackground;
 include protocol PBlobStream;
 include protocol PContent;
 include protocol PContentBridge;
 include protocol PFileDescriptorSet;
 include protocol PChildToParentStream;
 include protocol PParentToChildStream;
+include protocol PMemoryStream;
 
 include BlobTypes;
 include DOMTypes;
 include InputStreamParams;
 
 namespace mozilla {
 namespace dom {
 
new file mode 100644
--- /dev/null
+++ b/dom/file/ipc/PMemoryStream.ipdl
@@ -0,0 +1,23 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include protocol PBackground;
+include protocol PContent;
+include protocol PContentBridge;
+
+namespace mozilla {
+namespace ipc {
+
+protocol PMemoryStream
+{
+  manager PBackground or PContent or PContentBridge;
+
+parent:
+  async AddChunk(uint8_t[] data);
+
+  async __delete__();
+};
+
+} // namespace dom
+} // namespace mozilla
--- a/dom/file/ipc/moz.build
+++ b/dom/file/ipc/moz.build
@@ -2,27 +2,31 @@
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 EXPORTS.mozilla.dom.ipc += [
     'BlobChild.h',
     'BlobParent.h',
+    'MemoryStreamChild.h',
+    'MemoryStreamParent.h',
     'nsIRemoteBlob.h',
 ]
 
 UNIFIED_SOURCES += [
     'Blob.cpp',
+    'MemoryStreamParent.cpp',
 ]
 
 IPDL_SOURCES += [
     'BlobTypes.ipdlh',
     'PBlob.ipdl',
     'PBlobStream.ipdl',
+    'PMemoryStream.ipdl',
 ]
 
 LOCAL_INCLUDES += [
     '/dom/file',
     '/dom/ipc',
     '/dom/workers',
 ]
 
--- a/dom/ipc/ContentBridgeChild.cpp
+++ b/dom/ipc/ContentBridgeChild.cpp
@@ -63,16 +63,22 @@ ContentBridgeChild::RecvAsyncMessage(con
 
 PBlobChild*
 ContentBridgeChild::SendPBlobConstructor(PBlobChild* actor,
                                          const BlobConstructorParams& params)
 {
   return PContentBridgeChild::SendPBlobConstructor(actor, params);
 }
 
+PMemoryStreamChild*
+ContentBridgeChild::SendPMemoryStreamConstructor(const uint64_t& aSize)
+{
+  return PContentBridgeChild::SendPMemoryStreamConstructor(aSize);
+}
+
 bool
 ContentBridgeChild::SendPBrowserConstructor(PBrowserChild* aActor,
                                             const TabId& aTabId,
                                             const IPCTabContext& aContext,
                                             const uint32_t& aChromeFlags,
                                             const ContentParentId& aCpID,
                                             const bool& aIsForBrowser)
 {
@@ -163,16 +169,28 @@ ContentBridgeChild::AllocPBlobChild(cons
 }
 
 bool
 ContentBridgeChild::DeallocPBlobChild(PBlobChild* aActor)
 {
   return nsIContentChild::DeallocPBlobChild(aActor);
 }
 
+PMemoryStreamChild*
+ContentBridgeChild::AllocPMemoryStreamChild(const uint64_t& aSize)
+{
+  return nsIContentChild::AllocPMemoryStreamChild(aSize);
+}
+
+bool
+ContentBridgeChild::DeallocPMemoryStreamChild(PMemoryStreamChild* aActor)
+{
+  return nsIContentChild::DeallocPMemoryStreamChild(aActor);
+}
+
 PChildToParentStreamChild*
 ContentBridgeChild::AllocPChildToParentStreamChild()
 {
   return nsIContentChild::AllocPChildToParentStreamChild();
 }
 
 bool
 ContentBridgeChild::DeallocPChildToParentStreamChild(PChildToParentStreamChild* aActor)
--- a/dom/ipc/ContentBridgeChild.h
+++ b/dom/ipc/ContentBridgeChild.h
@@ -31,16 +31,19 @@ public:
                                                    InfallibleTArray<jsipc::CpowEntry>&& aCpows,
                                                    const IPC::Principal& aPrincipal,
                                                    const ClonedMessageData& aData) override;
 
   virtual PBlobChild*
   SendPBlobConstructor(PBlobChild* actor,
                        const BlobConstructorParams& aParams) override;
 
+  virtual PMemoryStreamChild*
+  SendPMemoryStreamConstructor(const uint64_t& aSize) override;
+
   jsipc::CPOWManager* GetCPOWManager() override;
 
   virtual bool SendPBrowserConstructor(PBrowserChild* aActor,
                                        const TabId& aTabId,
                                        const IPCTabContext& aContext,
                                        const uint32_t& aChromeFlags,
                                        const ContentParentId& aCpID,
                                        const bool& aIsForBrowser) override;
@@ -77,16 +80,20 @@ protected:
                                                           const bool& aIsForBrowser) override;
 
   virtual mozilla::jsipc::PJavaScriptChild* AllocPJavaScriptChild() override;
   virtual bool DeallocPJavaScriptChild(mozilla::jsipc::PJavaScriptChild*) override;
 
   virtual PBlobChild* AllocPBlobChild(const BlobConstructorParams& aParams) override;
   virtual bool DeallocPBlobChild(PBlobChild*) override;
 
+  virtual PMemoryStreamChild*
+  AllocPMemoryStreamChild(const uint64_t& aSize) override;
+  virtual bool DeallocPMemoryStreamChild(PMemoryStreamChild*) override;
+
   virtual mozilla::ipc::PChildToParentStreamChild*
   AllocPChildToParentStreamChild() override;
 
   virtual bool
   DeallocPChildToParentStreamChild(mozilla::ipc::PChildToParentStreamChild* aActor) override;
 
   virtual PParentToChildStreamChild* AllocPParentToChildStreamChild() override;
 
--- a/dom/ipc/ContentBridgeParent.cpp
+++ b/dom/ipc/ContentBridgeParent.cpp
@@ -2,16 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/ContentBridgeParent.h"
 #include "mozilla/dom/TabParent.h"
 #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
+#include "mozilla/dom/ipc/MemoryStreamParent.h"
 #include "nsXULAppAPI.h"
 #include "nsIObserverService.h"
 #include "base/task.h"
 
 using namespace mozilla::ipc;
 using namespace mozilla::jsipc;
 
 namespace mozilla {
@@ -123,16 +124,28 @@ ContentBridgeParent::AllocPBlobParent(co
 }
 
 bool
 ContentBridgeParent::DeallocPBlobParent(PBlobParent* aActor)
 {
   return nsIContentParent::DeallocPBlobParent(aActor);
 }
 
+PMemoryStreamParent*
+ContentBridgeParent::AllocPMemoryStreamParent(const uint64_t& aSize)
+{
+  return nsIContentParent::AllocPMemoryStreamParent(aSize);
+}
+
+bool
+ContentBridgeParent::DeallocPMemoryStreamParent(PMemoryStreamParent* aActor)
+{
+  return nsIContentParent::DeallocPMemoryStreamParent(aActor);
+}
+
 mozilla::jsipc::PJavaScriptParent *
 ContentBridgeParent::AllocPJavaScriptParent()
 {
   return nsIContentParent::AllocPJavaScriptParent();
 }
 
 bool
 ContentBridgeParent::DeallocPJavaScriptParent(PJavaScriptParent *parent)
--- a/dom/ipc/ContentBridgeParent.h
+++ b/dom/ipc/ContentBridgeParent.h
@@ -131,16 +131,21 @@ protected:
 
   virtual bool DeallocPBrowserParent(PBrowserParent*) override;
 
   virtual PBlobParent*
   AllocPBlobParent(const BlobConstructorParams& aParams) override;
 
   virtual bool DeallocPBlobParent(PBlobParent*) override;
 
+  virtual PMemoryStreamParent*
+  AllocPMemoryStreamParent(const uint64_t& aSize) override;
+
+  virtual bool DeallocPMemoryStreamParent(PMemoryStreamParent*) override;
+
   virtual PChildToParentStreamParent* AllocPChildToParentStreamParent() override;
 
   virtual bool
   DeallocPChildToParentStreamParent(PChildToParentStreamParent* aActor) override;
 
   virtual mozilla::ipc::PParentToChildStreamParent*
   AllocPParentToChildStreamParent() override;
 
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -1519,16 +1519,28 @@ ContentChild::DeallocPFileDescriptorSetC
 }
 
 bool
 ContentChild::DeallocPBrowserChild(PBrowserChild* aIframe)
 {
   return nsIContentChild::DeallocPBrowserChild(aIframe);
 }
 
+PMemoryStreamChild*
+ContentChild::AllocPMemoryStreamChild(const uint64_t& aSize)
+{
+  return nsIContentChild::AllocPMemoryStreamChild(aSize);
+}
+
+bool
+ContentChild::DeallocPMemoryStreamChild(PMemoryStreamChild* aActor)
+{
+  return nsIContentChild::DeallocPMemoryStreamChild(aActor);
+}
+
 PBlobChild*
 ContentChild::AllocPBlobChild(const BlobConstructorParams& aParams)
 {
   return nsIContentChild::AllocPBlobChild(aParams);
 }
 
 mozilla::PRemoteSpellcheckEngineChild *
 ContentChild::AllocPRemoteSpellcheckEngineChild()
@@ -1556,16 +1568,26 @@ ContentChild::SendPBlobConstructor(PBlob
 {
   if (IsShuttingDown()) {
     return nullptr;
   }
 
   return PContentChild::SendPBlobConstructor(aActor, aParams);
 }
 
+PMemoryStreamChild*
+ContentChild::SendPMemoryStreamConstructor(const uint64_t& aSize)
+{
+  if (IsShuttingDown()) {
+    return nullptr;
+  }
+
+  return PContentChild::SendPMemoryStreamConstructor(aSize);
+}
+
 PPresentationChild*
 ContentChild::AllocPPresentationChild()
 {
   MOZ_CRASH("We should never be manually allocating PPresentationChild actors");
   return nullptr;
 }
 
 bool
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -177,16 +177,22 @@ public:
 
   virtual bool DeallocPBrowserChild(PBrowserChild*) override;
 
   virtual PBlobChild*
   AllocPBlobChild(const BlobConstructorParams& aParams) override;
 
   virtual bool DeallocPBlobChild(PBlobChild* aActor) override;
 
+  virtual PMemoryStreamChild*
+  AllocPMemoryStreamChild(const uint64_t& aSize) override;
+
+  virtual bool
+  DeallocPMemoryStreamChild(PMemoryStreamChild* aActor) override;
+
   virtual PHalChild* AllocPHalChild() override;
   virtual bool DeallocPHalChild(PHalChild*) override;
 
   virtual PHeapSnapshotTempFileHelperChild*
   AllocPHeapSnapshotTempFileHelperChild() override;
 
   virtual bool
   DeallocPHeapSnapshotTempFileHelperChild(PHeapSnapshotTempFileHelperChild*) override;
@@ -484,16 +490,19 @@ public:
 #endif
 
   bool IsForBrowser() const { return mIsForBrowser; }
 
   virtual PBlobChild*
   SendPBlobConstructor(PBlobChild* actor,
                        const BlobConstructorParams& params) override;
 
+  virtual PMemoryStreamChild*
+  SendPMemoryStreamConstructor(const uint64_t& aSize) override;
+
   virtual PFileDescriptorSetChild*
   SendPFileDescriptorSetConstructor(const FileDescriptor&) override;
 
   virtual PFileDescriptorSetChild*
   AllocPFileDescriptorSetChild(const FileDescriptor&) override;
 
   virtual bool
   DeallocPFileDescriptorSetChild(PFileDescriptorSetChild*) override;
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -2815,16 +2815,28 @@ ContentParent::AllocPBlobParent(const Bl
 }
 
 bool
 ContentParent::DeallocPBlobParent(PBlobParent* aActor)
 {
   return nsIContentParent::DeallocPBlobParent(aActor);
 }
 
+PMemoryStreamParent*
+ContentParent::AllocPMemoryStreamParent(const uint64_t& aSize)
+{
+  return nsIContentParent::AllocPMemoryStreamParent(aSize);
+}
+
+bool
+ContentParent::DeallocPMemoryStreamParent(PMemoryStreamParent* aActor)
+{
+  return nsIContentParent::DeallocPMemoryStreamParent(aActor);
+}
+
 mozilla::ipc::IPCResult
 ContentParent::RecvPBlobConstructor(PBlobParent* aActor,
                                     const BlobConstructorParams& aParams)
 {
   const ParentBlobConstructorParams& params = aParams.get_ParentBlobConstructorParams();
   if (params.blobParams().type() == AnyBlobConstructorParams::TKnownBlobConstructorParams) {
     if (!aActor->SendCreatedFromKnownBlob()) {
       return IPC_FAIL_NO_REASON(this);
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -820,16 +820,21 @@ private:
 
   virtual bool DeallocPBrowserParent(PBrowserParent* frame) override;
 
   virtual PBlobParent*
   AllocPBlobParent(const BlobConstructorParams& aParams) override;
 
   virtual bool DeallocPBlobParent(PBlobParent* aActor) override;
 
+  virtual PMemoryStreamParent*
+  AllocPMemoryStreamParent(const uint64_t& aSize) override;
+
+  virtual bool DeallocPMemoryStreamParent(PMemoryStreamParent* aActor) override;
+
   virtual mozilla::ipc::IPCResult
   RecvPBlobConstructor(PBlobParent* aActor,
                        const BlobConstructorParams& params) override;
 
   virtual mozilla::ipc::IPCResult RecvNSSU2FTokenIsCompatibleVersion(const nsString& aVersion,
                                                                      bool* aIsCompatible) override;
 
   virtual mozilla::ipc::IPCResult RecvNSSU2FTokenIsRegistered(nsTArray<uint8_t>&& aKeyHandle,
--- a/dom/ipc/DOMTypes.ipdlh
+++ b/dom/ipc/DOMTypes.ipdlh
@@ -1,18 +1,16 @@
 /* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
 /* vim: set sw=4 ts=8 et tw=80 ft=cpp : */
 /* 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 protocol PBlob;
-include protocol PChildToParentStream;
-include protocol PParentToChildStream;
-include IPCStream;
+include protocol PMemoryStream;
 include ProtocolTypes;
 
 using struct mozilla::void_t
   from "ipc/IPCMessageUtils.h";
 
 using struct mozilla::SerializedStructuredCloneBuffer
   from "ipc/IPCMessageUtils.h";
 
@@ -42,17 +40,17 @@ struct ClonedMessageData
 {
   SerializedStructuredCloneBuffer data;
   PBlob[] blobs;
   MessagePortIdentifier[] identfiers;
 };
 
 struct BlobDataStream
 {
-  IPCStream stream;
+  PMemoryStream stream;
   uint64_t length;
 };
 
 union BlobData
 {
   // For remote blobs.
   nsID;
 
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -15,16 +15,17 @@ include protocol PPSMContentDownloader;
 include protocol PExternalHelperApp;
 include protocol PHandlerService;
 include protocol PFileDescriptorSet;
 include protocol PHal;
 include protocol PHeapSnapshotTempFileHelper;
 include protocol PProcessHangMonitor;
 include protocol PImageBridge;
 include protocol PMedia;
+include protocol PMemoryStream;
 include protocol PNecko;
 include protocol PGMPContent;
 include protocol PGMPService;
 include protocol PPluginModule;
 include protocol PGMP;
 include protocol PPrinting;
 include protocol PChildToParentStream;
 include protocol PParentToChildStream;
@@ -278,16 +279,17 @@ nested(upto inside_cpow) sync protocol P
     manages PCycleCollectWithLogs;
     manages PPSMContentDownloader;
     manages PExternalHelperApp;
     manages PFileDescriptorSet;
     manages PHal;
     manages PHandlerService;
     manages PHeapSnapshotTempFileHelper;
     manages PMedia;
+    manages PMemoryStream;
     manages PNecko;
     manages POfflineCacheUpdate;
     manages PPrinting;
     manages PChildToParentStream;
     manages PParentToChildStream;
     manages PSpeechSynthesis;
     manages PStorage;
     manages PTestShell;
@@ -642,16 +644,18 @@ parent:
      * |pluginEpoch == newPluginEpoch|, then |plugins| will be left empty.
      */
     sync FindPlugins(uint32_t pluginEpoch) returns (nsresult aResult, PluginTag[] plugins, uint32_t newPluginEpoch);
 
     async PJavaScript();
 
     async PRemoteSpellcheckEngine();
 
+    async PMemoryStream(uint64_t aSize);
+
     async InitCrashReporter(Shmem shmem, NativeThreadId tid);
 
     /**
      * Is this token compatible with the provided version?
      *
      * |version| The offered version to test
      * Returns |True| if the offered version is compatible
      */
--- a/dom/ipc/PContentBridge.ipdl
+++ b/dom/ipc/PContentBridge.ipdl
@@ -5,16 +5,17 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include protocol PBlob;
 include protocol PBrowser;
 include protocol PContent;
 include protocol PJavaScript;
 include protocol PFileDescriptorSet;
 include protocol PChildToParentStream;
+include protocol PMemoryStream;
 include protocol PParentToChildStream;
 
 include DOMTypes;
 include JavaScriptTypes;
 include PTabContext;
 
 using class IPC::Principal from "mozilla/dom/PermissionMessageUtils.h";
 using mozilla::dom::TabId from "mozilla/dom/ipc/IdType.h";
@@ -35,16 +36,17 @@ namespace dom {
  */
 nested(upto inside_cpow) sync protocol PContentBridge
 {
     manages PBlob;
     manages PBrowser;
     manages PFileDescriptorSet;
     manages PJavaScript;
     manages PChildToParentStream;
+    manages PMemoryStream;
     manages PParentToChildStream;
 
 child:
     async PParentToChildStream();
 
 child:
    /**
      * Sending an activate message moves focus to the child.
@@ -59,16 +61,18 @@ parent:
     sync SyncMessage(nsString aMessage, ClonedMessageData aData,
                      CpowEntry[] aCpows, Principal aPrincipal)
       returns (StructuredCloneData[] retval);
 
     async PJavaScript();
 
     async PChildToParentStream();
 
+    async PMemoryStream(uint64_t aSize);
+
 both:
     // Both the parent and the child can construct the PBrowser.
     // See the comment in PContent::PBrowser().
     async PBrowser(TabId tabId, IPCTabContext context, uint32_t chromeFlags,
                    ContentParentId cpId, bool isForBrowser);
 
     async PBlob(BlobConstructorParams params);
 
--- a/dom/ipc/nsIContentChild.cpp
+++ b/dom/ipc/nsIContentChild.cpp
@@ -15,16 +15,17 @@
 #include "mozilla/dom/ipc/StructuredCloneData.h"
 #include "mozilla/ipc/FileDescriptorSetChild.h"
 #include "mozilla/ipc/InputStreamUtils.h"
 #include "mozilla/ipc/IPCStreamAlloc.h"
 #include "mozilla/ipc/IPCStreamDestination.h"
 #include "mozilla/ipc/IPCStreamSource.h"
 #include "mozilla/ipc/PChildToParentStreamChild.h"
 #include "mozilla/ipc/PParentToChildStreamChild.h"
+#include "mozilla/dom/ipc/MemoryStreamChild.h"
 
 #include "nsPrintfCString.h"
 #include "xpcpublic.h"
 
 using namespace mozilla::ipc;
 using namespace mozilla::jsipc;
 
 namespace mozilla {
@@ -97,16 +98,29 @@ nsIContentChild::RecvPBrowserConstructor
   nsCOMPtr<nsIObserverService> os = services::GetObserverService();
   if (os) {
     os->NotifyObservers(static_cast<nsITabChild*>(tabChild), "tab-child-created", nullptr);
   }
 
   return IPC_OK();
 }
 
+PMemoryStreamChild*
+nsIContentChild::AllocPMemoryStreamChild(const uint64_t& aSize)
+{
+  return new MemoryStreamChild();
+}
+
+bool
+nsIContentChild::DeallocPMemoryStreamChild(PMemoryStreamChild* aActor)
+{
+  delete aActor;
+  return true;
+}
+
 PBlobChild*
 nsIContentChild::AllocPBlobChild(const BlobConstructorParams& aParams)
 {
   return BlobChild::Create(this, aParams);
 }
 
 bool
 nsIContentChild::DeallocPBlobChild(PBlobChild* aActor)
--- a/dom/ipc/nsIContentChild.h
+++ b/dom/ipc/nsIContentChild.h
@@ -59,16 +59,19 @@ public:
 
   BlobChild* GetOrCreateActorForBlob(Blob* aBlob);
   BlobChild* GetOrCreateActorForBlobImpl(BlobImpl* aImpl);
 
   virtual PBlobChild*
   SendPBlobConstructor(PBlobChild* aActor,
                        const BlobConstructorParams& aParams) = 0;
 
+  virtual mozilla::ipc::PMemoryStreamChild*
+  SendPMemoryStreamConstructor(const uint64_t& aSize) = 0;
+
   virtual bool
   SendPBrowserConstructor(PBrowserChild* aActor,
                           const TabId& aTabId,
                           const IPCTabContext& aContext,
                           const uint32_t& aChromeFlags,
                           const ContentParentId& aCpID,
                           const bool& aIsForBrowser) = 0;
 
@@ -95,16 +98,21 @@ protected:
                                                           const uint32_t& aChromeFlags,
                                                           const ContentParentId& aCpID,
                                                           const bool& aIsForBrowse);
 
   virtual PBlobChild* AllocPBlobChild(const BlobConstructorParams& aParams);
 
   virtual bool DeallocPBlobChild(PBlobChild* aActor);
 
+  virtual mozilla::ipc::PMemoryStreamChild*
+  AllocPMemoryStreamChild(const uint64_t& aSize);
+
+  virtual bool DeallocPMemoryStreamChild(mozilla::ipc::PMemoryStreamChild* aActor);
+
   virtual mozilla::ipc::PChildToParentStreamChild* AllocPChildToParentStreamChild();
 
   virtual bool
   DeallocPChildToParentStreamChild(mozilla::ipc::PChildToParentStreamChild* aActor);
 
   virtual mozilla::ipc::PParentToChildStreamChild* AllocPParentToChildStreamChild();
 
   virtual bool
--- a/dom/ipc/nsIContentParent.cpp
+++ b/dom/ipc/nsIContentParent.cpp
@@ -9,16 +9,17 @@
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/ContentBridgeParent.h"
 #include "mozilla/dom/PTabContext.h"
 #include "mozilla/dom/PermissionMessageUtils.h"
 #include "mozilla/dom/TabParent.h"
 #include "mozilla/dom/ipc/BlobParent.h"
+#include "mozilla/dom/ipc/MemoryStreamParent.h"
 #include "mozilla/dom/ipc/StructuredCloneData.h"
 #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
 #include "mozilla/ipc/FileDescriptorSetParent.h"
 #include "mozilla/ipc/PFileDescriptorSetParent.h"
 #include "mozilla/ipc/IPCStreamAlloc.h"
 #include "mozilla/ipc/IPCStreamDestination.h"
 #include "mozilla/ipc/IPCStreamSource.h"
 #include "mozilla/Unused.h"
@@ -182,16 +183,29 @@ nsIContentParent::AllocPBlobParent(const
 
 bool
 nsIContentParent::DeallocPBlobParent(PBlobParent* aActor)
 {
   BlobParent::Destroy(aActor);
   return true;
 }
 
+PMemoryStreamParent*
+nsIContentParent::AllocPMemoryStreamParent(const uint64_t& aSize)
+{
+  return new MemoryStreamParent(aSize);
+}
+
+bool
+nsIContentParent::DeallocPMemoryStreamParent(PMemoryStreamParent* aActor)
+{
+  delete aActor;
+  return true;
+}
+
 BlobParent*
 nsIContentParent::GetOrCreateActorForBlob(Blob* aBlob)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aBlob);
 
   RefPtr<BlobImpl> blobImpl = aBlob->Impl();
   MOZ_ASSERT(blobImpl);
--- a/dom/ipc/nsIContentParent.h
+++ b/dom/ipc/nsIContentParent.h
@@ -31,16 +31,17 @@ namespace jsipc {
 class PJavaScriptParent;
 class CpowEntry;
 } // namespace jsipc
 
 namespace ipc {
 class PFileDescriptorSetParent;
 class PChildToParentStreamParent;
 class PParentToChildStreamParent;
+class PMemoryStreamParent;
 }
 
 namespace dom {
 
 class Blob;
 class BlobConstructorParams;
 class BlobImpl;
 class BlobParent;
@@ -116,16 +117,21 @@ protected: // IPDL methods
                                               const ContentParentId& aCpId,
                                               const bool& aIsForBrowser);
   virtual bool DeallocPBrowserParent(PBrowserParent* frame);
 
   virtual PBlobParent* AllocPBlobParent(const BlobConstructorParams& aParams);
 
   virtual bool DeallocPBlobParent(PBlobParent* aActor);
 
+  virtual mozilla::ipc::PMemoryStreamParent*
+  AllocPMemoryStreamParent(const uint64_t& aSize);
+
+  virtual bool DeallocPMemoryStreamParent(mozilla::ipc::PMemoryStreamParent* aActor);
+
   virtual mozilla::ipc::PFileDescriptorSetParent*
   AllocPFileDescriptorSetParent(const mozilla::ipc::FileDescriptor& aFD);
 
   virtual bool
   DeallocPFileDescriptorSetParent(mozilla::ipc::PFileDescriptorSetParent* aActor);
 
   virtual mozilla::ipc::PChildToParentStreamParent* AllocPChildToParentStreamParent();
 
--- a/dom/tests/mochitest/chrome/queryCaretRectWin.html
+++ b/dom/tests/mochitest/chrome/queryCaretRectWin.html
@@ -15,17 +15,17 @@
 -->
 
 <style>
   #text {
   position: absolute;
   left: 0em;
   top: 0em;
   font-size: 10pt;
-  font-family: monospace;
+  font-family: 'Courier New';
   line-height: 20px;
   letter-spacing: 0px;
   margin-top:-1px; /* nix the text area border */
   overflow: hidden;
   width:800px;
   height:400px;
   }
   #div-hor {
--- a/dom/tests/mochitest/chrome/selectAtPoint.html
+++ b/dom/tests/mochitest/chrome/selectAtPoint.html
@@ -1,16 +1,18 @@
 <!DOCTYPE HTML>
 <html>
 <head>
 <title>nsIDOMWindowUtils::selectAtPoint test</title>
 <script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
 <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
 
-<script type="application/javascript">
+<script>
+  "use strict";
+
   var SimpleTest = window.opener.SimpleTest;
   var Ci = Components.interfaces;
 
   function ok() { window.opener.ok.apply(window.opener, arguments); }
   function done() { window.opener.done.apply(window.opener, arguments); }
 
   function dumpLn() {
     for (let idx = 0; idx < arguments.length; idx++)
@@ -122,23 +124,17 @@
 
     // Separate character blocks in a word 't(te)s(ts)election1'
     targetPoint = { xPos: rect.left + (charDims.width + (charDims.width / 2)),
                     yPos: rect.top + (charDims.height / 2) };
     setStart(dwu, targetPoint.xPos, targetPoint.yPos, Ci.nsIDOMWindowUtils.SELECT_CHARACTER);
     targetPoint = { xPos: rect.left + ((charDims.width * 4) + (charDims.width / 2)),
                     yPos: rect.top + (charDims.height / 2) };
     setEnd(dwu, targetPoint.xPos, targetPoint.yPos, Ci.nsIDOMWindowUtils.SELECT_CHARACTER);
-    if (isLinux || isMac) {
-      // XXX I think this is a bug, the right hand selection is 4.5 characters over with a
-      // monspaced font. what we want: t(te)s(ts)election1 what we get: t(te)st(se)lection1
-      checkSelection(document, "split selection", "tese");
-    } else if (isWindows) {
       checkSelection(document, "split selection", "tets");
-    }
 
     // Trying to select where there's no text, should fail but not throw
     let result = dwu.selectAtPoint(rect.left - 20, rect.top - 20, Ci.nsIDOMWindowUtils.SELECT_CHARACTER, false);
     ok(result == false, "couldn't select?");
 
     // Second div in the main page
 
     div = document.getElementById("div2");
@@ -223,17 +219,17 @@
   }
 
   window.addEventListener("MozAfterPaint", onPaint);
 </script>
 
 <style type="text/css">
 
 body {
-  font-family: monospace;
+  font-family: 'Courier New';
   margin-left: 40px;
   margin-top: 40px;
   padding: 0;
 }
 
 #div1 {
   border: 1px solid red;
   width: 400px;
@@ -262,17 +258,17 @@ body {
 </style>
 </head>
 <body id="body" onload="onPageLoad();">
 
 <div id="div1">ttestselection1 Lorem ipsum dolor sit amet, at duo debet graeci, vivendum vulputate per ut. Ne labore incorrupte vix. Cu copiosae postulant tincidunt ius, in illud appetere contentiones eos. Ei munere officiis assentior pro, nibh decore ius at.</div>
 
 <br />
 
-<iframe id="frame1" src="data:text/html,<html><body style='margin: 0; padding: 0; font-family: monospace;' onload='window.parent.onFrameLoad();'><div id='sel2'>ttestselection2 Lorem ipsum dolor sit amet, at duo debet graeci, vivendum vulputate per ut.</div><br/><br/></body></html>"></iframe>
+<iframe id="frame1" src="data:text/html,<html><body style='margin: 0; padding: 0; font-family: \'Courier New\';' onload='window.parent.onFrameLoad();'><div id='sel2'>ttestselection2 Lorem ipsum dolor sit amet, at duo debet graeci, vivendum vulputate per ut.</div><br/><br/></body></html>"></iframe>
 
 <br/>
 
 <div id="div2">Lorem ipsum dolor sit amet, at duo debet graeci, vivendum vulputate per ut. Ne labore incorrupte vix. Cu copiosae postulant tincidunt ius, in illud appetere contentiones eos.</div>
 
 <br />
 
 <span id="measure">t</span>
--- a/dom/webbrowserpersist/nsWebBrowserPersist.cpp
+++ b/dom/webbrowserpersist/nsWebBrowserPersist.cpp
@@ -1115,16 +1115,18 @@ NS_IMETHODIMP nsWebBrowserPersist::OnSta
         switch ( status )
         {
         case NS_NET_STATUS_RESOLVING_HOST:
         case NS_NET_STATUS_RESOLVED_HOST:
         case NS_NET_STATUS_BEGIN_FTP_TRANSACTION:
         case NS_NET_STATUS_END_FTP_TRANSACTION:
         case NS_NET_STATUS_CONNECTING_TO:
         case NS_NET_STATUS_CONNECTED_TO:
+        case NS_NET_STATUS_TLS_HANDSHAKE_STARTING:
+        case NS_NET_STATUS_TLS_HANDSHAKE_ENDED:
         case NS_NET_STATUS_SENDING_TO:
         case NS_NET_STATUS_RECEIVING_FROM:
         case NS_NET_STATUS_WAITING_FOR:
         case NS_NET_STATUS_READING:
         case NS_NET_STATUS_WRITING:
             break;
 
         default:
--- a/dom/workers/WorkerNavigator.cpp
+++ b/dom/workers/WorkerNavigator.cpp
@@ -133,27 +133,19 @@ public:
     aWorkerPrivate->AssertIsOnWorkerThread();
   }
 
   virtual bool MainThreadRun() override
   {
     AssertIsOnMainThread();
 
     nsCOMPtr<nsPIDOMWindowInner> window = mWorkerPrivate->GetWindow();
-    nsCOMPtr<nsIURI> uri;
-    if (window && window->GetDocShell()) {
-      nsIDocument* doc = window->GetExtantDoc();
-      if (doc) {
-        doc->NodePrincipal()->GetURI(getter_AddRefs(uri));
-      }
-    }
 
     bool isCallerChrome = mWorkerPrivate->UsesSystemPrincipal();
-    nsresult rv = dom::Navigator::GetUserAgent(window, uri,
-                                               isCallerChrome, mUA);
+    nsresult rv = dom::Navigator::GetUserAgent(window, isCallerChrome, mUA);
     if (NS_FAILED(rv)) {
       NS_WARNING("Failed to retrieve user-agent from the worker thread.");
     }
 
     return true;
   }
 };
 
--- a/dom/xhr/tests/browser_blobFromFile.js
+++ b/dom/xhr/tests/browser_blobFromFile.js
@@ -1,11 +1,15 @@
 let { classes: Cc, interfaces: Ci } = Components;
 
 add_task(function* test() {
+  yield SpecialPowers.pushPrefEnv(
+    {set: [["browser.tabs.remote.separateFileUriProcess", true]]}
+  );
+
   let file = Cc["@mozilla.org/file/directory_service;1"]
                .getService(Ci.nsIDirectoryService)
                .QueryInterface(Ci.nsIProperties)
                .get("ProfD", Ci.nsIFile);
 
   let fileHandler = Cc["@mozilla.org/network/io-service;1"]
                       .getService(Ci.nsIIOService)
                       .getProtocolHandler("file")
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -480,16 +480,17 @@ private:
   DECL_GFX_PREF(Once, "image.mem.surfacecache.size_factor",    ImageMemSurfaceCacheSizeFactor, uint32_t, 64);
   DECL_GFX_PREF(Once, "image.multithreaded_decoding.limit",    ImageMTDecodingLimit, int32_t, -1);
 
   DECL_GFX_PREF(Once, "layers.acceleration.disabled",          LayersAccelerationDisabledDoNotUseDirectly, bool, false);
   DECL_GFX_PREF(Live, "layers.acceleration.draw-fps",          LayersDrawFPS, bool, false);
   DECL_GFX_PREF(Live, "layers.acceleration.draw-fps.print-histogram",  FPSPrintHistogram, bool, false);
   DECL_GFX_PREF(Live, "layers.acceleration.draw-fps.write-to-file", WriteFPSToFile, bool, false);
   DECL_GFX_PREF(Once, "layers.acceleration.force-enabled",     LayersAccelerationForceEnabledDoNotUseDirectly, bool, false);
+  DECL_OVERRIDE_PREF(Live, "layers.advanced.background-image", LayersAllowBackgroundImage, false);
   DECL_OVERRIDE_PREF(Live, "layers.advanced.border-layers",    LayersAllowBorderLayers, false);
   DECL_OVERRIDE_PREF(Live, "layers.advanced.boxshadow-inset-layers", LayersAllowInsetBoxShadow, gfxPrefs::OverrideBase_WebRender());
   DECL_OVERRIDE_PREF(Live, "layers.advanced.boxshadow-outer-layers", LayersAllowOuterBoxShadow, false);
   DECL_GFX_PREF(Live, "layers.advanced.bullet-layers",         LayersAllowBulletLayers, bool, false);
   DECL_GFX_PREF(Live, "layers.advanced.button-foreground-layers", LayersAllowButtonForegroundLayers, bool, false);
   DECL_GFX_PREF(Live, "layers.advanced.canvas-background-color", LayersAllowCanvasBackgroundColorLayers, bool, false);
   DECL_OVERRIDE_PREF(Live, "layers.advanced.caret-layers",     LayersAllowCaretLayers, gfxPrefs::OverrideBase_WebRender());
   DECL_OVERRIDE_PREF(Live, "layers.advanced.displaybuttonborder-layers", LayersAllowDisplayButtonBorder, gfxPrefs::OverrideBase_WebRender());
--- a/gfx/webrender_bindings/WebRenderTypes.h
+++ b/gfx/webrender_bindings/WebRenderTypes.h
@@ -157,16 +157,71 @@ static inline WrColor ToWrColor(const gf
   WrColor c;
   c.r = color.r;
   c.g = color.g;
   c.b = color.b;
   c.a = color.a;
   return c;
 }
 
+template<class T>
+static inline WrPoint ToWrPoint(const gfx::PointTyped<T>& point)
+{
+  WrPoint p;
+  p.x = point.x;
+  p.y = point.y;
+  return p;
+}
+
+template<class T>
+static inline WrPoint ToWrPoint(const gfx::IntPointTyped<T>& point)
+{
+  return ToWrPoint(IntPointToPoint(point));
+}
+
+static inline WrPoint ToWrPoint(const gfx::Point& point)
+{
+  WrPoint p;
+  p.x = point.x;
+  p.y = point.y;
+  return p;
+}
+
+template<class T>
+static inline WrRect ToWrRect(const gfx::RectTyped<T>& rect)
+{
+  WrRect r;
+  r.x = rect.x;
+  r.y = rect.y;
+  r.width = rect.width;
+  r.height = rect.height;
+  return r;
+}
+
+template<class T>
+static inline WrRect ToWrRect(const gfx::IntRectTyped<T>& rect)
+{
+  return ToWrRect(IntRectToRect(rect));
+}
+
+template<class T>
+static inline WrSize ToWrSize(const gfx::SizeTyped<T>& size)
+{
+  WrSize ls;
+  ls.width = size.width;
+  ls.height = size.height;
+  return ls;
+}
+
+template<class T>
+static inline WrSize ToWrSize(const gfx::IntSizeTyped<T>& size)
+{
+  return ToWrSize(IntSizeToSize(size));
+}
+
 static inline WrBorderStyle ToWrBorderStyle(const uint8_t& style)
 {
   switch (style) {
   case NS_STYLE_BORDER_STYLE_NONE:
     return WrBorderStyle::None;
   case NS_STYLE_BORDER_STYLE_SOLID:
     return WrBorderStyle::Solid;
   case NS_STYLE_BORDER_STYLE_DOUBLE:
@@ -194,32 +249,16 @@ static inline WrBorderStyle ToWrBorderSt
 static inline WrBorderSide ToWrBorderSide(const gfx::Color& color, const uint8_t& style)
 {
   WrBorderSide bs;
   bs.color = ToWrColor(color);
   bs.style = ToWrBorderStyle(style);
   return bs;
 }
 
-static inline WrPoint ToWrPoint(const LayerPoint point)
-{
-  WrPoint lp;
-  lp.x = point.x;
-  lp.y = point.y;
-  return lp;
-}
-
-static inline WrSize ToWrSize(const LayerSize size)
-{
-  WrSize ls;
-  ls.width = size.width;
-  ls.height = size.height;
-  return ls;
-}
-
 static inline WrBorderRadius ToWrUniformBorderRadius(const LayerSize& aSize)
 {
   WrBorderRadius br;
   br.top_left = ToWrSize(aSize);
   br.top_right = ToWrSize(aSize);
   br.bottom_left = ToWrSize(aSize);
   br.bottom_right = ToWrSize(aSize);
   return br;
@@ -290,50 +329,25 @@ static inline WrRepeatMode ToWrRepeatMod
   default:
     MOZ_ASSERT(false);
   }
 
   return WrRepeatMode::Stretch;
 }
 
 template<class T>
-static inline WrRect ToWrRect(const gfx::RectTyped<T>& rect)
-{
-  WrRect r;
-  r.x = rect.x;
-  r.y = rect.y;
-  r.width = rect.width;
-  r.height = rect.height;
-  return r;
-}
-
-template<class T>
-static inline WrRect ToWrRect(const gfx::IntRectTyped<T>& rect)
-{
-  return ToWrRect(IntRectToRect(rect));
-}
-
-template<class T>
 static inline WrComplexClipRegion ToWrComplexClipRegion(const gfx::RectTyped<T>& rect,
                                                         const LayerSize& size)
 {
   WrComplexClipRegion complex_clip;
   complex_clip.rect = wr::ToWrRect(rect);
   complex_clip.radii = wr::ToWrUniformBorderRadius(size);
   return complex_clip;
 }
 
-static inline WrPoint ToWrPoint(const gfx::Point& point)
-{
-  WrPoint p;
-  p.x = point.x;
-  p.y = point.y;
-  return p;
-}
-
 static inline WrExternalImageId ToWrExternalImageId(uint64_t aID)
 {
   WrExternalImageId id;
   id.id = aID;
   return id;
 }
 
 struct VecU8 {
--- a/image/RasterImage.cpp
+++ b/image/RasterImage.cpp
@@ -444,24 +444,16 @@ RasterImage::WillDrawOpaqueNow()
 void
 RasterImage::OnSurfaceDiscarded(const SurfaceKey& aSurfaceKey)
 {
   MOZ_ASSERT(mProgressTracker);
 
   bool animatedFramesDiscarded =
     mAnimationState && aSurfaceKey.Playback() == PlaybackType::eAnimated;
 
-  if (animatedFramesDiscarded && NS_IsMainThread()) {
-    MOZ_ASSERT(gfxPrefs::ImageMemAnimatedDiscardable());
-    mAnimationState->UpdateState(mAnimationFinished, this, mSize);
-    // We don't need OnSurfaceDiscardedInternal to handle the animated frames
-    // being discarded because we just did.
-    animatedFramesDiscarded = false;
-  }
-
   RefPtr<RasterImage> image = this;
   NS_DispatchToMainThread(NS_NewRunnableFunction(
                             "RasterImage::OnSurfaceDiscarded",
                             [=]() -> void {
     image->OnSurfaceDiscardedInternal(animatedFramesDiscarded);
   }));
 }
 
--- a/ipc/glue/BackgroundChildImpl.cpp
+++ b/ipc/glue/BackgroundChildImpl.cpp
@@ -18,16 +18,17 @@
 #include "mozilla/dom/PBlobChild.h"
 #include "mozilla/dom/PFileSystemRequestChild.h"
 #include "mozilla/dom/FileSystemTaskBase.h"
 #include "mozilla/dom/asmjscache/AsmJSCache.h"
 #include "mozilla/dom/cache/ActorUtils.h"
 #include "mozilla/dom/indexedDB/PBackgroundIDBFactoryChild.h"
 #include "mozilla/dom/indexedDB/PBackgroundIndexedDBUtilsChild.h"
 #include "mozilla/dom/ipc/BlobChild.h"
+#include "mozilla/dom/ipc/MemoryStreamChild.h"
 #include "mozilla/dom/quota/PQuotaChild.h"
 #include "mozilla/dom/GamepadEventChannelChild.h"
 #include "mozilla/dom/GamepadTestChannelChild.h"
 #include "mozilla/dom/MessagePortChild.h"
 #include "mozilla/ipc/IPCStreamAlloc.h"
 #include "mozilla/ipc/PBackgroundTestChild.h"
 #include "mozilla/ipc/PChildToParentStreamChild.h"
 #include "mozilla/ipc/PParentToChildStreamChild.h"
@@ -210,16 +211,29 @@ bool
 BackgroundChildImpl::DeallocPBlobChild(PBlobChild* aActor)
 {
   MOZ_ASSERT(aActor);
 
   mozilla::dom::BlobChild::Destroy(aActor);
   return true;
 }
 
+PMemoryStreamChild*
+BackgroundChildImpl::AllocPMemoryStreamChild(const uint64_t& aSize)
+{
+  return new mozilla::dom::MemoryStreamChild();
+}
+
+bool
+BackgroundChildImpl::DeallocPMemoryStreamChild(PMemoryStreamChild* aActor)
+{
+  delete aActor;
+  return true;
+}
+
 PFileDescriptorSetChild*
 BackgroundChildImpl::AllocPFileDescriptorSetChild(
                                           const FileDescriptor& aFileDescriptor)
 {
   return new FileDescriptorSetChild(aFileDescriptor);
 }
 
 bool
--- a/ipc/glue/BackgroundChildImpl.h
+++ b/ipc/glue/BackgroundChildImpl.h
@@ -71,16 +71,22 @@ protected:
                                         override;
 
   virtual PBlobChild*
   AllocPBlobChild(const BlobConstructorParams& aParams) override;
 
   virtual bool
   DeallocPBlobChild(PBlobChild* aActor) override;
 
+  virtual PMemoryStreamChild*
+  AllocPMemoryStreamChild(const uint64_t& aSize) override;
+
+  virtual bool
+  DeallocPMemoryStreamChild(PMemoryStreamChild* aActor) override;
+
   virtual PFileDescriptorSetChild*
   AllocPFileDescriptorSetChild(const FileDescriptor& aFileDescriptor)
                                override;
 
   virtual bool
   DeallocPFileDescriptorSetChild(PFileDescriptorSetChild* aActor) override;
 
   virtual PCamerasChild*
--- a/ipc/glue/BackgroundParentImpl.cpp
+++ b/ipc/glue/BackgroundParentImpl.cpp
@@ -23,16 +23,17 @@
 #include "mozilla/dom/PGamepadEventChannelParent.h"
 #include "mozilla/dom/PGamepadTestChannelParent.h"
 #include "mozilla/dom/MessagePortParent.h"
 #include "mozilla/dom/ServiceWorkerRegistrar.h"
 #include "mozilla/dom/asmjscache/AsmJSCache.h"
 #include "mozilla/dom/cache/ActorUtils.h"
 #include "mozilla/dom/indexedDB/ActorsParent.h"
 #include "mozilla/dom/ipc/BlobParent.h"
+#include "mozilla/dom/ipc/MemoryStreamParent.h"
 #include "mozilla/dom/quota/ActorsParent.h"
 #include "mozilla/ipc/BackgroundParent.h"
 #include "mozilla/ipc/BackgroundUtils.h"
 #include "mozilla/ipc/IPCStreamAlloc.h"
 #include "mozilla/ipc/PBackgroundSharedTypes.h"
 #include "mozilla/ipc/PBackgroundTestParent.h"
 #include "mozilla/ipc/PChildToParentStreamParent.h"
 #include "mozilla/ipc/PParentToChildStreamParent.h"
@@ -261,16 +262,36 @@ BackgroundParentImpl::DeallocPBlobParent
   AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aActor);
 
   mozilla::dom::BlobParent::Destroy(aActor);
   return true;
 }
 
+PMemoryStreamParent*
+BackgroundParentImpl::AllocPMemoryStreamParent(const uint64_t& aSize)
+{
+  AssertIsInMainProcess();
+  AssertIsOnBackgroundThread();
+
+  return new mozilla::dom::MemoryStreamParent(aSize);
+}
+
+bool
+BackgroundParentImpl::DeallocPMemoryStreamParent(PMemoryStreamParent* aActor)
+{
+  AssertIsInMainProcess();
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aActor);
+
+  delete aActor;
+  return true;
+}
+
 mozilla::ipc::IPCResult
 BackgroundParentImpl::RecvPBlobConstructor(PBlobParent* aActor,
                                            const BlobConstructorParams& aParams)
 {
   const ParentBlobConstructorParams& params = aParams;
   if (params.blobParams().type() == AnyBlobConstructorParams::TKnownBlobConstructorParams) {
     if (!aActor->SendCreatedFromKnownBlob()) {
       return IPC_FAIL_NO_REASON(this);
--- a/ipc/glue/BackgroundParentImpl.h
+++ b/ipc/glue/BackgroundParentImpl.h
@@ -64,16 +64,22 @@ protected:
   RecvFlushPendingFileDeletions() override;
 
   virtual PBlobParent*
   AllocPBlobParent(const BlobConstructorParams& aParams) override;
 
   virtual bool
   DeallocPBlobParent(PBlobParent* aActor) override;
 
+  virtual PMemoryStreamParent*
+  AllocPMemoryStreamParent(const uint64_t& aSize) override;
+
+  virtual bool
+  DeallocPMemoryStreamParent(PMemoryStreamParent* aActor) override;
+
   virtual mozilla::ipc::IPCResult
   RecvPBlobConstructor(PBlobParent* aActor,
                        const BlobConstructorParams& params) override;
 
   virtual PFileDescriptorSetParent*
   AllocPFileDescriptorSetParent(const FileDescriptor& aFileDescriptor)
                                 override;
 
--- a/ipc/glue/PBackground.ipdl
+++ b/ipc/glue/PBackground.ipdl
@@ -10,16 +10,17 @@ include protocol PBlob;
 include protocol PBroadcastChannel;
 include protocol PCache;
 include protocol PCacheStorage;
 include protocol PCacheStreamControl;
 include protocol PFileDescriptorSet;
 include protocol PFileSystemRequest;
 include protocol PGamepadEventChannel;
 include protocol PGamepadTestChannel;
+include protocol PMemoryStream;
 include protocol PMessagePort;
 include protocol PCameras;
 include protocol PQuota;
 include protocol PChildToParentStream;
 include protocol PParentToChildStream;
 include protocol PServiceWorkerManager;
 include protocol PUDPSocket;
 include protocol PVsync;
@@ -54,16 +55,17 @@ sync protocol PBackground
   manages PBroadcastChannel;
   manages PCache;
   manages PCacheStorage;
   manages PCacheStreamControl;
   manages PFileDescriptorSet;
   manages PFileSystemRequest;
   manages PGamepadEventChannel;
   manages PGamepadTestChannel;
+  manages PMemoryStream;
   manages PMessagePort;
   manages PCameras;
   manages PQuota;
   manages PChildToParentStream;
   manages PParentToChildStream;
   manages PServiceWorkerManager;
   manages PUDPSocket;
   manages PVsync;
@@ -105,16 +107,18 @@ parent:
   async PQuota();
 
   async PFileSystemRequest(FileSystemParams params);
 
   async PGamepadEventChannel();
 
   async PGamepadTestChannel();
 
+  async PMemoryStream(uint64_t aSize);
+
 child:
   async PCache();
   async PCacheStreamControl();
 
   async PParentToChildStream();
 
 both:
   async PBlob(BlobConstructorParams params);
--- a/js/public/Class.h
+++ b/js/public/Class.h
@@ -847,17 +847,17 @@ struct JSClass {
 // with the following flags. Failure to use JSCLASS_GLOBAL_FLAGS was
 // previously allowed, but is now an ES5 violation and thus unsupported.
 //
 // JSCLASS_GLOBAL_APPLICATION_SLOTS is the number of slots reserved at
 // the beginning of every global object's slots for use by the
 // application.
 #define JSCLASS_GLOBAL_APPLICATION_SLOTS 5
 #define JSCLASS_GLOBAL_SLOT_COUNT                                             \
-    (JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 41)
+    (JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 46)
 #define JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(n)                                    \
     (JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT + (n)))
 #define JSCLASS_GLOBAL_FLAGS                                                  \
     JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(0)
 #define JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(clasp)                              \
   (((clasp)->flags & JSCLASS_IS_GLOBAL)                                       \
    && JSCLASS_RESERVED_SLOTS(clasp) >= JSCLASS_GLOBAL_SLOT_COUNT)
 
new file mode 100644
--- /dev/null
+++ b/js/src/builtin/AsyncIteration.js
@@ -0,0 +1,7 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+function AsyncIteratorIdentity() {
+    return this;
+}
--- a/js/src/builtin/Promise.cpp
+++ b/js/src/builtin/Promise.cpp
@@ -7,20 +7,22 @@
 
 #include "builtin/Promise.h"
 
 #include "mozilla/Atomics.h"
 #include "mozilla/TimeStamp.h"
 
 #include "jscntxt.h"
 #include "jsexn.h"
+#include "jsiter.h"
 
 #include "gc/Heap.h"
 #include "js/Debug.h"
 #include "vm/AsyncFunction.h"
+#include "vm/AsyncIteration.h"
 
 #include "jsobjinlines.h"
 
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 
 static double
@@ -31,16 +33,26 @@ MillisecondsSinceStartup()
     return (now - mozilla::TimeStamp::ProcessCreation(ignored)).ToMilliseconds();
 }
 
 enum PromiseHandler {
     PromiseHandlerIdentity = 0,
     PromiseHandlerThrower,
     PromiseHandlerAsyncFunctionAwaitFulfilled,
     PromiseHandlerAsyncFunctionAwaitRejected,
+    PromiseHandlerAsyncGeneratorAwaitFulfilled,
+    PromiseHandlerAsyncGeneratorAwaitRejected,
+
+    // Async Iteration proposal 6.1.1.2.1.
+    // Async iterator handlers take the resolved value and create new iterator
+    // objects.  To do so it needs to forward whether the iterator is done. In
+    // spec, this is achieved via the [[Done]] internal slot. We enumerate both
+    // true and false cases here.
+    PromiseHandlerAsyncIteratorValueUnwrapDone,
+    PromiseHandlerAsyncIteratorValueUnwrapNotDone,
 };
 
 enum ResolutionMode {
     ResolveMode,
     RejectMode
 };
 
 enum ResolveFunctionSlots {
@@ -173,23 +185,25 @@ enum ReactionRecordSlots {
     ReactionRecordSlot_Promise = 0,
     ReactionRecordSlot_OnFulfilled,
     ReactionRecordSlot_OnRejected,
     ReactionRecordSlot_Resolve,
     ReactionRecordSlot_Reject,
     ReactionRecordSlot_IncumbentGlobalObject,
     ReactionRecordSlot_Flags,
     ReactionRecordSlot_HandlerArg,
+    ReactionRecordSlot_Generator,
     ReactionRecordSlots,
 };
 
 #define REACTION_FLAG_RESOLVED                  0x1
 #define REACTION_FLAG_FULFILLED                 0x2
 #define REACTION_FLAG_IGNORE_DEFAULT_RESOLUTION 0x4
 #define REACTION_FLAG_ASYNC_FUNCTION_AWAIT      0x8
+#define REACTION_FLAG_ASYNC_GENERATOR_AWAIT     0x10
 
 // ES2016, 25.4.1.2.
 class PromiseReactionRecord : public NativeObject
 {
   public:
     static const Class class_;
 
     JSObject* promise() { return getFixedSlot(ReactionRecordSlot_Promise).toObjectOrNull(); }
@@ -215,16 +229,32 @@ class PromiseReactionRecord : public Nat
         int32_t flags = this->flags();
         flags |= REACTION_FLAG_ASYNC_FUNCTION_AWAIT;
         setFixedSlot(ReactionRecordSlot_Flags, Int32Value(flags));
     }
     bool isAsyncFunctionAwait() {
         int32_t flags = this->flags();
         return flags & REACTION_FLAG_ASYNC_FUNCTION_AWAIT;
     }
+    void setIsAsyncGeneratorAwait(Handle<AsyncGeneratorObject*> asyncGenObj) {
+        int32_t flags = this->flags();
+        flags |= REACTION_FLAG_ASYNC_GENERATOR_AWAIT;
+        setFixedSlot(ReactionRecordSlot_Flags, Int32Value(flags));
+
+        setFixedSlot(ReactionRecordSlot_Generator, ObjectValue(*asyncGenObj));
+    }
+    bool isAsyncGeneratorAwait() {
+        int32_t flags = this->flags();
+        return flags & REACTION_FLAG_ASYNC_GENERATOR_AWAIT;
+    }
+    AsyncGeneratorObject* asyncGenerator() {
+        MOZ_ASSERT(isAsyncGeneratorAwait());
+        return &getFixedSlot(ReactionRecordSlot_Generator).toObject()
+                                                          .as<AsyncGeneratorObject>();
+    }
     Value handler() {
         MOZ_ASSERT(targetState() != JS::PromiseState::Pending);
         uint32_t slot = targetState() == JS::PromiseState::Fulfilled
                         ? ReactionRecordSlot_OnFulfilled
                         : ReactionRecordSlot_OnRejected;
         return getFixedSlot(slot);
     }
     Value handlerArg() {
@@ -839,16 +869,44 @@ AsyncFunctionAwaitPromiseReactionJob(JSC
         if (!AsyncFunctionAwaitedRejected(cx, resultPromise, generatorVal, argument))
             return false;
     }
 
     rval.setUndefined();
     return true;
 }
 
+static MOZ_MUST_USE bool
+AsyncGeneratorAwaitPromiseReactionJob(JSContext* cx, Handle<PromiseReactionRecord*> reaction,
+                                      MutableHandleValue rval)
+{
+    MOZ_ASSERT(reaction->isAsyncGeneratorAwait());
+
+    RootedValue handlerVal(cx, reaction->handler());
+    RootedValue argument(cx, reaction->handlerArg());
+    Rooted<AsyncGeneratorObject*> asyncGenObj(cx, reaction->asyncGenerator());
+
+    int32_t handlerNum = int32_t(handlerVal.toNumber());
+    MOZ_ASSERT(handlerNum == PromiseHandlerAsyncGeneratorAwaitFulfilled ||
+               handlerNum == PromiseHandlerAsyncGeneratorAwaitRejected);
+
+    // Await's handlers don't return a value, nor throw exception.
+    // They fail only on OOM.
+    if (handlerNum == PromiseHandlerAsyncGeneratorAwaitFulfilled) {
+        if (!AsyncGeneratorAwaitedFulfilled(cx, asyncGenObj, argument))
+            return false;
+    } else {
+        if (!AsyncGeneratorAwaitedRejected(cx, asyncGenObj, argument))
+            return false;
+    }
+
+    rval.setUndefined();
+    return true;
+}
+
 // ES2016, 25.4.2.1.
 /**
  * Callback triggering the fulfill/reject reaction for a resolved Promise,
  * to be invoked by the embedding during its processing of the Promise job
  * queue.
  *
  * See http://www.ecma-international.org/ecma-262/7.0/index.html#sec-jobs-and-job-queues
  *
@@ -877,37 +935,49 @@ PromiseReactionJob(JSContext* cx, unsign
         reactionObj = UncheckedUnwrap(reactionObj);
         ac.emplace(cx, reactionObj);
     }
 
     // Steps 1-2.
     Rooted<PromiseReactionRecord*> reaction(cx, &reactionObj->as<PromiseReactionRecord>());
     if (reaction->isAsyncFunctionAwait())
         return AsyncFunctionAwaitPromiseReactionJob(cx, reaction, args.rval());
+    if (reaction->isAsyncGeneratorAwait())
+        return AsyncGeneratorAwaitPromiseReactionJob(cx, reaction, args.rval());
 
     // Step 3.
     RootedValue handlerVal(cx, reaction->handler());
 
     RootedValue argument(cx, reaction->handlerArg());
 
     RootedValue handlerResult(cx);
     ResolutionMode resolutionMode = ResolveMode;
 
     // Steps 4-6.
     if (handlerVal.isNumber()) {
         int32_t handlerNum = int32_t(handlerVal.toNumber());
 
         // Step 4.
         if (handlerNum == PromiseHandlerIdentity) {
             handlerResult = argument;
-        } else {
+        } else if (handlerNum == PromiseHandlerThrower) {
             // Step 5.
-            MOZ_ASSERT(handlerNum == PromiseHandlerThrower);
             resolutionMode = RejectMode;
             handlerResult = argument;
+        } else {
+            MOZ_ASSERT(handlerNum == PromiseHandlerAsyncIteratorValueUnwrapDone ||
+                       handlerNum == PromiseHandlerAsyncIteratorValueUnwrapNotDone);
+
+            bool done = handlerNum == PromiseHandlerAsyncIteratorValueUnwrapDone;
+            // Async Iteration proposal 6.1.1.2.1 step 1.
+            RootedObject resultObj(cx, CreateIterResultObject(cx, argument, done));
+            if (!resultObj)
+                return false;
+
+            handlerResult = ObjectValue(*resultObj);
         }
     } else {
         // Step 6.
         FixedInvokeArgs<1> args2(cx);
         args2[0].set(argument);
         if (!Call(cx, handlerVal, UndefinedHandleValue, args2, &handlerResult)) {
             resolutionMode = RejectMode;
             if (!MaybeGetAndClearException(cx, &handlerResult))
@@ -2186,16 +2256,354 @@ js::AsyncFunctionAwait(JSContext* cx, Ha
         return false;
 
     reaction->setIsAsyncFunctionAwait();
 
     // Step 8.
     return PerformPromiseThenWithReaction(cx, promise, reaction);
 }
 
+// Async Iteration proposal 5.1 steps 2-9.
+MOZ_MUST_USE bool
+js::AsyncGeneratorAwait(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
+                        HandleValue value)
+{
+    // Step 2.
+    Rooted<PromiseObject*> promise(cx, CreatePromiseObjectWithoutResolutionFunctions(cx));
+    if (!promise)
+        return false;
+
+    // Steps 3.
+    if (!ResolvePromiseInternal(cx, promise, value))
+        return false;
+
+    // Steps 4-5.
+    RootedValue onFulfilled(cx, Int32Value(PromiseHandlerAsyncGeneratorAwaitFulfilled));
+    RootedValue onRejected(cx, Int32Value(PromiseHandlerAsyncGeneratorAwaitRejected));
+
+    RootedObject incumbentGlobal(cx);
+    if (!GetObjectFromIncumbentGlobal(cx, &incumbentGlobal))
+        return false;
+
+    // Step 6 (skipped).
+
+    // Steps 7-8.
+    Rooted<PromiseReactionRecord*> reaction(cx, NewReactionRecord(cx, nullptr,
+                                                                  onFulfilled, onRejected,
+                                                                  nullptr, nullptr,
+                                                                  incumbentGlobal));
+    if (!reaction)
+        return false;
+
+    reaction->setIsAsyncGeneratorAwait(asyncGenObj);
+
+    // Step 9.
+    return PerformPromiseThenWithReaction(cx, promise, reaction);
+}
+
+// Async Iteration proposal 6.1.3.2.1 %AsyncFromSyncIteratorPrototype%.next
+// Async Iteration proposal 6.1.3.2.2 %AsyncFromSyncIteratorPrototype%.return
+// Async Iteration proposal 6.1.3.2.3 %AsyncFromSyncIteratorPrototype%.throw
+bool
+js::AsyncFromSyncIteratorMethod(JSContext* cx, CallArgs& args, CompletionKind completionKind)
+{
+    // Step 1.
+    RootedValue thisVal(cx, args.thisv());
+
+    // Step 2.
+    RootedObject resultPromise(cx, CreatePromiseObjectWithoutResolutionFunctions(cx));
+    if (!resultPromise)
+        return false;
+
+    // Step 3.
+    if (!thisVal.isObject() || !thisVal.toObject().is<AsyncFromSyncIteratorObject>()) {
+        // Step 3.a.
+        RootedValue badGeneratorError(cx);
+        if (!GetTypeError(cx, JSMSG_NOT_AN_ASYNC_ITERATOR, &badGeneratorError))
+            return false;
+
+        // Step 3.b.
+        if (!RejectMaybeWrappedPromise(cx, resultPromise, badGeneratorError))
+            return false;
+
+        // Step 3.c.
+        args.rval().setObject(*resultPromise);
+        return true;
+    }
+
+    Rooted<AsyncFromSyncIteratorObject*> asyncIter(
+        cx, &thisVal.toObject().as<AsyncFromSyncIteratorObject>());
+
+    // Step 4.
+    RootedObject iter(cx, asyncIter->iterator());
+
+    RootedValue resultVal(cx);
+    RootedValue func(cx);
+    if (completionKind == CompletionKind::Normal) {
+        // 6.1.3.2.1 steps 5-6 (partially).
+        if (!GetProperty(cx, iter, iter, cx->names().next, &func))
+            return AbruptRejectPromise(cx, args, resultPromise, nullptr);
+    } else if (completionKind == CompletionKind::Return) {
+        // 6.1.3.2.2 steps 5-6.
+        if (!GetProperty(cx, iter, iter, cx->names().return_, &func))
+            return AbruptRejectPromise(cx, args, resultPromise, nullptr);
+
+        // Step 7.
+        if (func.isNullOrUndefined()) {
+            // Step 7.a.
+            RootedObject resultObj(cx, CreateIterResultObject(cx, args.get(0), true));
+            if (!resultObj)
+                return AbruptRejectPromise(cx, args, resultPromise, nullptr);
+
+            RootedValue resultVal(cx, ObjectValue(*resultObj));
+
+            // Step 7.b.
+            if (!ResolvePromiseInternal(cx, resultPromise, resultVal))
+                return AbruptRejectPromise(cx, args, resultPromise, nullptr);
+
+            // Step 7.c.
+            args.rval().setObject(*resultPromise);
+            return true;
+        }
+    } else {
+        // 6.1.3.2.3 steps 5-6.
+        MOZ_ASSERT(completionKind == CompletionKind::Throw);
+        if (!GetProperty(cx, iter, iter, cx->names().throw_, &func))
+            return AbruptRejectPromise(cx, args, resultPromise, nullptr);
+
+        // Step 7.
+        if (func.isNullOrUndefined()) {
+            // Step 7.a.
+            if (!RejectMaybeWrappedPromise(cx, resultPromise, args.get(0)))
+                return AbruptRejectPromise(cx, args, resultPromise, nullptr);
+
+            // Step 7.b.
+            args.rval().setObject(*resultPromise);
+            return true;
+        }
+    }
+
+    // 6.1.3.2.1 steps 5-6 (partially).
+    // 6.1.3.2.2, 6.1.3.2.3 steps 8-9.
+    RootedValue iterVal(cx, ObjectValue(*iter));
+    FixedInvokeArgs<1> args2(cx);
+    args2[0].set(args.get(0));
+    if (!js::Call(cx, func, iterVal, args2, &resultVal))
+        return AbruptRejectPromise(cx, args, resultPromise, nullptr);
+
+    // 6.1.3.2.1 steps 5-6 (partially).
+    // 6.1.3.2.2, 6.1.3.2.3 steps 10.
+    if (!resultVal.isObject()) {
+        CheckIsObjectKind kind;
+        switch (completionKind) {
+          case CompletionKind::Normal:
+            kind = CheckIsObjectKind::IteratorNext;
+            break;
+          case CompletionKind::Throw:
+            kind = CheckIsObjectKind::IteratorThrow;
+            break;
+          case CompletionKind::Return:
+            kind = CheckIsObjectKind::IteratorReturn;
+            break;
+        }
+        MOZ_ALWAYS_FALSE(ThrowCheckIsObject(cx, kind));
+        return AbruptRejectPromise(cx, args, resultPromise, nullptr);
+    }
+
+    RootedObject resultObj(cx, &resultVal.toObject());
+
+    // Following step numbers are for 6.1.3.2.1.
+    // For 6.1.3.2.2 and 6.1.3.2.3, steps 7-16 corresponds to steps 11-20.
+
+    // Steps 7-8.
+    RootedValue value(cx);
+    if (!GetProperty(cx, resultObj, resultObj, cx->names().value, &value))
+        return AbruptRejectPromise(cx, args, resultPromise, nullptr);
+
+    // Steps 9-10.
+    RootedValue doneVal(cx);
+    if (!GetProperty(cx, resultObj, resultObj, cx->names().done, &doneVal))
+        return AbruptRejectPromise(cx, args, resultPromise, nullptr);
+    bool done = ToBoolean(doneVal);
+
+    // Step 11.
+    Rooted<PromiseObject*> promise(cx, CreatePromiseObjectWithoutResolutionFunctions(cx));
+    if (!promise)
+        return false;
+
+    // Step 12.
+    if (!ResolvePromiseInternal(cx, promise, value))
+        return false;
+
+    // Steps 13-14.
+    RootedValue onFulfilled(cx, Int32Value(done
+                                           ? PromiseHandlerAsyncIteratorValueUnwrapDone
+                                           : PromiseHandlerAsyncIteratorValueUnwrapNotDone));
+
+    RootedObject incumbentGlobal(cx);
+    if (!GetObjectFromIncumbentGlobal(cx, &incumbentGlobal))
+        return false;
+
+    // Step 15.
+    Rooted<PromiseReactionRecord*> reaction(cx, NewReactionRecord(cx, resultPromise, onFulfilled,
+                                                                  UndefinedHandleValue,
+                                                                  nullptr, nullptr,
+                                                                  incumbentGlobal));
+    if (!reaction)
+        return false;
+
+    if (!PerformPromiseThenWithReaction(cx, promise, reaction))
+        return false;
+
+    // Step 16.
+    args.rval().setObject(*resultPromise);
+    return true;
+}
+
+// Async Iteration proposal 6.4.3.3.
+MOZ_MUST_USE bool
+js::AsyncGeneratorResolve(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
+                          HandleValue value, bool done)
+{
+    // Step 1 (implicit).
+
+    // Steps 2-3.
+    MOZ_ASSERT(!asyncGenObj->isQueueEmpty());
+
+    // Step 4.
+    Rooted<AsyncGeneratorRequest*> request(
+        cx, AsyncGeneratorObject::dequeueRequest(cx, asyncGenObj));
+    if (!request)
+        return false;
+
+    // Step 5.
+    RootedObject resultPromise(cx, request->promise());
+
+    // Step 6.
+    Rooted<PromiseObject*> promise(cx, CreatePromiseObjectWithoutResolutionFunctions(cx));
+    if (!promise)
+        return false;
+
+    // Step 7.
+    if (!ResolvePromiseInternal(cx, promise, value))
+        return false;
+
+    // Steps 8-9.
+    RootedValue onFulfilled(cx, Int32Value(done
+                                           ? PromiseHandlerAsyncIteratorValueUnwrapDone
+                                           : PromiseHandlerAsyncIteratorValueUnwrapNotDone));
+
+    RootedObject incumbentGlobal(cx);
+    if (!GetObjectFromIncumbentGlobal(cx, &incumbentGlobal))
+        return false;
+
+    // Step 10.
+    Rooted<PromiseReactionRecord*> reaction(cx, NewReactionRecord(cx, resultPromise, onFulfilled,
+                                                                  UndefinedHandleValue,
+                                                                  nullptr, nullptr,
+                                                                  incumbentGlobal));
+    if (!reaction)
+        return false;
+
+    if (!PerformPromiseThenWithReaction(cx, promise, reaction))
+        return false;
+
+    // Step 11.
+    if (!AsyncGeneratorResumeNext(cx, asyncGenObj))
+        return false;
+
+    // Step 12.
+    return true;
+}
+
+// Async Iteration proposal 6.4.3.4.
+MOZ_MUST_USE bool
+js::AsyncGeneratorReject(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
+                         HandleValue exception)
+{
+    // Step 1 (implicit).
+
+    // Steps 2-3.
+    MOZ_ASSERT(!asyncGenObj->isQueueEmpty());
+
+    // Step 4.
+    Rooted<AsyncGeneratorRequest*> request(
+        cx, AsyncGeneratorObject::dequeueRequest(cx, asyncGenObj));
+    if (!request)
+        return false;
+
+    // Step 5.
+    RootedObject resultPromise(cx, request->promise());
+
+    // Step 6.
+    if (!RejectMaybeWrappedPromise(cx, resultPromise, exception))
+        return false;
+
+    // Step 7.
+    if (!AsyncGeneratorResumeNext(cx, asyncGenObj))
+        return false;
+
+    // Step 8.
+    return true;
+}
+
+// Async Iteration proposal 6.4.3.6.
+MOZ_MUST_USE bool
+js::AsyncGeneratorEnqueue(JSContext* cx, HandleValue asyncGenVal,
+                          CompletionKind completionKind, HandleValue completionValue,
+                          MutableHandleValue result)
+{
+    // Step 1 (implicit).
+
+    // Step 2.
+    RootedObject resultPromise(cx, CreatePromiseObjectWithoutResolutionFunctions(cx));
+    if (!resultPromise)
+        return false;
+
+    // Step 3.
+    if (!asyncGenVal.isObject() || !asyncGenVal.toObject().is<AsyncGeneratorObject>()) {
+        // Step 3.a.
+        RootedValue badGeneratorError(cx);
+        if (!GetTypeError(cx, JSMSG_NOT_AN_ASYNC_GENERATOR, &badGeneratorError))
+            return false;
+
+        // Step 3.b.
+        if (!RejectMaybeWrappedPromise(cx, resultPromise, badGeneratorError))
+            return false;
+
+        // Step 3.c.
+        result.setObject(*resultPromise);
+        return true;
+    }
+
+    Rooted<AsyncGeneratorObject*> asyncGenObj(
+        cx, &asyncGenVal.toObject().as<AsyncGeneratorObject>());
+
+    // Step 5 (reordered).
+    Rooted<AsyncGeneratorRequest*> request(
+        cx, AsyncGeneratorRequest::create(cx, completionKind, completionValue, resultPromise));
+    if (!request)
+        return false;
+
+    // Steps 4, 6.
+    if (!AsyncGeneratorObject::enqueueRequest(cx, asyncGenObj, request))
+        return false;
+
+    // Step 7.
+    if (!asyncGenObj->isExecuting()) {
+        // Step 8.
+        if (!AsyncGeneratorResumeNext(cx, asyncGenObj))
+            return false;
+    }
+
+    // Step 9.
+    result.setObject(*resultPromise);
+    return true;
+}
+
 // ES2016, 25.4.5.3.
 bool
 js::Promise_then(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     // Step 1.
     RootedValue promiseVal(cx, args.thisv());
--- a/js/src/builtin/Promise.h
+++ b/js/src/builtin/Promise.h
@@ -130,16 +130,36 @@ MOZ_MUST_USE bool
 AsyncFunctionReturned(JSContext* cx, Handle<PromiseObject*> resultPromise, HandleValue value);
 
 MOZ_MUST_USE bool
 AsyncFunctionThrown(JSContext* cx, Handle<PromiseObject*> resultPromise);
 
 MOZ_MUST_USE bool
 AsyncFunctionAwait(JSContext* cx, Handle<PromiseObject*> resultPromise, HandleValue value);
 
+class AsyncGeneratorObject;
+
+MOZ_MUST_USE bool
+AsyncGeneratorAwait(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj, HandleValue value);
+
+MOZ_MUST_USE bool
+AsyncGeneratorResolve(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
+                      HandleValue value, bool done);
+
+MOZ_MUST_USE bool
+AsyncGeneratorReject(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
+                     HandleValue exception);
+
+MOZ_MUST_USE bool
+AsyncGeneratorEnqueue(JSContext* cx, HandleValue asyncGenVal, CompletionKind completionKind,
+                      HandleValue completionValue, MutableHandleValue result);
+
+bool
+AsyncFromSyncIteratorMethod(JSContext* cx, CallArgs& args, CompletionKind completionKind);
+
 /**
  * A PromiseTask represents a task that can be dispatched to a helper thread
  * (via StartPromiseTask), executed (by implementing PromiseTask::execute()),
  * and then resolved back on the original JSContext owner thread.
  * Because it contains a PersistentRooted, a PromiseTask will only be destroyed
  * on the JSContext's owner thread.
  */
 class PromiseTask : public JS::AsyncTask
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -773,8 +773,20 @@ frontend::CompileStandaloneAsyncFunction
                                          JS::SourceBufferHolder& srcBuf,
                                          const Maybe<uint32_t>& parameterListEnd)
 {
     RootedScope emptyGlobalScope(cx, &cx->global()->emptyGlobalScope());
 
     BytecodeCompiler compiler(cx, cx->tempLifoAlloc(), options, srcBuf, emptyGlobalScope);
     return compiler.compileStandaloneFunction(fun, NotGenerator, AsyncFunction, parameterListEnd);
 }
+
+bool
+frontend::CompileStandaloneAsyncGenerator(JSContext* cx, MutableHandleFunction fun,
+                                          const ReadOnlyCompileOptions& options,
+                                          JS::SourceBufferHolder& srcBuf,
+                                          const Maybe<uint32_t>& parameterListEnd)
+{
+    RootedScope emptyGlobalScope(cx, &cx->global()->emptyGlobalScope());
+
+    BytecodeCompiler compiler(cx, cx->tempLifoAlloc(), options, srcBuf, emptyGlobalScope);
+    return compiler.compileStandaloneFunction(fun, StarGenerator, AsyncFunction, parameterListEnd);
+}
--- a/js/src/frontend/BytecodeCompiler.h
+++ b/js/src/frontend/BytecodeCompiler.h
@@ -85,16 +85,22 @@ CompileStandaloneGenerator(JSContext* cx
 
 MOZ_MUST_USE bool
 CompileStandaloneAsyncFunction(JSContext* cx, MutableHandleFunction fun,
                                const ReadOnlyCompileOptions& options,
                                JS::SourceBufferHolder& srcBuf,
                                const mozilla::Maybe<uint32_t>& parameterListEnd);
 
 MOZ_MUST_USE bool
+CompileStandaloneAsyncGenerator(JSContext* cx, MutableHandleFunction fun,
+                                const ReadOnlyCompileOptions& options,
+                                JS::SourceBufferHolder& srcBuf,
+                                const mozilla::Maybe<uint32_t>& parameterListEnd);
+
+MOZ_MUST_USE bool
 CompileAsyncFunctionBody(JSContext* cx, MutableHandleFunction fun,
                          const ReadOnlyCompileOptions& options,
                          Handle<PropertyNameVector> formals, JS::SourceBufferHolder& srcBuf);
 
 ScriptSourceObject*
 CreateScriptSourceObject(JSContext* cx, const ReadOnlyCompileOptions& options,
                          const mozilla::Maybe<uint32_t>& parameterListEnd = mozilla::Nothing());
 
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -1999,21 +1999,25 @@ class ForOfLoopControl : public LoopCont
     //         throw e;
     //       IteratorClose(iterator, { throw, e });
     //     }
     //   }
     Maybe<TryEmitter> tryCatch_;
 
     bool allowSelfHosted_;
 
+    IteratorKind iterKind_;
+
   public:
-    ForOfLoopControl(BytecodeEmitter* bce, int32_t iterDepth, bool allowSelfHosted)
+    ForOfLoopControl(BytecodeEmitter* bce, int32_t iterDepth, bool allowSelfHosted,
+                     IteratorKind iterKind)
       : LoopControl(bce, StatementKind::ForOfLoop),
         iterDepth_(iterDepth),
-        allowSelfHosted_(allowSelfHosted)
+        allowSelfHosted_(allowSelfHosted),
+        iterKind_(iterKind)
     {
     }
 
     bool emitBeginCodeNeedingIteratorClose(BytecodeEmitter* bce) {
         tryCatch_.emplace(bce, TryEmitter::TryCatch, TryEmitter::DontUseRetVal);
 
         if (!tryCatch_->emitTry())
             return false;
@@ -2059,17 +2063,17 @@ class ForOfLoopControl : public LoopCont
 
         tryCatch_.reset();
         return true;
     }
 
     bool emitIteratorClose(BytecodeEmitter* bce,
                            CompletionKind completionKind = CompletionKind::Normal) {
         ptrdiff_t start = bce->offset();
-        if (!bce->emitIteratorClose(completionKind, allowSelfHosted_))
+        if (!bce->emitIteratorClose(iterKind_, completionKind, allowSelfHosted_))
             return false;
         ptrdiff_t end = bce->offset();
         return bce->tryNoteList.append(JSTRY_FOR_OF_ITERCLOSE, 0, start, end);
     }
 
     bool emitPrepareForNonLocalJump(BytecodeEmitter* bce, bool isTarget) {
         // Pop unnecessary values from the stack.  Effectively this means
         // leaving try-catch block.  However, the performing IteratorClose can
@@ -3672,16 +3676,28 @@ BytecodeEmitter::emitFinishIteratorResul
     if (!emit1(done ? JSOP_TRUE : JSOP_FALSE))
         return false;
     if (!emitIndex32(JSOP_INITPROP, done_id))
         return false;
     return true;
 }
 
 bool
+BytecodeEmitter::emitToIteratorResult(bool done)
+{
+    if (!emitPrepareIteratorResult())    // VALUE OBJ
+        return false;
+    if (!emit1(JSOP_SWAP))               // OBJ VALUE
+        return false;
+    if (!emitFinishIteratorResult(done)) // RESULT
+        return false;
+    return true;
+}
+
+bool
 BytecodeEmitter::emitGetNameAtLocation(JSAtom* name, const NameLocation& loc, bool callContext)
 {
     switch (loc.kind()) {
       case NameLocation::Kind::Dynamic:
         if (!emitAtomOp(name, JSOP_GETNAME))
             return false;
         break;
 
@@ -5205,38 +5221,46 @@ BytecodeEmitter::emitSetOrInitializeDest
         if (!emit1(JSOP_POP))
             return false;
     }
 
     return true;
 }
 
 bool
-BytecodeEmitter::emitIteratorNext(ParseNode* pn, bool allowSelfHosted /* = false */)
+BytecodeEmitter::emitIteratorNext(ParseNode* pn, IteratorKind iterKind /* = IteratorKind::Sync */,
+                                  bool allowSelfHosted /* = false */)
 {
     MOZ_ASSERT(allowSelfHosted || emitterMode != BytecodeEmitter::SelfHosting,
                ".next() iteration is prohibited in self-hosted code because it "
                "can run user-modifiable iteration code");
 
     if (!emit1(JSOP_DUP))                                 // ... ITER ITER
         return false;
     if (!emitAtomOp(cx->names().next, JSOP_CALLPROP))     // ... ITER NEXT
         return false;
     if (!emit1(JSOP_SWAP))                                // ... NEXT ITER
         return false;
     if (!emitCall(JSOP_CALL, 0, pn))                      // ... RESULT
         return false;
+
+    if (iterKind == IteratorKind::Async) {
+        if (!emitAwait())                                 // ... RESULT
+            return false;
+    }
+
     if (!emitCheckIsObj(CheckIsObjectKind::IteratorNext)) // ... RESULT
         return false;
     checkTypeSet(JSOP_CALL);
     return true;
 }
 
 bool
-BytecodeEmitter::emitIteratorClose(CompletionKind completionKind /* = CompletionKind::Normal */,
+BytecodeEmitter::emitIteratorClose(IteratorKind iterKind /* = IteratorKind::Sync */,
+                                   CompletionKind completionKind /* = CompletionKind::Normal */,
                                    bool allowSelfHosted /* = false */)
 {
     MOZ_ASSERT(allowSelfHosted || emitterMode != BytecodeEmitter::SelfHosting,
                ".close() on iterators is prohibited in self-hosted code because it "
                "can run user-modifiable iteration code");
 
     // Generate inline logic corresponding to IteratorClose (ES 7.4.6).
     //
@@ -5311,26 +5335,31 @@ BytecodeEmitter::emitIteratorClose(Compl
         if (!emitDupAt(2))                                // ... RET ITER UNDEF RET ITER
             return false;
     }
 
     if (!emitCall(JSOP_CALL, 0))                          // ... ... RESULT
         return false;
     checkTypeSet(JSOP_CALL);
 
+    if (iterKind == IteratorKind::Async) {
+        if (!emitAwait())                                 // ... ... RESULT
+            return false;
+    }
+
     if (completionKind == CompletionKind::Throw) {
         if (!emit1(JSOP_SWAP))                            // ... RET ITER RESULT UNDEF
             return false;
         if (!emit1(JSOP_POP))                             // ... RET ITER RESULT
             return false;
 
         if (!tryCatch->emitCatch())                       // ... RET ITER RESULT
             return false;
 
-        // Just ignore the exception thrown by call.
+        // Just ignore the exception thrown by call and await.
         if (!emit1(JSOP_EXCEPTION))                       // ... RET ITER RESULT EXC
             return false;
         if (!emit1(JSOP_POP))                             // ... RET ITER RESULT
             return false;
 
         if (!tryCatch->emitEnd())                         // ... RET ITER RESULT
             return false;
 
@@ -6742,16 +6771,73 @@ BytecodeEmitter::emitIterator()
         return false;
     checkTypeSet(JSOP_CALLITER);
     if (!emitCheckIsObj(CheckIsObjectKind::GetIterator))          // ITER
         return false;
     return true;
 }
 
 bool
+BytecodeEmitter::emitAsyncIterator()
+{
+    // Convert iterable to iterator.
+    if (!emit1(JSOP_DUP))                                         // OBJ OBJ
+        return false;
+    if (!emit2(JSOP_SYMBOL, uint8_t(JS::SymbolCode::asyncIterator))) // OBJ OBJ @@ASYNCITERATOR
+        return false;
+    if (!emitElemOpBase(JSOP_CALLELEM))                           // OBJ ITERFN
+        return false;
+
+    IfThenElseEmitter ifAsyncIterIsUndefined(this);
+    if (!emit1(JSOP_DUP))                                         // OBJ ITERFN ITERFN
+        return false;
+    if (!emit1(JSOP_UNDEFINED))                                   // OBJ ITERFN ITERFN UNDEF
+        return false;
+    if (!emit1(JSOP_EQ))                                          // OBJ ITERFN EQ
+        return false;
+    if (!ifAsyncIterIsUndefined.emitIfElse())                     // OBJ ITERFN
+        return false;
+
+    if (!emit1(JSOP_POP))                                         // OBJ
+        return false;
+    if (!emit1(JSOP_DUP))                                         // OBJ OBJ
+        return false;
+    if (!emit2(JSOP_SYMBOL, uint8_t(JS::SymbolCode::iterator)))   // OBJ OBJ @@ITERATOR
+        return false;
+    if (!emitElemOpBase(JSOP_CALLELEM))                           // OBJ ITERFN
+        return false;
+    if (!emit1(JSOP_SWAP))                                        // ITERFN OBJ
+        return false;
+    if (!emitCall(JSOP_CALLITER, 0))                              // ITER
+        return false;
+    checkTypeSet(JSOP_CALLITER);
+    if (!emitCheckIsObj(CheckIsObjectKind::GetIterator))          // ITER
+        return false;
+
+    if (!emit1(JSOP_TOASYNCITER))                                 // ITER
+        return false;
+
+    if (!ifAsyncIterIsUndefined.emitElse())                       // OBJ ITERFN
+        return false;
+
+    if (!emit1(JSOP_SWAP))                                        // ITERFN OBJ
+        return false;
+    if (!emitCall(JSOP_CALLITER, 0))                              // ITER
+        return false;
+    checkTypeSet(JSOP_CALLITER);
+    if (!emitCheckIsObj(CheckIsObjectKind::GetIterator))          // ITER
+        return false;
+
+    if (!ifAsyncIterIsUndefined.emitEnd())                        // ITER
+        return false;
+
+    return true;
+}
+
+bool
 BytecodeEmitter::emitSpread(bool allowSelfHosted)
 {
     LoopControl loopInfo(this, StatementKind::Spread);
 
     // Jump down to the loop condition to minimize overhead assuming at least
     // one iteration, as the other loop forms do.  Annotate so IonMonkey can
     // find the loop-closing jump.
     unsigned noteIndex;
@@ -6794,17 +6880,17 @@ BytecodeEmitter::emitSpread(bool allowSe
         // and enclosing "update" offsets, as we do with for-loops.
 
         // COME FROM the beginning of the loop to here.
         if (!emitLoopEntry(nullptr, initialJump))         // ITER ARR I
             return false;
 
         if (!emitDupAt(2))                                // ITER ARR I ITER
             return false;
-        if (!emitIteratorNext(nullptr, allowSelfHosted))  // ITER ARR I RESULT
+        if (!emitIteratorNext(nullptr, IteratorKind::Sync, allowSelfHosted))  // ITER ARR I RESULT
             return false;
         if (!emit1(JSOP_DUP))                             // ITER ARR I RESULT RESULT
             return false;
         if (!emitAtomOp(cx->names().done, JSOP_GETPROP))  // ITER ARR I RESULT DONE
             return false;
 
         if (!emitBackwardJump(JSOP_IFEQ, top, &beq, &breakTarget)) // ITER ARR I RESULT
             return false;
@@ -6894,44 +6980,56 @@ BytecodeEmitter::emitForOf(ParseNode* fo
 {
     MOZ_ASSERT(forOfLoop->isKind(PNK_FOR));
     MOZ_ASSERT(forOfLoop->isArity(PN_BINARY));
 
     ParseNode* forOfHead = forOfLoop->pn_left;
     MOZ_ASSERT(forOfHead->isKind(PNK_FOROF));
     MOZ_ASSERT(forOfHead->isArity(PN_TERNARY));
 
+    unsigned iflags = forOfLoop->pn_iflags;
+    IteratorKind iterKind = (iflags & JSITER_FORAWAITOF)
+                            ? IteratorKind::Async
+                            : IteratorKind::Sync;
+    MOZ_ASSERT_IF(iterKind == IteratorKind::Async, sc->asFunctionBox());
+    MOZ_ASSERT_IF(iterKind == IteratorKind::Async, sc->asFunctionBox()->isAsync());
+
     ParseNode* forHeadExpr = forOfHead->pn_kid3;
 
     // Certain builtins (e.g. Array.from) are implemented in self-hosting
     // as for-of loops.
     bool allowSelfHostedIter = false;
     if (emitterMode == BytecodeEmitter::SelfHosting &&
         forHeadExpr->isKind(PNK_CALL) &&
         forHeadExpr->pn_head->name() == cx->names().allowContentIter)
     {
         allowSelfHostedIter = true;
     }
 
     // Evaluate the expression being iterated.
     if (!emitTree(forHeadExpr))                           // ITERABLE
         return false;
-    if (!emitIterator())                                  // ITER
-        return false;
+    if (iterKind == IteratorKind::Async) {
+        if (!emitAsyncIterator())                         // ITER
+            return false;
+    } else {
+        if (!emitIterator())                              // ITER
+            return false;
+    }
 
     int32_t iterDepth = stackDepth;
 
     // For-of loops have both the iterator, the result, and the result.value
     // on the stack. Push undefineds to balance the stack.
     if (!emit1(JSOP_UNDEFINED))                           // ITER RESULT
         return false;
     if (!emit1(JSOP_UNDEFINED))                           // ITER RESULT UNDEF
         return false;
 
-    ForOfLoopControl loopInfo(this, iterDepth, allowSelfHostedIter);
+    ForOfLoopControl loopInfo(this, iterDepth, allowSelfHostedIter, iterKind);
 
     // Annotate so IonMonkey can find the loop-closing jump.
     unsigned noteIndex;
     if (!newSrcNote(SRC_FOR_OF, &noteIndex))
         return false;
 
     JumpList initialJump;
     if (!emitJump(JSOP_GOTO, &initialJump))               // ITER RESULT UNDEF
@@ -7017,17 +7115,17 @@ BytecodeEmitter::emitForOf(ParseNode* fo
 
         if (!emit1(JSOP_SWAP))                            // ITER UNDEF RESULT
             return false;
         if (!emit1(JSOP_POP))                             // ITER UNDEF
             return false;
         if (!emitDupAt(1))                                // ITER UNDEF ITER
             return false;
 
-        if (!emitIteratorNext(forOfHead, allowSelfHostedIter)) // ITER UNDEF RESULT
+        if (!emitIteratorNext(forOfHead, iterKind, allowSelfHostedIter)) // ITER UNDEF RESULT
             return false;
 
         if (!emit1(JSOP_SWAP))                            // ITER RESULT UNDEF
             return false;
 
         if (!emitDupAt(1))                                // ITER RESULT UNDEF RESULT
             return false;
         if (!emitAtomOp(cx->names().done, JSOP_GETPROP))  // ITER RESULT UNDEF DONE
@@ -7836,17 +7934,18 @@ BytecodeEmitter::emitFunction(ParseNode*
     unsigned index = objectList.add(pn->pn_funbox);
 
     /* Non-hoisted functions simply emit their respective op. */
     if (!pn->functionIsHoisted()) {
         /* JSOP_LAMBDA_ARROW is always preceded by a new.target */
         MOZ_ASSERT(fun->isArrow() == (pn->getOp() == JSOP_LAMBDA_ARROW));
         if (funbox->isAsync()) {
             MOZ_ASSERT(!needsProto);
-            return emitAsyncWrapper(index, funbox->needsHomeObject(), fun->isArrow());
+            return emitAsyncWrapper(index, funbox->needsHomeObject(), fun->isArrow(),
+                                    fun->isStarGenerator());
         }
 
         if (fun->isArrow()) {
             if (sc->allowNewTarget()) {
                 if (!emit1(JSOP_NEWTARGET))
                     return false;
             } else {
                 if (!emit1(JSOP_NULL))
@@ -7891,37 +7990,42 @@ BytecodeEmitter::emitFunction(ParseNode*
             RootedModuleObject module(cx, sc->asModuleContext()->module());
             if (!module->noteFunctionDeclaration(cx, name, fun))
                 return false;
         } else {
             MOZ_ASSERT(sc->isGlobalContext() || sc->isEvalContext());
             MOZ_ASSERT(pn->getOp() == JSOP_NOP);
             switchToPrologue();
             if (funbox->isAsync()) {
-                if (!emitAsyncWrapper(index, fun->isMethod(), fun->isArrow()))
+                if (!emitAsyncWrapper(index, fun->isMethod(), fun->isArrow(),
+                                      fun->isStarGenerator()))
+                {
                     return false;
+                }
             } else {
                 if (!emitIndex32(JSOP_LAMBDA, index))
                     return false;
             }
             if (!emit1(JSOP_DEFFUN))
                 return false;
             if (!updateSourceCoordNotes(pn->pn_pos.begin))
                 return false;
             switchToMain();
         }
     } else {
         // For functions nested within functions and blocks, make a lambda and
         // initialize the binding name of the function in the current scope.
 
         bool isAsync = funbox->isAsync();
-        auto emitLambda = [index, isAsync](BytecodeEmitter* bce, const NameLocation&, bool) {
+        bool isStarGenerator = funbox->isStarGenerator();
+        auto emitLambda = [index, isAsync, isStarGenerator](BytecodeEmitter* bce,
+                                                            const NameLocation&, bool) {
             if (isAsync) {
                 return bce->emitAsyncWrapper(index, /* needsHomeObject = */ false,
-                                             /* isArrow = */ false);
+                                             /* isArrow = */ false, isStarGenerator);
             }
             return bce->emitIndexOp(JSOP_LAMBDA, index);
         };
 
         if (!emitInitializeName(name, emitLambda))
             return false;
         if (!emit1(JSOP_POP))
             return false;
@@ -7946,17 +8050,18 @@ BytecodeEmitter::emitAsyncWrapperLambda(
         if (!emitIndex32(JSOP_LAMBDA, index))
             return false;
     }
 
     return true;
 }
 
 bool
-BytecodeEmitter::emitAsyncWrapper(unsigned index, bool needsHomeObject, bool isArrow)
+BytecodeEmitter::emitAsyncWrapper(unsigned index, bool needsHomeObject, bool isArrow,
+                                  bool isStarGenerator)
 {
     // needsHomeObject can be true for propertyList for extended class.
     // In that case push both unwrapped and wrapped function, in order to
     // initialize home object of unwrapped function, and set wrapped function
     // as a property.
     //
     //   lambda       // unwrapped
     //   dup          // unwrapped unwrapped
@@ -7978,18 +8083,23 @@ BytecodeEmitter::emitAsyncWrapper(unsign
     //
     // needsHomeObject is false for other cases, push wrapped function only.
     if (!emitAsyncWrapperLambda(index, isArrow))
         return false;
     if (needsHomeObject) {
         if (!emit1(JSOP_DUP))
             return false;
     }
-    if (!emit1(JSOP_TOASYNC))
-        return false;
+    if (isStarGenerator) {
+        if (!emit1(JSOP_TOASYNCGEN))
+            return false;
+    } else {
+        if (!emit1(JSOP_TOASYNC))
+            return false;
+    }
     return true;
 }
 
 bool
 BytecodeEmitter::emitDo(ParseNode* pn)
 {
     /* Emit an annotated nop so IonBuilder can recognize the 'do' loop. */
     unsigned noteIndex;
@@ -8350,40 +8460,53 @@ BytecodeEmitter::emitYield(ParseNode* pn
 
     if (!emitYieldOp(JSOP_YIELD))
         return false;
 
     return true;
 }
 
 bool
+BytecodeEmitter::emitAwait()
+{
+    if (!emitGetDotGenerator())
+        return false;
+    if (!emitYieldOp(JSOP_AWAIT))
+        return false;
+    return true;
+}
+
+bool
 BytecodeEmitter::emitAwait(ParseNode* pn)
 {
     MOZ_ASSERT(sc->isFunctionBox());
     MOZ_ASSERT(pn->getOp() == JSOP_AWAIT);
 
     if (!emitTree(pn->pn_kid))
         return false;
-    if (!emitGetDotGenerator())
-        return false;
-    if (!emitYieldOp(JSOP_AWAIT))
-        return false;
-    return true;
+    return emitAwait();
 }
 
 bool
 BytecodeEmitter::emitYieldStar(ParseNode* iter)
 {
     MOZ_ASSERT(sc->isFunctionBox());
     MOZ_ASSERT(sc->asFunctionBox()->isStarGenerator());
 
+    bool isAsyncGenerator = sc->asFunctionBox()->isAsync();
+
     if (!emitTree(iter))                                  // ITERABLE
         return false;
-    if (!emitIterator())                                  // ITER
-        return false;
+    if (isAsyncGenerator) {
+        if (!emitAsyncIterator())                         // ITER
+            return false;
+    } else {
+        if (!emitIterator())                              // ITER
+            return false;
+    }
 
     // Initial send value is undefined.
     if (!emit1(JSOP_UNDEFINED))                           // ITER RECEIVED
         return false;
 
     int32_t savedDepthTemp;
     int32_t startDepth = stackDepth;
     MOZ_ASSERT(startDepth >= 2);
@@ -8431,32 +8554,39 @@ BytecodeEmitter::emitYieldStar(ParseNode
         return false;
     savedDepthTemp = stackDepth;
     if (!emit1(JSOP_POP))                                 // ITER RESULT EXCEPTION ITER
         return false;
     // ES 14.4.13, YieldExpression : yield * AssignmentExpression, step 5.b.iii.2
     //
     // If the iterator does not have a "throw" method, it calls IteratorClose
     // and then throws a TypeError.
-    if (!emitIteratorClose())                             // ITER RESULT EXCEPTION
+    IteratorKind iterKind = isAsyncGenerator ? IteratorKind::Async : IteratorKind::Sync;
+    if (!emitIteratorClose(iterKind))                    // ITER RESULT EXCEPTION
         return false;
     if (!emitUint16Operand(JSOP_THROWMSG, JSMSG_ITERATOR_NO_THROW)) // throw
         return false;
     stackDepth = savedDepthTemp;
     if (!ifThrowMethodIsNotDefined.emitEnd())             // ITER OLDRESULT EXCEPTION ITER THROW
         return false;
     // ES 14.4.13, YieldExpression : yield * AssignmentExpression, step 5.b.iii.4.
     // RESULT = ITER.throw(EXCEPTION)                     // ITER OLDRESULT EXCEPTION ITER THROW
     if (!emit1(JSOP_SWAP))                                // ITER OLDRESULT EXCEPTION THROW ITER
         return false;
     if (!emit2(JSOP_PICK, 2))                             // ITER OLDRESULT THROW ITER EXCEPTION
         return false;
     if (!emitCall(JSOP_CALL, 1, iter))                    // ITER OLDRESULT RESULT
         return false;
     checkTypeSet(JSOP_CALL);
+
+    if (isAsyncGenerator) {
+        if (!emitAwait())                                 // ITER OLDRESULT RESULT
+            return false;
+    }
+
     if (!emitCheckIsObj(CheckIsObjectKind::IteratorThrow)) // ITER OLDRESULT RESULT
         return false;
     if (!emit1(JSOP_SWAP))                                // ITER RESULT OLDRESULT
         return false;
     if (!emit1(JSOP_POP))                                 // ITER RESULT
         return false;
     MOZ_ASSERT(this->stackDepth == startDepth);
     JumpList checkResult;
@@ -8513,16 +8643,21 @@ BytecodeEmitter::emitYieldStar(ParseNode
     if (!emit1(JSOP_GETRVAL))                             // ITER OLDRESULT FTYPE FVALUE RET ITER RVAL
         return false;
     if (!emitAtomOp(cx->names().value, JSOP_GETPROP))     // ITER OLDRESULT FTYPE FVALUE RET ITER VALUE
         return false;
     if (!emitCall(JSOP_CALL, 1))                          // ITER OLDRESULT FTYPE FVALUE RESULT
         return false;
     checkTypeSet(JSOP_CALL);
 
+    if (iterKind == IteratorKind::Async) {
+        if (!emitAwait())                                 // ... FTYPE FVALUE RESULT
+            return false;
+    }
+
     // Step v.
     if (!emitCheckIsObj(CheckIsObjectKind::IteratorReturn)) // ITER OLDRESULT FTYPE FVALUE RESULT
         return false;
 
     // Steps vi-viii.
     //
     // Check if the returned object from iterator.return() is done. If not,
     // continuing yielding.
@@ -8530,16 +8665,22 @@ BytecodeEmitter::emitYieldStar(ParseNode
     if (!emit1(JSOP_DUP))                                 // ITER OLDRESULT FTYPE FVALUE RESULT RESULT
         return false;
     if (!emitAtomOp(cx->names().done, JSOP_GETPROP))      // ITER OLDRESULT FTYPE FVALUE RESULT DONE
         return false;
     if (!ifReturnDone.emitIfElse())                       // ITER OLDRESULT FTYPE FVALUE RESULT
         return false;
     if (!emitAtomOp(cx->names().value, JSOP_GETPROP))     // ITER OLDRESULT FTYPE FVALUE VALUE
         return false;
+
+    if (isAsyncGenerator) {
+        if (!emitAwait())                                 // ITER OLDRESULT FTYPE FVALUE VALUE
+            return false;
+    }
+
     if (!emitPrepareIteratorResult())                     // ITER OLDRESULT FTYPE FVALUE VALUE RESULT
         return false;
     if (!emit1(JSOP_SWAP))                                // ITER OLDRESULT FTYPE FVALUE RESULT VALUE
         return false;
     if (!emitFinishIteratorResult(true))                  // ITER OLDRESULT FTYPE FVALUE RESULT
         return false;
     if (!emit1(JSOP_SETRVAL))                             // ITER OLDRESULT FTYPE FVALUE
         return false;
@@ -8585,19 +8726,25 @@ BytecodeEmitter::emitYieldStar(ParseNode
     if (!emitAtomOp(cx->names().next, JSOP_CALLPROP))            // RECEIVED ITER ITER NEXT
         return false;
     if (!emit1(JSOP_SWAP))                                       // RECEIVED ITER NEXT ITER
         return false;
     if (!emit2(JSOP_PICK, 3))                                    // ITER NEXT ITER RECEIVED
         return false;
     if (!emitCall(JSOP_CALL, 1, iter))                           // ITER RESULT
         return false;
+    checkTypeSet(JSOP_CALL);
+
+    if (isAsyncGenerator) {
+        if (!emitAwait())                                        // ITER RESULT RESULT
+            return false;
+    }
+
     if (!emitCheckIsObj(CheckIsObjectKind::IteratorNext))        // ITER RESULT
         return false;
-    checkTypeSet(JSOP_CALL);
     MOZ_ASSERT(this->stackDepth == startDepth);
 
     if (!emitJumpTargetAndPatch(checkResult))                    // checkResult:
         return false;
 
     // if (!result.done) goto tryStart;                          // ITER RESULT
     if (!emit1(JSOP_DUP))                                        // ITER RESULT RESULT
         return false;
@@ -8614,16 +8761,21 @@ BytecodeEmitter::emitYieldStar(ParseNode
     // result.value
     if (!emit1(JSOP_SWAP))                                       // RESULT ITER
         return false;
     if (!emit1(JSOP_POP))                                        // RESULT
         return false;
     if (!emitAtomOp(cx->names().value, JSOP_GETPROP))            // VALUE
         return false;
 
+    if (isAsyncGenerator) {
+        if (!emitAwait())                                        // VALUE
+            return false;
+    }
+
     MOZ_ASSERT(this->stackDepth == startDepth - 1);
 
     return true;
 }
 
 bool
 BytecodeEmitter::emitStatementList(ParseNode* pn)
 {
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* JS bytecode generation. */
 
 #ifndef frontend_BytecodeEmitter_h
 #define frontend_BytecodeEmitter_h
 
 #include "jscntxt.h"
+#include "jsiter.h"
 #include "jsopcode.h"
 #include "jsscript.h"
 
 #include "ds/InlineTable.h"
 #include "frontend/Parser.h"
 #include "frontend/SharedContext.h"
 #include "frontend/SourceNotes.h"
 #include "vm/Interpreter.h"
@@ -600,31 +601,34 @@ struct MOZ_STACK_CLASS BytecodeEmitter
                                             ParseNode* initializer);
 
     MOZ_MUST_USE bool emitNewInit(JSProtoKey key);
     MOZ_MUST_USE bool emitSingletonInitialiser(ParseNode* pn);
 
     MOZ_MUST_USE bool emitPrepareIteratorResult();
     MOZ_MUST_USE bool emitFinishIteratorResult(bool done);
     MOZ_MUST_USE bool iteratorResultShape(unsigned* shape);
+    MOZ_MUST_USE bool emitToIteratorResult(bool done);
 
     MOZ_MUST_USE bool emitGetDotGenerator();
 
     MOZ_MUST_USE bool emitInitialYield(ParseNode* pn);
     MOZ_MUST_USE bool emitYield(ParseNode* pn);
     MOZ_MUST_USE bool emitYieldOp(JSOp op);
     MOZ_MUST_USE bool emitYieldStar(ParseNode* iter);
+    MOZ_MUST_USE bool emitAwait();
     MOZ_MUST_USE bool emitAwait(ParseNode* pn);
 
     MOZ_MUST_USE bool emitPropLHS(ParseNode* pn);
     MOZ_MUST_USE bool emitPropOp(ParseNode* pn, JSOp op);
     MOZ_MUST_USE bool emitPropIncDec(ParseNode* pn);
 
     MOZ_MUST_USE bool emitAsyncWrapperLambda(unsigned index, bool isArrow);
-    MOZ_MUST_USE bool emitAsyncWrapper(unsigned index, bool needsHomeObject, bool isArrow);
+    MOZ_MUST_USE bool emitAsyncWrapper(unsigned index, bool needsHomeObject, bool isArrow,
+                                       bool isStarGenerator);
 
     MOZ_MUST_USE bool emitComputedPropertyName(ParseNode* computedPropName);
 
     // Emit bytecode to put operands for a JSOP_GETELEM/CALLELEM/SETELEM/DELELEM
     // opcode onto the stack in the right order. In the case of SETELEM, the
     // value to be assigned must already be pushed.
     enum class EmitElemOption { Get, Set, Call, IncDec, CompoundAssign, Ref };
     MOZ_MUST_USE bool emitElemOperands(ParseNode* pn, EmitElemOption opts);
@@ -688,20 +692,24 @@ struct MOZ_STACK_CLASS BytecodeEmitter
     // Throw a TypeError if the value atop the stack isn't convertible to an
     // object, with no overall effect on the stack.
     MOZ_MUST_USE bool emitRequireObjectCoercible();
 
     // emitIterator expects the iterable to already be on the stack.
     // It will replace that stack value with the corresponding iterator
     MOZ_MUST_USE bool emitIterator();
 
+    MOZ_MUST_USE bool emitAsyncIterator();
+
     // Pops iterator from the top of the stack. Pushes the result of |.next()|
     // onto the stack.
-    MOZ_MUST_USE bool emitIteratorNext(ParseNode* pn, bool allowSelfHosted = false);
-    MOZ_MUST_USE bool emitIteratorClose(CompletionKind completionKind = CompletionKind::Normal,
+    MOZ_MUST_USE bool emitIteratorNext(ParseNode* pn, IteratorKind kind = IteratorKind::Sync,
+                                       bool allowSelfHosted = false);
+    MOZ_MUST_USE bool emitIteratorClose(IteratorKind iterKind = IteratorKind::Sync,
+                                        CompletionKind completionKind = CompletionKind::Normal,
                                         bool allowSelfHosted = false);
 
     template <typename InnerEmitter>
     MOZ_MUST_USE bool wrapWithDestructuringIteratorCloseTryNote(int32_t iterDepth,
                                                                 InnerEmitter emitter);
 
     // Check if the value on top of the stack is "undefined". If so, replace
     // that value on the stack with the value defined by |defaultExpr|.
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -3748,19 +3748,21 @@ Parser<ParseHandler>::functionStmt(uint3
     }
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt))
         return null();
 
     GeneratorKind generatorKind = NotGenerator;
     if (tt == TOK_MUL) {
-        if (asyncKind != SyncFunction) {
-            error(JSMSG_ASYNC_GENERATOR);
-            return null();
+        if (!asyncIterationSupported()) {
+            if (asyncKind != SyncFunction) {
+                error(JSMSG_ASYNC_GENERATOR);
+                return null();
+            }
         }
         generatorKind = StarGenerator;
         if (!tokenStream.getToken(&tt))
             return null();
     }
 
     RootedPropertyName name(context);
     if (TokenKindIsPossibleIdentifier(tt)) {
@@ -3820,19 +3822,21 @@ Parser<ParseHandler>::functionExpr(uint3
 
     AutoAwaitIsKeyword<ParseHandler> awaitIsKeyword(this, asyncKind == AsyncFunction);
     GeneratorKind generatorKind = NotGenerator;
     TokenKind tt;
     if (!tokenStream.getToken(&tt))
         return null();
 
     if (tt == TOK_MUL) {
-        if (asyncKind != SyncFunction) {
-            error(JSMSG_ASYNC_GENERATOR);
-            return null();
+        if (!asyncIterationSupported()) {
+            if (asyncKind != SyncFunction) {
+                error(JSMSG_ASYNC_GENERATOR);
+                return null();
+            }
         }
         generatorKind = StarGenerator;
         if (!tokenStream.getToken(&tt))
             return null();
     }
 
     YieldHandling yieldHandling = GetYieldHandling(generatorKind);
 
@@ -5852,16 +5856,17 @@ Parser<ParseHandler>::matchInOrOf(bool* 
 
     MOZ_ASSERT_IF(*isForInp || *isForOfp, *isForInp != *isForOfp);
     return true;
 }
 
 template <class ParseHandler>
 bool
 Parser<ParseHandler>::forHeadStart(YieldHandling yieldHandling,
+                                   IteratorKind iterKind,
                                    ParseNodeKind* forHeadKind,
                                    Node* forInitialPart,
                                    Maybe<ParseContext::Scope>& forLoopLexicalScope,
                                    Node* forInOrOfExpression)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LP));
 
     TokenKind tt;
@@ -5942,16 +5947,21 @@ Parser<ParseHandler>::forHeadStart(Yield
     *forInitialPart = expr(InProhibited, yieldHandling, TripledotProhibited, &possibleError);
     if (!*forInitialPart)
         return false;
 
     bool isForIn, isForOf;
     if (!matchInOrOf(&isForIn, &isForOf))
         return false;
 
+    if (iterKind == IteratorKind::Async && !isForOf) {
+        error(JSMSG_FOR_AWAIT_NOT_OF);
+        return false;
+    }
+
     // If we don't encounter 'in'/'of', we have a for(;;) loop.  We've handled
     // the init expression; the caller handles the rest.  Allow the Operand
     // modifier when regetting: Operand must be used to examine the ';' in
     // |for (;|, and our caller handles this case and that.
     if (!isForIn && !isForOf) {
         if (!possibleError.checkForExpressionError())
             return false;
         *forHeadKind = PNK_FORHEAD;
@@ -6015,31 +6025,45 @@ typename ParseHandler::Node
 Parser<ParseHandler>::forStatement(YieldHandling yieldHandling)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR));
     uint32_t begin = pos().begin;
 
     ParseContext::Statement stmt(pc, StatementKind::ForLoop);
 
     bool isForEach = false;
+    IteratorKind iterKind = IteratorKind::Sync;
     unsigned iflags = 0;
 
     if (allowsForEachIn()) {
         bool matched;
         if (!tokenStream.matchToken(&matched, TOK_EACH))
             return null();
         if (matched) {
             iflags = JSITER_FOREACH;
             isForEach = true;
             addTelemetry(JSCompartment::DeprecatedForEach);
             if (!warnOnceAboutForEach())
                 return null();
         }
     }
 
+    if (asyncIterationSupported()) {
+        if (pc->isAsync()) {
+            bool matched;
+            if (!tokenStream.matchToken(&matched, TOK_AWAIT))
+                return null();
+
+            if (matched) {
+                iflags |= JSITER_FORAWAITOF;
+                iterKind = IteratorKind::Async;
+            }
+        }
+    }
+
     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
 
     // PNK_FORHEAD, PNK_FORIN, or PNK_FOROF depending on the loop type.
     ParseNodeKind headKind;
 
     // |x| in either |for (x; ...; ...)| or |for (x in/of ...)|.
     Node startNode;
 
@@ -6067,17 +6091,17 @@ Parser<ParseHandler>::forStatement(Yield
     //
     // ...OR, parse up to the first ';' in a C-style for-loop:
     //
     //   for (...; ...; ...) ...
     //           ^next token
     //
     // In either case the subsequent token can be consistently accessed using
     // TokenStream::None semantics.
-    if (!forHeadStart(yieldHandling, &headKind, &startNode, forLoopLexicalScope,
+    if (!forHeadStart(yieldHandling, iterKind, &headKind, &startNode, forLoopLexicalScope,
                       &iteratedExpr))
     {
         return null();
     }
 
     MOZ_ASSERT(headKind == PNK_FORIN || headKind == PNK_FOROF || headKind == PNK_FORHEAD);
 
     Node forHead;
@@ -9283,21 +9307,16 @@ Parser<ParseHandler>::propertyName(Yield
     TokenKind ltok;
     if (!tokenStream.getToken(&ltok))
         return null();
 
     MOZ_ASSERT(ltok != TOK_RC, "caller should have handled TOK_RC");
 
     bool isGenerator = false;
     bool isAsync = false;
-    if (ltok == TOK_MUL) {
-        isGenerator = true;
-        if (!tokenStream.getToken(&ltok))
-            return null();
-    }
 
     if (ltok == TOK_ASYNC) {
         // AsyncMethod[Yield, Await]:
         //   async [no LineTerminator here] PropertyName[?Yield, ?Await] ...
         //
         // PropertyName:
         //   LiteralPropertyName
         //   ComputedPropertyName[?Yield, ?Await]
@@ -9316,19 +9335,26 @@ Parser<ParseHandler>::propertyName(Yield
             TokenKindIsPossibleIdentifierName(tt))
         {
             isAsync = true;
             tokenStream.consumeKnownToken(tt);
             ltok = tt;
         }
     }
 
-    if (isAsync && isGenerator) {
-        error(JSMSG_ASYNC_GENERATOR);
-        return null();
+    if (ltok == TOK_MUL) {
+        if (!asyncIterationSupported()) {
+            if (isAsync) {
+                error(JSMSG_ASYNC_GENERATOR);
+                return null();
+            }
+        }
+        isGenerator = true;
+        if (!tokenStream.getToken(&ltok))
+            return null();
     }
 
     propAtom.set(nullptr);
     Node propName;
     switch (ltok) {
       case TOK_NUMBER:
         propAtom.set(DoubleToAtom(context, tokenStream.currentToken().number()));
         if (!propAtom.get())
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -8,16 +8,17 @@
 
 #ifndef frontend_Parser_h
 #define frontend_Parser_h
 
 #include "mozilla/Array.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/TypeTraits.h"
 
+#include "jsiter.h"
 #include "jspubtd.h"
 
 #include "frontend/BytecodeCompiler.h"
 #include "frontend/FullParseHandler.h"
 #include "frontend/NameAnalysisTypes.h"
 #include "frontend/NameCollections.h"
 #include "frontend/SharedContext.h"
 #include "frontend/SyntaxParseHandler.h"
@@ -816,16 +817,26 @@ class ParserBase : public StrictModeGett
     // whether it's prohibited due to strictness, JS version, or occurrence
     // inside a star generator.
     bool yieldExpressionsSupported() {
         return (versionNumber() >= JSVERSION_1_7 && !pc->isAsync()) ||
                pc->isStarGenerator() ||
                pc->isLegacyGenerator();
     }
 
+    bool asyncIterationSupported() {
+#ifdef RELEASE_OR_BETA
+        return false;
+#else
+        // Expose Async Iteration only to web content until the spec proposal
+        // gets stable.
+        return !options().isProbablySystemOrAddonCode;
+#endif
+    }
+
     virtual bool strictMode() { return pc->sc()->strict(); }
     bool setLocalStrictMode(bool strict) {
         MOZ_ASSERT(tokenStream.debugHasNoLookahead());
         return pc->sc()->setLocalStrictMode(strict);
     }
 
     const ReadOnlyCompileOptions& options() const {
         return tokenStream.options();
@@ -1170,16 +1181,17 @@ class Parser final : public ParserBase, 
 
     Node blockStatement(YieldHandling yieldHandling,
                         unsigned errorNumber = JSMSG_CURLY_IN_COMPOUND);
     Node doWhileStatement(YieldHandling yieldHandling);
     Node whileStatement(YieldHandling yieldHandling);
 
     Node forStatement(YieldHandling yieldHandling);
     bool forHeadStart(YieldHandling yieldHandling,
+                      IteratorKind iterKind,
                       ParseNodeKind* forHeadKind,
                       Node* forInitialPart,
                       mozilla::Maybe<ParseContext::Scope>& forLetImpliedScope,
                       Node* forInOrOfExpression);
     Node expressionAfterForInOrOf(ParseNodeKind forHeadKind, YieldHandling yieldHandling);
 
     Node switchStatement(YieldHandling yieldHandling);
     Node continueStatement(YieldHandling yieldHandling);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/modules/bug-1284486-2.js
@@ -0,0 +1,24 @@
+// |jit-test| error: InternalError
+
+// This tests that attempting to perform ModuleDeclarationInstantation a second
+// time after a failure throws an error. Doing this would be a bug in the module
+// loader, which is expected to throw away modules if there is an error
+// instantiating them.
+//
+// The first attempt fails becuase module 'a' is not available. The second
+// attempt fails because of the previous failure.
+//
+// This test exercises the path where the previously instantiated module is
+// re-instantiated directly.
+
+let moduleRepo = {};
+setModuleResolveHook(function(module, specifier) {
+    return moduleRepo[specifier];
+});
+let c;
+try {
+    let b = moduleRepo['b'] = parseModule("export var b = 3; export var c = 4;");
+    c = moduleRepo['c'] = parseModule("export * from 'a'; export * from 'b';");
+    c.declarationInstantiation();
+} catch (exc) {}
+c.declarationInstantiation();
--- a/js/src/jit-test/tests/modules/bug-1284486.js
+++ b/js/src/jit-test/tests/modules/bug-1284486.js
@@ -3,16 +3,19 @@
 // This tests that attempting to perform ModuleDeclarationInstantation a second
 // time after a failure throws an error. Doing this would be a bug in the module
 // loader, which is expected to throw away modules if there is an error
 // instantiating them.
 //
 // The first attempt fails becuase module 'a' is not available. The second
 // attempt fails because of the previous failure (it would otherwise succeed as
 // 'a' is now available).
+//
+// This test exercises the path where the previously instantiated module is
+// encountered as an import.
 
 let moduleRepo = {};
 setModuleResolveHook(function(module, specifier) {
     return moduleRepo[specifier];
 });
 try {
     let b = moduleRepo['b'] = parseModule("export var b = 3; export var c = 4;");
     let c = moduleRepo['c'] = parseModule("export * from 'a'; export * from 'b';");
--- a/js/src/jit-test/tests/wasm/regress/oom-eval.js
+++ b/js/src/jit-test/tests/wasm/regress/oom-eval.js
@@ -2,11 +2,11 @@
 
 if (typeof oomTest !== 'function' || !wasmIsSupported()) {
     print('Missing oomTest or wasm support in wasm/regress/oom-eval');
     quit();
 }
 
 function foo() {
   var g = newGlobal();
-  g.eval(`o = new WebAssembly.Instance(new WebAssemby.Module(wasmTextToBinary('(module (func) (export "" 0))')));`);
+  g.eval(`o = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary('(module (func) (export "" 0))')));`);
 }
 oomTest(foo);
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -20,16 +20,17 @@
 #include "jit/Linker.h"
 #ifdef JS_ION_PERF
 # include "jit/PerfSpewer.h"
 #endif
 #include "jit/SharedICHelpers.h"
 #include "jit/VMFunctions.h"
 #include "js/UniquePtr.h"
 #include "vm/AsyncFunction.h"
+#include "vm/AsyncIteration.h"
 #include "vm/EnvironmentObject.h"
 #include "vm/Interpreter.h"
 #include "vm/TraceLogging.h"
 #include "vtune/VTuneWrapper.h"
 
 #include "jsscriptinlines.h"
 
 #include "jit/BaselineFrameInfo-inl.h"
@@ -3917,16 +3918,60 @@ BaselineCompiler::emit_JSOP_TOASYNC()
         return false;
 
     masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
     frame.pop();
     frame.push(R0);
     return true;
 }
 
+typedef JSObject* (*ToAsyncGenFn)(JSContext*, HandleFunction);
+static const VMFunction ToAsyncGenInfo =
+    FunctionInfo<ToAsyncGenFn>(js::WrapAsyncGenerator, "ToAsyncGen");
+
+bool
+BaselineCompiler::emit_JSOP_TOASYNCGEN()
+{
+    frame.syncStack(0);
+    masm.unboxObject(frame.addressOfStackValue(frame.peek(-1)), R0.scratchReg());
+
+    prepareVMCall();
+    pushArg(R0.scratchReg());
+
+    if (!callVM(ToAsyncGenInfo))
+        return false;
+
+    masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
+    frame.pop();
+    frame.push(R0);
+    return true;
+}
+
+typedef JSObject* (*ToAsyncIterFn)(JSContext*, HandleObject);
+static const VMFunction ToAsyncIterInfo =
+    FunctionInfo<ToAsyncIterFn>(js::CreateAsyncFromSyncIterator, "ToAsyncIter");
+
+bool
+BaselineCompiler::emit_JSOP_TOASYNCITER()
+{
+    frame.syncStack(0);
+    masm.unboxObject(frame.addressOfStackValue(frame.peek(-1)), R0.scratchReg());
+
+    prepareVMCall();
+    pushArg(R0.scratchReg());
+
+    if (!callVM(ToAsyncIterInfo))
+        return false;
+
+    masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
+    frame.pop();
+    frame.push(R0);
+    return true;
+}
+
 typedef bool (*ThrowObjectCoercibleFn)(JSContext*, HandleValue);
 static const VMFunction ThrowObjectCoercibleInfo =
     FunctionInfo<ThrowObjectCoercibleFn>(ThrowObjectCoercible, "ThrowObjectCoercible");
 
 bool
 BaselineCompiler::emit_JSOP_CHECKOBJCOERCIBLE()
 {
     frame.syncStack(0);
--- a/js/src/jit/BaselineCompiler.h
+++ b/js/src/jit/BaselineCompiler.h
@@ -192,16 +192,18 @@ namespace jit {
     _(JSOP_PUSHVARENV)         \
     _(JSOP_POPVARENV)          \
     _(JSOP_EXCEPTION)          \
     _(JSOP_DEBUGGER)           \
     _(JSOP_ARGUMENTS)          \
     _(JSOP_RUNONCE)            \
     _(JSOP_REST)               \
     _(JSOP_TOASYNC)            \
+    _(JSOP_TOASYNCGEN)         \
+    _(JSOP_TOASYNCITER)        \
     _(JSOP_TOID)               \
     _(JSOP_TOSTRING)           \
     _(JSOP_TABLESWITCH)        \
     _(JSOP_ITER)               \
     _(JSOP_MOREITER)           \
     _(JSOP_ISNOITER)           \
     _(JSOP_ENDITER)            \
     _(JSOP_ISGENCLOSING)       \
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -36,16 +36,17 @@
 #include "jit/JitSpewer.h"
 #include "jit/Linker.h"
 #include "jit/Lowering.h"
 #include "jit/MIRGenerator.h"
 #include "jit/MoveEmitter.h"
 #include "jit/RangeAnalysis.h"
 #include "jit/SharedICHelpers.h"
 #include "vm/AsyncFunction.h"
+#include "vm/AsyncIteration.h"
 #include "vm/MatchPairs.h"
 #include "vm/RegExpObject.h"
 #include "vm/RegExpStatics.h"
 #include "vm/TraceLogging.h"
 #include "vm/Unicode.h"
 #include "vtune/VTuneWrapper.h"
 
 #include "jsboolinlines.h"
@@ -10691,16 +10692,38 @@ static const VMFunction ToAsyncInfo = Fu
 
 void
 CodeGenerator::visitToAsync(LToAsync* lir)
 {
     pushArg(ToRegister(lir->unwrapped()));
     callVM(ToAsyncInfo, lir);
 }
 
+typedef JSObject* (*ToAsyncGenFn)(JSContext*, HandleFunction);
+static const VMFunction ToAsyncGenInfo =
+    FunctionInfo<ToAsyncGenFn>(js::WrapAsyncGenerator, "ToAsyncGen");
+
+void
+CodeGenerator::visitToAsyncGen(LToAsyncGen* lir)
+{
+    pushArg(ToRegister(lir->unwrapped()));
+    callVM(ToAsyncGenInfo, lir);
+}
+
+typedef JSObject* (*ToAsyncIterFn)(JSContext*, HandleObject);
+static const VMFunction ToAsyncIterInfo =
+    FunctionInfo<ToAsyncIterFn>(js::CreateAsyncFromSyncIterator, "ToAsyncIter");
+
+void
+CodeGenerator::visitToAsyncIter(LToAsyncIter* lir)
+{
+    pushArg(ToRegister(lir->unwrapped()));
+    callVM(ToAsyncIterInfo, lir);
+}
+
 typedef bool (*ToIdFn)(JSContext*, HandleScript, jsbytecode*, HandleValue,
                        MutableHandleValue);
 static const VMFunction ToIdInfo = FunctionInfo<ToIdFn>(ToIdOperation, "ToIdOperation");
 
 void
 CodeGenerator::visitToIdV(LToIdV* lir)
 {
     Label notInt32;
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -291,16 +291,18 @@ class CodeGenerator final : public CodeG
     void visitCallGetProperty(LCallGetProperty* lir);
     void visitCallGetElement(LCallGetElement* lir);
     void visitCallSetElement(LCallSetElement* lir);
     void visitCallInitElementArray(LCallInitElementArray* lir);
     void visitThrow(LThrow* lir);
     void visitTypeOfV(LTypeOfV* lir);
     void visitOutOfLineTypeOfV(OutOfLineTypeOfV* ool);
     void visitToAsync(LToAsync* lir);
+    void visitToAsyncGen(LToAsyncGen* lir);
+    void visitToAsyncIter(LToAsyncIter* lir);
     void visitToIdV(LToIdV* lir);
     template<typename T> void emitLoadElementT(LLoadElementT* lir, const T& source);
     void visitLoadElementT(LLoadElementT* lir);
     void visitLoadElementV(LLoadElementV* load);
     void visitLoadElementHole(LLoadElementHole* lir);
     void visitLoadUnboxedPointerV(LLoadUnboxedPointerV* lir);
     void visitLoadUnboxedPointerT(LLoadUnboxedPointerT* lir);
     void visitUnboxObjectOrNull(LUnboxObjectOrNull* lir);
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -2063,23 +2063,23 @@ CompileBackEnd(MIRGenerator* mir)
 
     LIRGraph* lir = GenerateLIR(mir);
     if (!lir)
         return nullptr;
 
     return GenerateCode(mir, lir);
 }
 
-// Find a finished builder for the compartment.
+// Find a finished builder for the runtime.
 static IonBuilder*
 GetFinishedBuilder(JSContext* cx, GlobalHelperThreadState::IonBuilderVector& finished)
 {
     for (size_t i = 0; i < finished.length(); i++) {
         IonBuilder* testBuilder = finished[i];
-        if (testBuilder->compartment == CompileCompartment::get(cx->compartment())) {
+        if (testBuilder->script()->runtimeFromAnyThread() == cx->runtime()) {
             HelperThreadState().remove(finished, &i);
             return testBuilder;
         }
     }
 
     return nullptr;
 }
 
@@ -2090,20 +2090,20 @@ AttachFinishedCompilations(JSContext* cx
     if (!ion)
         return;
 
     {
         AutoLockHelperThreadState lock;
 
         GlobalHelperThreadState::IonBuilderVector& finished = HelperThreadState().ionFinishedList(lock);
 
-        // Incorporate any off thread compilations for the compartment which have
+        // Incorporate any off thread compilations for the runtime which have
         // finished, failed or have been cancelled.
         while (true) {
-            // Find a finished builder for the compartment.
+            // Find a finished builder for the runtime.
             IonBuilder* builder = GetFinishedBuilder(cx, finished);
             if (!builder)
                 break;
 
             JSScript* script = builder->script();
             MOZ_ASSERT(script->hasBaselineScript());
             script->baselineScript()->setPendingIonBuilder(cx->runtime(), script, builder);
             cx->runtime()->ionLazyLinkListAdd(builder);
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -2207,16 +2207,22 @@ IonBuilder::inspectOpcode(JSOp op)
 
       case JSOP_TYPEOF:
       case JSOP_TYPEOFEXPR:
         return jsop_typeof();
 
       case JSOP_TOASYNC:
         return jsop_toasync();
 
+      case JSOP_TOASYNCGEN:
+        return jsop_toasyncgen();
+
+      case JSOP_TOASYNCITER:
+        return jsop_toasynciter();
+
       case JSOP_TOID:
         return jsop_toid();
 
       case JSOP_LAMBDA:
         return jsop_lambda(info().getFunction(pc));
 
       case JSOP_LAMBDA_ARROW:
         return jsop_lambda_arrow(info().getFunction(pc));
@@ -12253,16 +12259,44 @@ IonBuilder::jsop_toasync()
 
     current->add(ins);
     current->push(ins);
 
     return resumeAfter(ins);
 }
 
 AbortReasonOr<Ok>
+IonBuilder::jsop_toasyncgen()
+{
+    MDefinition* unwrapped = current->pop();
+    MOZ_ASSERT(unwrapped->type() == MIRType::Object);
+
+    MToAsyncGen* ins = MToAsyncGen::New(alloc(), unwrapped);
+
+    current->add(ins);
+    current->push(ins);
+
+    return resumeAfter(ins);
+}
+
+AbortReasonOr<Ok>
+IonBuilder::jsop_toasynciter()
+{
+    MDefinition* unwrapped = current->pop();
+    MOZ_ASSERT(unwrapped->type() == MIRType::Object);
+
+    MToAsyncIter* ins = MToAsyncIter::New(alloc(), unwrapped);
+
+    current->add(ins);
+    current->push(ins);
+
+    return resumeAfter(ins);
+}
+
+AbortReasonOr<Ok>
 IonBuilder::jsop_toid()
 {
     // No-op if the index is an integer.
     if (current->peek(-1)->type() == MIRType::Int32)
         return Ok();
 
     MDefinition* index = current->pop();
     MToId* ins = MToId::New(alloc(), index);
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -564,16 +564,18 @@ class IonBuilder
     AbortReasonOr<Ok> jsop_lambda_arrow(JSFunction* fun);
     AbortReasonOr<Ok> jsop_setfunname(uint8_t prefixKind);
     AbortReasonOr<Ok> jsop_pushlexicalenv(uint32_t index);
     AbortReasonOr<Ok> jsop_copylexicalenv(bool copySlots);
     AbortReasonOr<Ok> jsop_functionthis();
     AbortReasonOr<Ok> jsop_globalthis();
     AbortReasonOr<Ok> jsop_typeof();
     AbortReasonOr<Ok> jsop_toasync();
+    AbortReasonOr<Ok> jsop_toasyncgen();
+    AbortReasonOr<Ok> jsop_toasynciter();
     AbortReasonOr<Ok> jsop_toid();
     AbortReasonOr<Ok> jsop_iter(uint8_t flags);
     AbortReasonOr<Ok> jsop_itermore();
     AbortReasonOr<Ok> jsop_isnoiter();
     AbortReasonOr<Ok> jsop_iterend();
     AbortReasonOr<Ok> jsop_in();
     AbortReasonOr<Ok> jsop_instanceof();
     AbortReasonOr<Ok> jsop_getaliasedvar(EnvironmentCoordinate ec);
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -1206,16 +1206,32 @@ void
 LIRGenerator::visitToAsync(MToAsync* ins)
 {
     LToAsync* lir = new(alloc()) LToAsync(useRegisterAtStart(ins->input()));
     defineReturn(lir, ins);
     assignSafepoint(lir, ins);
 }
 
 void
+LIRGenerator::visitToAsyncGen(MToAsyncGen* ins)
+{
+    LToAsyncGen* lir = new(alloc()) LToAsyncGen(useRegisterAtStart(ins->input()));
+    defineReturn(lir, ins);
+    assignSafepoint(lir, ins);
+}
+
+void
+LIRGenerator::visitToAsyncIter(MToAsyncIter* ins)
+{
+    LToAsyncIter* lir = new(alloc()) LToAsyncIter(useRegisterAtStart(ins->input()));
+    defineReturn(lir, ins);
+    assignSafepoint(lir, ins);
+}
+
+void
 LIRGenerator::visitToId(MToId* ins)
 {
     LToIdV* lir = new(alloc()) LToIdV(useBox(ins->input()), tempDouble());
     defineBox(lir, ins);
     assignSafepoint(lir, ins);
 }
 
 void
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -116,16 +116,18 @@ class LIRGenerator : public LIRGenerator
     void visitCallDirectEval(MCallDirectEval* ins);
     void visitTest(MTest* test);
     void visitGotoWithFake(MGotoWithFake* ins);
     void visitFunctionDispatch(MFunctionDispatch* ins);
     void visitObjectGroupDispatch(MObjectGroupDispatch* ins);
     void visitCompare(MCompare* comp);
     void visitTypeOf(MTypeOf* ins);
     void visitToAsync(MToAsync* ins);
+    void visitToAsyncGen(MToAsyncGen* ins);
+    void visitToAsyncIter(MToAsyncIter* ins);
     void visitToId(MToId* ins);
     void visitBitNot(MBitNot* ins);
     void visitBitAnd(MBitAnd* ins);
     void visitBitOr(MBitOr* ins);
     void visitBitXor(MBitXor* ins);
     void visitLsh(MLsh* ins);
     void visitRsh(MRsh* ins);
     void visitUrsh(MUrsh* ins);
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -5820,16 +5820,46 @@ class MToAsync
         setResultType(MIRType::Object);
     }
 
   public:
     INSTRUCTION_HEADER(ToAsync)
     TRIVIAL_NEW_WRAPPERS
 };
 
+class MToAsyncGen
+  : public MUnaryInstruction,
+    public SingleObjectPolicy::Data
+{
+    explicit MToAsyncGen(MDefinition* unwrapped)
+      : MUnaryInstruction(unwrapped)
+    {
+        setResultType(MIRType::Object);
+    }
+
+  public:
+    INSTRUCTION_HEADER(ToAsyncGen)
+    TRIVIAL_NEW_WRAPPERS
+};
+
+class MToAsyncIter
+  : public MUnaryInstruction,
+    public SingleObjectPolicy::Data
+{
+    explicit MToAsyncIter(MDefinition* unwrapped)
+      : MUnaryInstruction(unwrapped)
+    {
+        setResultType(MIRType::Object);
+    }
+
+  public:
+    INSTRUCTION_HEADER(ToAsyncIter)
+    TRIVIAL_NEW_WRAPPERS
+};
+
 class MToId
   : public MUnaryInstruction,
     public BoxInputsPolicy::Data
 {
     explicit MToId(MDefinition* index)
       : MUnaryInstruction(index)
     {
         setResultType(MIRType::Value);
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -74,16 +74,18 @@ namespace jit {
     _(EncodeSnapshot)                                                       \
     _(AssertFloat32)                                                        \
     _(AssertRecoveredOnBailout)                                             \
     _(GetDynamicName)                                                       \
     _(CallDirectEval)                                                       \
     _(BitNot)                                                               \
     _(TypeOf)                                                               \
     _(ToAsync)                                                              \
+    _(ToAsyncGen)                                                           \
+    _(ToAsyncIter)                                                          \
     _(ToId)                                                                 \
     _(BitAnd)                                                               \
     _(BitOr)                                                                \
     _(BitXor)                                                               \
     _(Lsh)                                                                  \
     _(Rsh)                                                                  \
     _(Ursh)                                                                 \
     _(SignExtend)                                                           \
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -359,18 +359,18 @@ ArrayPushDense(JSContext* cx, HandleObje
     JS::AutoValueArray<3> argv(cx);
     AutoDetectInvalidation adi(cx, argv[0]);
     argv[0].setUndefined();
     argv[1].setObject(*obj);
     argv[2].set(v);
     if (!js::array_push(cx, 1, argv.begin()))
         return false;
 
-    if (MOZ_LIKELY(argv[0].isInt32())) {
-        *length = argv[0].isInt32();
+    if (argv[0].isInt32()) {
+        *length = argv[0].toInt32();
         return true;
     }
 
     // array_push changed the length to be larger than INT32_MAX. In this case
     // OBJECT_FLAG_LENGTH_OVERFLOW was set, TI invalidated the script, and the
     // AutoDetectInvalidation instance on the stack will replace *length with
     // the actual return value during bailout.
     MOZ_ASSERT(adi.shouldSetReturnOverride());
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -1613,16 +1613,42 @@ class LToAsync : public LCallInstruction
         setOperand(0, input);
     }
 
     const LAllocation* unwrapped() {
         return getOperand(0);
     }
 };
 
+class LToAsyncGen : public LCallInstructionHelper<1, 1, 0>
+{
+  public:
+    LIR_HEADER(ToAsyncGen)
+    explicit LToAsyncGen(const LAllocation& input) {
+        setOperand(0, input);
+    }
+
+    const LAllocation* unwrapped() {
+        return getOperand(0);
+    }
+};
+
+class LToAsyncIter : public LCallInstructionHelper<1, 1, 0>
+{
+  public:
+    LIR_HEADER(ToAsyncIter)
+    explicit LToAsyncIter(const LAllocation& input) {
+        setOperand(0, input);
+    }
+
+    const LAllocation* unwrapped() {
+        return getOperand(0);
+    }
+};
+
 class LToIdV : public LInstructionHelper<BOX_PIECES, BOX_PIECES, 1>
 {
   public:
     LIR_HEADER(ToIdV)
 
     LToIdV(const LBoxAllocation& input, const LDefinition& temp)
     {
         setBoxOperand(Input, input);
--- a/js/src/jit/shared/LOpcodes-shared.h
+++ b/js/src/jit/shared/LOpcodes-shared.h
@@ -358,16 +358,18 @@
     _(GetFrameArgument)             \
     _(SetFrameArgumentT)            \
     _(SetFrameArgumentC)            \
     _(SetFrameArgumentV)            \
     _(RunOncePrologue)              \
     _(Rest)                         \
     _(TypeOfV)                      \
     _(ToAsync)                      \
+    _(ToAsyncGen)                   \
+    _(ToAsyncIter)                  \
     _(ToIdV)                        \
     _(Floor)                        \
     _(FloorF)                       \
     _(Ceil)                         \
     _(CeilF)                        \
     _(Round)                        \
     _(RoundF)                       \
     _(NearbyInt)                    \
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -586,8 +586,14 @@ MSG_DEF(JSMSG_CANNOT_RESOLVE_PROMISE_WIT
 MSG_DEF(JSMSG_PROMISE_CAPABILITY_HAS_SOMETHING_ALREADY, 0, JSEXN_TYPEERR, "GetCapabilitiesExecutor function already invoked with non-undefined values.")
 MSG_DEF(JSMSG_PROMISE_RESOLVE_FUNCTION_NOT_CALLABLE,    0, JSEXN_TYPEERR, "A Promise subclass passed a non-callable value as the resolve function.")
 MSG_DEF(JSMSG_PROMISE_REJECT_FUNCTION_NOT_CALLABLE,     0, JSEXN_TYPEERR, "A Promise subclass passed a non-callable value as the reject function.")
 MSG_DEF(JSMSG_PROMISE_ERROR_IN_WRAPPED_REJECTION_REASON,0, JSEXN_INTERNALERR, "Promise rejection value is a non-unwrappable cross-compartment wrapper.")
 
 // Iterator
 MSG_DEF(JSMSG_RETURN_NOT_CALLABLE,     0, JSEXN_TYPEERR, "property 'return' of iterator is not callable")
 MSG_DEF(JSMSG_ITERATOR_NO_THROW,       0, JSEXN_TYPEERR, "iterator does not have a 'throw' method")
+
+// Async Iteration
+MSG_DEF(JSMSG_FOR_AWAIT_NOT_OF,        0, JSEXN_TYPEERR, "'for await' loop should be used with 'of'")
+MSG_DEF(JSMSG_NOT_AN_ASYNC_GENERATOR,  0, JSEXN_TYPEERR, "Not an async generator")
+MSG_DEF(JSMSG_NOT_AN_ASYNC_ITERATOR,   0, JSEXN_TYPEERR, "Not an async from sync iterator")
+MSG_DEF(JSMSG_GET_ASYNC_ITER_RETURNED_PRIMITIVE, 0, JSEXN_TYPEERR, "[Symbol.asyncIterator]() returned a non-object value")
--- a/js/src/jsapi-tests/testGCGrayMarking.cpp
+++ b/js/src/jsapi-tests/testGCGrayMarking.cpp
@@ -152,21 +152,29 @@ TestMarking()
 }
 
 bool
 TestWeakMaps()
 {
     JSObject* weakMap = JS::NewWeakMapObject(cx);
     CHECK(weakMap);
 
-    JSObject* key = AllocWeakmapKeyObject();
-    CHECK(key);
+    JSObject* key;
+    JSObject* value;
+    {
+        JS::RootedObject rootedMap(cx, weakMap);
 
-    JSObject* value = AllocPlainObject();
-    CHECK(value);
+        key = AllocWeakmapKeyObject();
+        CHECK(key);
+
+        value = AllocPlainObject();
+        CHECK(value);
+
+        weakMap = rootedMap;
+    }
 
     {
         JS::RootedObject rootedMap(cx, weakMap);
         JS::RootedObject rootedKey(cx, key);
         JS::RootedValue rootedValue(cx, ObjectValue(*value));
         CHECK(SetWeakMapEntry(cx, rootedMap, rootedKey, rootedValue));
     }
 
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -66,16 +66,17 @@
 #include "js/Conversions.h"
 #include "js/Date.h"
 #include "js/Initialization.h"
 #include "js/Proxy.h"
 #include "js/SliceBudget.h"
 #include "js/StructuredClone.h"
 #include "js/Utility.h"
 #include "vm/AsyncFunction.h"
+#include "vm/AsyncIteration.h"
 #include "vm/DateObject.h"
 #include "vm/Debugger.h"
 #include "vm/EnvironmentObject.h"
 #include "vm/ErrorObject.h"
 #include "vm/HelperThreads.h"
 #include "vm/Interpreter.h"
 #include "vm/RegExpStatics.h"
 #include "vm/Runtime.h"
@@ -3610,16 +3611,21 @@ CloneFunctionObject(JSContext* cx, Handl
         return nullptr;
     }
 
     if (IsWrappedAsyncFunction(fun)) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_CLONE_OBJECT);
         return nullptr;
     }
 
+    if (IsWrappedAsyncGenerator(fun)) {
+        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_CLONE_OBJECT);
+        return nullptr;
+    }
+
     if (CanReuseScriptForClone(cx->compartment(), fun, env)) {
         // If the script is to be reused, either the script can already handle
         // non-syntactic scopes, or there is only the standard global lexical
         // scope.
 #ifdef DEBUG
         // Fail here if we OOM during debug asserting.
         // CloneFunctionReuseScript will delazify the script anyways, so we
         // are not creating an extra failure condition for DEBUG builds.
@@ -3862,16 +3868,17 @@ JS::TransitiveCompileOptions::copyPODTra
     asmJSOption = rhs.asmJSOption;
     throwOnAsmJSValidationFailureOption = rhs.throwOnAsmJSValidationFailureOption;
     forceAsync = rhs.forceAsync;
     sourceIsLazy = rhs.sourceIsLazy;
     introductionType = rhs.introductionType;
     introductionLineno = rhs.introductionLineno;
     introductionOffset = rhs.introductionOffset;
     hasIntroductionInfo = rhs.hasIntroductionInfo;
+    isProbablySystemOrAddonCode = rhs.isProbablySystemOrAddonCode;
 };
 
 void
 JS::ReadOnlyCompileOptions::copyPODOptions(const ReadOnlyCompileOptions& rhs)
 {
     copyPODTransitiveOptions(rhs);
     lineno = rhs.lineno;
     column = rhs.column;
@@ -3974,16 +3981,17 @@ JS::CompileOptions::CompileOptions(JSCon
     : ReadOnlyCompileOptions(), elementRoot(cx), elementAttributeNameRoot(cx),
       introductionScriptRoot(cx)
 {
     this->version = (version != JSVERSION_UNKNOWN) ? version : cx->findVersion();
 
     strictOption = cx->options().strictMode();
     extraWarningsOption = cx->compartment()->behaviors().extraWarnings(cx);
     forEachStatementOption = cx->options().forEachStatement();
+    isProbablySystemOrAddonCode = cx->compartment()->isProbablySystemOrAddonCode();
     werrorOption = cx->options().werror();
     if (!cx->options().asmJS())
         asmJSOption = AsmJSOption::Disabled;
     else if (cx->compartment()->debuggerObservesAsmJS())
         asmJSOption = AsmJSOption::DisabledByDebugger;
     else
         asmJSOption = AsmJSOption::Enabled;
     throwOnAsmJSValidationFailureOption = cx->options().throwOnAsmJSValidationFailure();
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -3874,16 +3874,17 @@ class JS_FRIEND_API(TransitiveCompileOpt
         extraWarningsOption(false),
         forEachStatementOption(false),
         werrorOption(false),
         asmJSOption(AsmJSOption::Disabled),
         throwOnAsmJSValidationFailureOption(false),
         forceAsync(false),
         sourceIsLazy(false),
         allowHTMLComments(true),
+        isProbablySystemOrAddonCode(false),
         introductionType(nullptr),
         introductionLineno(0),
         introductionOffset(0),
         hasIntroductionInfo(false)
     { }
 
     // Set all POD options (those not requiring reference counts, copies,
     // rooting, or other hand-holding) to their values in |rhs|.
@@ -3910,16 +3911,17 @@ class JS_FRIEND_API(TransitiveCompileOpt
     bool extraWarningsOption;
     bool forEachStatementOption;
     bool werrorOption;
     AsmJSOption asmJSOption;
     bool throwOnAsmJSValidationFailureOption;
     bool forceAsync;
     bool sourceIsLazy;
     bool allowHTMLComments;
+    bool isProbablySystemOrAddonCode;
 
     // |introductionType| is a statically allocated C string:
     // one of "eval", "Function", or "GeneratorFunction".
     const char* introductionType;
     unsigned introductionLineno;
     uint32_t introductionOffset;
     bool hasIntroductionInfo;
 
@@ -5157,17 +5159,18 @@ GetSymbolDescription(HandleSymbol symbol
     macro(match) \
     macro(replace) \
     macro(search) \
     macro(species) \
     macro(hasInstance) \
     macro(split) \
     macro(toPrimitive) \
     macro(toStringTag) \
-    macro(unscopables)
+    macro(unscopables) \
+    macro(asyncIterator)
 
 enum class SymbolCode : uint32_t {
     // There is one SymbolCode for each well-known symbol.
 #define JS_DEFINE_SYMBOL_ENUM(name) name,
     JS_FOR_EACH_WELL_KNOWN_SYMBOL(JS_DEFINE_SYMBOL_ENUM)  // SymbolCode::iterator, etc.
 #undef JS_DEFINE_SYMBOL_ENUM
     Limit,
     InSymbolRegistry = 0xfffffffe,  // created by Symbol.for() or JS::GetSymbolFor()
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -644,29 +644,27 @@ JSCompartment::getTemplateLiteralObject(
 {
     if (TemplateRegistry::AddPtr p = templateLiteralMap_.lookupForAdd(rawStrings)) {
         templateObj.set(p->value());
 
         // The template object must have been frozen when it was added to the
         // registry.
         MOZ_ASSERT(!templateObj->nonProxyIsExtensible());
     } else {
-        // Add the template object to the registry before freezing to avoid
-        // needing to call relookupOrAdd.
-        if (!templateLiteralMap_.add(p, rawStrings, templateObj))
-            return false;
-
         MOZ_ASSERT(templateObj->nonProxyIsExtensible());
         RootedValue rawValue(cx, ObjectValue(*rawStrings));
         if (!DefineProperty(cx, templateObj, cx->names().raw, rawValue, nullptr, nullptr, 0))
             return false;
         if (!FreezeObject(cx, rawStrings))
             return false;
         if (!FreezeObject(cx, templateObj))
             return false;
+
+        if (!templateLiteralMap_.relookupOrAdd(p, rawStrings, templateObj))
+            return false;
     }
 
     return true;
 }
 
 JSObject*
 JSCompartment::getExistingTemplateLiteralObject(JSObject* rawStrings)
 {
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -970,16 +970,17 @@ IsObjectInContextCompartment(JSObject* o
  */
 #define JSITER_ENUMERATE  0x1   /* for-in compatible hidden default iterator */
 #define JSITER_FOREACH    0x2   /* get obj[key] for each property */
 #define JSITER_KEYVALUE   0x4   /* obsolete destructuring for-in wants [key, value] */
 #define JSITER_OWNONLY    0x8   /* iterate over obj's own properties only */
 #define JSITER_HIDDEN     0x10  /* also enumerate non-enumerable properties */
 #define JSITER_SYMBOLS    0x20  /* also include symbol property keys */
 #define JSITER_SYMBOLSONLY 0x40 /* exclude string property keys */
+#define JSITER_FORAWAITOF 0x80  /* for-await-of */
 
 JS_FRIEND_API(bool)
 RunningWithTrustedPrincipals(JSContext* cx);
 
 MOZ_ALWAYS_INLINE uintptr_t
 GetNativeStackLimit(JSContext* cx, JS::StackKind kind, int extraAllowance = 0)
 {
     uintptr_t limit = JS::RootingContext::get(cx)->nativeStackLimit[kind];
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -36,16 +36,17 @@
 #include "gc/Marking.h"
 #include "gc/Policy.h"
 #include "jit/InlinableNatives.h"
 #include "jit/Ion.h"
 #include "jit/JitFrameIterator.h"
 #include "js/CallNonGenericMethod.h"
 #include "js/Proxy.h"
 #include "vm/AsyncFunction.h"
+#include "vm/AsyncIteration.h"
 #include "vm/Debugger.h"
 #include "vm/GlobalObject.h"
 #include "vm/Interpreter.h"
 #include "vm/SelfHosting.h"
 #include "vm/Shape.h"
 #include "vm/SharedImmutableStringsCache.h"
 #include "vm/StringBuffer.h"
 #include "vm/WrapperObject.h"
@@ -301,16 +302,18 @@ CallerGetterImpl(JSContext* cx, const Ca
         if (!callerObj) {
             args.rval().setNull();
             return true;
         }
 
         JSFunction* callerFun = &callerObj->as<JSFunction>();
         if (IsWrappedAsyncFunction(callerFun))
             callerFun = GetUnwrappedAsyncFunction(callerFun);
+        else if (IsWrappedAsyncGenerator(callerFun))
+            callerFun = GetUnwrappedAsyncGenerator(callerFun);
         MOZ_ASSERT(!callerFun->isBuiltin(), "non-builtin iterator returned a builtin?");
 
         if (callerFun->strict()) {
             JS_ReportErrorFlagsAndNumberASCII(cx, JSREPORT_ERROR, GetErrorMessage, nullptr,
                                               JSMSG_CALLER_IS_STRICT);
             return false;
         }
     }
@@ -397,49 +400,53 @@ static const JSPropertySpec function_pro
     JS_PSGS("arguments", ArgumentsGetter, ArgumentsSetter, 0),
     JS_PSGS("caller", CallerGetter, CallerSetter, 0),
     JS_PS_END
 };
 
 static bool
 ResolveInterpretedFunctionPrototype(JSContext* cx, HandleFunction fun, HandleId id)
 {
-    MOZ_ASSERT(fun->isInterpreted() || fun->isAsmJSNative());
+    bool isAsyncGenerator = IsWrappedAsyncGenerator(fun);
+
+    MOZ_ASSERT_IF(!isAsyncGenerator, fun->isInterpreted() || fun->isAsmJSNative());
     MOZ_ASSERT(id == NameToId(cx->names().prototype));
 
     // Assert that fun is not a compiler-created function object, which
     // must never leak to script or embedding code and then be mutated.
     // Also assert that fun is not bound, per the ES5 15.3.4.5 ref above.
-    MOZ_ASSERT(!IsInternalFunctionObject(*fun));
+    MOZ_ASSERT_IF(!isAsyncGenerator, !IsInternalFunctionObject(*fun));
     MOZ_ASSERT(!fun->isBoundFunction());
 
     // Make the prototype object an instance of Object with the same parent as
     // the function object itself, unless the function is an ES6 generator.  In
     // that case, per the 15 July 2013 ES6 draft, section 15.19.3, its parent is
     // the GeneratorObjectPrototype singleton.
     bool isStarGenerator = fun->isStarGenerator();
     Rooted<GlobalObject*> global(cx, &fun->global());
     RootedObject objProto(cx);
-    if (isStarGenerator)
+    if (isAsyncGenerator)
+        objProto = GlobalObject::getOrCreateAsyncGeneratorPrototype(cx, global);
+    else if (isStarGenerator)
         objProto = GlobalObject::getOrCreateStarGeneratorObjectPrototype(cx, global);
     else
         objProto = GlobalObject::getOrCreateObjectPrototype(cx, global);
     if (!objProto)
         return false;
 
     RootedPlainObject proto(cx, NewObjectWithGivenProto<PlainObject>(cx, objProto,
                                                                      SingletonObject));
     if (!proto)
         return false;
 
     // Per ES5 13.2 the prototype's .constructor property is configurable,
     // non-enumerable, and writable.  However, per the 15 July 2013 ES6 draft,
     // section 15.19.3, the .prototype of a generator function does not link
     // back with a .constructor.
-    if (!isStarGenerator) {
+    if (!isStarGenerator && !isAsyncGenerator) {
         RootedValue objVal(cx, ObjectValue(*fun));
         if (!DefineProperty(cx, proto, cx->names().constructor, objVal, nullptr, nullptr, 0))
             return false;
     }
 
     // Per ES5 15.3.5.2 a user-defined function's .prototype property is
     // initially non-configurable, non-enumerable, and writable.
     RootedValue protoVal(cx, ObjectValue(*proto));
@@ -479,21 +486,23 @@ fun_resolve(JSContext* cx, HandleObject 
          * Generators are not constructors, but they have a .prototype property anyway,
          * according to errata to ES6. See bug 1191486.
          *
          * Thus all of the following don't get a .prototype property:
          * - Methods (that are not class-constructors or generators)
          * - Arrow functions
          * - Function.prototype
          */
-        if (fun->isBuiltin())
-            return true;
-        if (!fun->isConstructor()) {
-            if (!fun->isStarGenerator() && !fun->isLegacyGenerator() && !fun->isAsync())
+        if (!IsWrappedAsyncGenerator(fun)) {
+            if (fun->isBuiltin())
                 return true;
+            if (!fun->isConstructor()) {
+                if (!fun->isStarGenerator() && !fun->isLegacyGenerator() && !fun->isAsync())
+                    return true;
+            }
         }
 
         if (!ResolveInterpretedFunctionPrototype(cx, fun, id))
             return false;
 
         *resolvedp = true;
         return true;
     }
@@ -980,16 +989,20 @@ js::FunctionToString(JSContext* cx, Hand
         return AsmJSModuleToString(cx, fun, !prettyPrint);
     if (IsAsmJSFunction(fun))
         return AsmJSFunctionToString(cx, fun);
 
     if (IsWrappedAsyncFunction(fun)) {
         RootedFunction unwrapped(cx, GetUnwrappedAsyncFunction(fun));
         return FunctionToString(cx, unwrapped, prettyPrint);
     }
+    if (IsWrappedAsyncGenerator(fun)) {
+        RootedFunction unwrapped(cx, GetUnwrappedAsyncGenerator(fun));
+        return FunctionToString(cx, unwrapped, prettyPrint);
+    }
 
     StringBuffer out(cx);
     RootedScript script(cx);
 
     if (fun->hasScript()) {
         script = fun->nonLazyScript();
         if (script->isGeneratorExp()) {
             if (!out.append("function genexp() {") ||
@@ -1632,20 +1645,24 @@ FunctionConstructor(JSContext* cx, const
     const char* filename;
     unsigned lineno;
     bool mutedErrors;
     uint32_t pcOffset;
     DescribeScriptedCallerForCompilation(cx, &maybeScript, &filename, &lineno, &pcOffset,
                                          &mutedErrors);
 
     const char* introductionType = "Function";
-    if (isAsync)
-        introductionType = "AsyncFunction";
-    else if (generatorKind != NotGenerator)
+    if (isAsync) {
+        if (isStarGenerator)
+            introductionType = "AsyncGenerator";
+        else
+            introductionType = "AsyncFunction";
+    } else if (generatorKind != NotGenerator) {
         introductionType = "GeneratorFunction";
+    }
 
     const char* introducerFilename = filename;
     if (maybeScript && maybeScript->scriptSource()->introducerFilename())
         introducerFilename = maybeScript->scriptSource()->introducerFilename();
 
     CompileOptions options(cx);
     options.setMutedErrors(mutedErrors)
            .setFileAndLine(filename, 1)
@@ -1762,22 +1779,30 @@ FunctionConstructor(JSContext* cx, const
         return false;
 
     mozilla::Range<const char16_t> chars = stableChars.twoByteRange();
     SourceBufferHolder::Ownership ownership = stableChars.maybeGiveOwnershipToCaller()
                                               ? SourceBufferHolder::GiveOwnership
                                               : SourceBufferHolder::NoOwnership;
     bool ok;
     SourceBufferHolder srcBuf(chars.begin().get(), chars.length(), ownership);
-    if (isAsync)
-        ok = frontend::CompileStandaloneAsyncFunction(cx, &fun, options, srcBuf, parameterListEnd);
-    else if (isStarGenerator)
-        ok = frontend::CompileStandaloneGenerator(cx, &fun, options, srcBuf, parameterListEnd);
-    else
-        ok = frontend::CompileStandaloneFunction(cx, &fun, options, srcBuf, parameterListEnd);
+    if (isAsync) {
+        if (isStarGenerator) {
+            ok = frontend::CompileStandaloneAsyncGenerator(cx, &fun, options, srcBuf,
+                                                           parameterListEnd);
+        } else {
+            ok = frontend::CompileStandaloneAsyncFunction(cx, &fun, options, srcBuf,
+                                                          parameterListEnd);
+        }
+    } else {
+        if (isStarGenerator)
+            ok = frontend::CompileStandaloneGenerator(cx, &fun, options, srcBuf, parameterListEnd);
+        else
+            ok = frontend::CompileStandaloneFunction(cx, &fun, options, srcBuf, parameterListEnd);
+    }
 
     // Step 33.
     args.rval().setObject(*fun);
     return ok;
 }
 
 bool
 js::Function(JSContext* cx, unsigned argc, Value* vp)
@@ -1793,17 +1818,17 @@ js::Generator(JSContext* cx, unsigned ar
     return FunctionConstructor(cx, args, StarGenerator, SyncFunction);
 }
 
 bool
 js::AsyncFunctionConstructor(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
-    // Save the callee before its reset in FunctionConstructor().
+    // Save the callee before it's reset in FunctionConstructor().
     RootedObject newTarget(cx);
     if (args.isConstructing())
         newTarget = &args.newTarget().toObject();
     else
         newTarget = &args.callee();
 
     if (!FunctionConstructor(cx, args, NotGenerator, AsyncFunction))
         return false;
@@ -1826,16 +1851,50 @@ js::AsyncFunctionConstructor(JSContext* 
     if (!wrapped)
         return false;
 
     args.rval().setObject(*wrapped);
     return true;
 }
 
 bool
+js::AsyncGeneratorConstructor(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    // Save the callee before its reset in FunctionConstructor().
+    RootedObject newTarget(cx);
+    if (args.isConstructing())
+        newTarget = &args.newTarget().toObject();
+    else
+        newTarget = &args.callee();
+
+    if (!FunctionConstructor(cx, args, StarGenerator, AsyncFunction))
+        return false;
+
+    RootedObject proto(cx);
+    if (!GetPrototypeFromConstructor(cx, newTarget, &proto))
+        return false;
+
+    if (!proto) {
+        proto = GlobalObject::getOrCreateAsyncGenerator(cx, cx->global());
+        if (!proto)
+            return false;
+    }
+
+    RootedFunction unwrapped(cx, &args.rval().toObject().as<JSFunction>());
+    RootedObject wrapped(cx, WrapAsyncGeneratorWithProto(cx, unwrapped, proto));
+    if (!wrapped)
+        return false;
+
+    args.rval().setObject(*wrapped);
+    return true;
+}
+
+bool
 JSFunction::isBuiltinFunctionConstructor()
 {
     return maybeNative() == Function || maybeNative() == Generator;
 }
 
 bool
 JSFunction::needsExtraBodyVarEnvironment() const
 {
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -642,16 +642,19 @@ extern bool
 Function(JSContext* cx, unsigned argc, Value* vp);
 
 extern bool
 Generator(JSContext* cx, unsigned argc, Value* vp);
 
 extern bool
 AsyncFunctionConstructor(JSContext* cx, unsigned argc, Value* vp);
 
+extern bool
+AsyncGeneratorConstructor(JSContext* cx, unsigned argc, Value* vp);
+
 // Allocate a new function backed by a JSNative.  Note that by default this
 // creates a singleton object.
 extern JSFunction*
 NewNativeFunction(JSContext* cx, JSNative native, unsigned nargs, HandleAtom atom,
                   gc::AllocKind allocKind = gc::AllocKind::FUNCTION,
                   NewObjectKind newKind = SingletonObject);
 
 // Allocate a new constructor backed by a JSNative.  Note that by default this
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -933,38 +933,40 @@ JSObject*
 js::GetIteratorObject(JSContext* cx, HandleObject obj, uint32_t flags)
 {
     RootedObject iterator(cx);
     if (!GetIterator(cx, obj, flags, &iterator))
         return nullptr;
     return iterator;
 }
 
+// ES 2017 draft 7.4.7.
 JSObject*
-js::CreateItrResultObject(JSContext* cx, HandleValue value, bool done)
+js::CreateIterResultObject(JSContext* cx, HandleValue value, bool done)
 {
-    // FIXME: We can cache the iterator result object shape somewhere.
-    AssertHeapIsIdle();
+    // Step 1 (implicit).
 
-    RootedObject proto(cx, GlobalObject::getOrCreateObjectPrototype(cx, cx->global()));
-    if (!proto)
+    // Step 2.
+    RootedObject resultObj(cx, NewBuiltinClassInstance<PlainObject>(cx));
+    if (!resultObj)
         return nullptr;
 
-    RootedPlainObject obj(cx, NewObjectWithGivenProto<PlainObject>(cx, proto));
-    if (!obj)
+    // Step 3.
+    if (!DefineProperty(cx, resultObj, cx->names().value, value))
         return nullptr;
 
-    if (!DefineProperty(cx, obj, cx->names().value, value))
+    // Step 4.
+    if (!DefineProperty(cx, resultObj, cx->names().done,
+                        done ? TrueHandleValue : FalseHandleValue))
+    {
         return nullptr;
+    }
 
-    RootedValue doneBool(cx, BooleanValue(done));
-    if (!DefineProperty(cx, obj, cx->names().done, doneBool))
-        return nullptr;
-
-    return obj;
+    // Step 5.
+    return resultObj;
 }
 
 bool
 js::ThrowStopIteration(JSContext* cx)
 {
     MOZ_ASSERT(!JS_IsExceptionPending(cx));
 
     // StopIteration isn't a constructor, but it's stored in GlobalObject
--- a/js/src/jsiter.h
+++ b/js/src/jsiter.h
@@ -205,22 +205,24 @@ SuppressDeletedElement(JSContext* cx, Ha
 extern bool
 IteratorMore(JSContext* cx, HandleObject iterobj, MutableHandleValue rval);
 
 extern bool
 ThrowStopIteration(JSContext* cx);
 
 /*
  * Create an object of the form { value: VALUE, done: DONE }.
- * ES6 draft from 2013-09-05, section 25.4.3.4.
+ * ES 2017 draft 7.4.7.
  */
 extern JSObject*
-CreateItrResultObject(JSContext* cx, HandleValue value, bool done);
+CreateIterResultObject(JSContext* cx, HandleValue value, bool done);
 
 extern JSObject*
 InitLegacyIteratorClass(JSContext* cx, HandleObject obj);
 
 extern JSObject*
 InitStopIterationClass(JSContext* cx, HandleObject obj);
 
+enum class IteratorKind { Sync, Async };
+
 } /* namespace js */
 
 #endif /* jsiter_h */
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -2022,18 +2022,22 @@ ExpressionDecompiler::decompilePC(jsbyte
             return write("JS_IS_CONSTRUCTING");
 
           case JSOP_ITER:
             return write("ITER");
 
           case JSOP_LAMBDA:
           case JSOP_LAMBDA_ARROW:
           case JSOP_TOASYNC:
+          case JSOP_TOASYNCGEN:
             return write("FUN");
 
+          case JSOP_TOASYNCITER:
+            return write("ASYNCITER");
+
           case JSOP_MOREITER:
             // For stack dump, defIndex == 0 is not used.
             MOZ_ASSERT(defIndex == 1);
             return write("MOREITER");
 
           case JSOP_MUTATEPROTO:
             return write("SUCCEEDED");
 
@@ -2066,16 +2070,17 @@ ExpressionDecompiler::decompilePC(jsbyte
           case JSOP_TOSTRING:
             return write("TOSTRING(") &&
                    decompilePCForStackOperand(pc, -1) &&
                    write(")");
 
           case JSOP_UNINITIALIZED:
             return write("UNINITIALIZED");
 
+          case JSOP_AWAIT:
           case JSOP_YIELD:
             // Printing "yield SOMETHING" is confusing since the operand doesn't
             // match to the syntax, since the stack operand for "yield 10" is
             // the result object, not 10.
             return write("RVAL");
 
           default:
             break;
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -306,16 +306,17 @@ UNIFIED_SOURCES += [
     'proxy/ScriptedProxyHandler.cpp',
     'proxy/SecurityWrapper.cpp',
     'proxy/Wrapper.cpp',
     'threading/Mutex.cpp',
     'threading/ProtectedData.cpp',
     'vm/ArgumentsObject.cpp',
     'vm/ArrayBufferObject.cpp',
     'vm/AsyncFunction.cpp',
+    'vm/AsyncIteration.cpp',
     'vm/Caches.cpp',
     'vm/CallNonGenericMethod.cpp',
     'vm/CharacterEncoding.cpp',
     'vm/CodeCoverage.cpp',
     'vm/Compression.cpp',
     'vm/DateTime.cpp',
     'vm/Debugger.cpp',
     'vm/DebuggerMemory.cpp',
@@ -748,16 +749,17 @@ GENERATED_FILES += [('selfhosted.out.h',
 selfhosted = GENERATED_FILES[('selfhosted.out.h', 'selfhosted.js')]
 selfhosted.script = 'builtin/embedjs.py:generate_selfhosted'
 selfhosted.inputs = [
     'js.msg',
     'builtin/TypedObjectConstants.h',
     'builtin/SelfHostingDefines.h',
     'builtin/Utilities.js',
     'builtin/Array.js',
+    'builtin/AsyncIteration.js',
     'builtin/Classes.js',
     'builtin/Date.js',
     'builtin/Error.js',
     'builtin/Function.js',
     'builtin/Generator.js',
     'builtin/Intl.js',
     'builtin/IntlCurrency.js',
     'builtin/IntlData.js',
@@ -788,8 +790,11 @@ if CONFIG['JS_HAS_CTYPES']:
         DEFINES['FFI_BUILDING'] = True
 
 if CONFIG['GNU_CXX']:
     CXXFLAGS += ['-Wno-shadow', '-Werror=format']
 
 # Suppress warnings in third-party code.
 if CONFIG['CLANG_CXX']:
     SOURCES['jsdtoa.cpp'].flags += ['-Wno-implicit-fallthrough']
+
+if CONFIG['OS_ARCH'] == 'WINNT':
+    DEFINES['NOMINMAX'] = True
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -81,16 +81,17 @@
 #include "shell/jsoptparse.h"
 #include "shell/jsshell.h"
 #include "shell/OSObject.h"
 #include "threading/ConditionVariable.h"
 #include "threading/LockGuard.h"
 #include "threading/Thread.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/AsyncFunction.h"
+#include "vm/AsyncIteration.h"
 #include "vm/Compression.h"
 #include "vm/Debugger.h"
 #include "vm/HelperThreads.h"
 #include "vm/Monitor.h"
 #include "vm/MutexIDs.h"
 #include "vm/Shape.h"
 #include "vm/SharedArrayObject.h"
 #include "vm/StringBuffer.h"
@@ -2390,16 +2391,18 @@ ValueToScript(JSContext* cx, HandleValue
             fun = &target->as<JSFunction>();
         else
             break;
     }
 
     // Get unwrapped async function.
     if (IsWrappedAsyncFunction(fun))
         fun = GetUnwrappedAsyncFunction(fun);
+    if (IsWrappedAsyncGenerator(fun))
+        fun = GetUnwrappedAsyncGenerator(fun);
 
     if (!fun->isInterpreted()) {
         JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr, JSSMSG_SCRIPTS_ONLY);
         return nullptr;
     }
 
     JSScript* script = JSFunction::getOrCreateScript(cx, fun);
     if (!script)
--- a/js/src/tests/ecma_2017/AsyncFunctions/syntax.js
+++ b/js/src/tests/ecma_2017/AsyncFunctions/syntax.js
@@ -4,19 +4,16 @@ var summary = "async/await syntax";
 print(BUGNUMBER + ": " + summary);
 
 if (typeof Reflect !== "undefined" && Reflect.parse) {
     assertEq(Reflect.parse("function a() {}").body[0].async, false);
     assertEq(Reflect.parse("function* a() {}").body[0].async, false);
     assertEq(Reflect.parse("async function a() {}").body[0].async, true);
     assertEq(Reflect.parse("() => {}").body[0].async, undefined);
 
-    // Async generators are not allowed (with regards to spec)
-    assertThrows(() => Reflect.parse("async function* a() {}"), SyntaxError);
-
     // No line terminator after async
     assertEq(Reflect.parse("async\nfunction a(){}").body[0].expression.name, "async");
 
     // Async function expressions
     assertEq(Reflect.parse("(async function() {})()").body[0].expression.callee.async, true);
     assertEq(Reflect.parse("var k = async function() {}").body[0].declarations[0].init.async, true);
     assertEq(Reflect.parse("var nmd = async function named() {}").body[0].declarations[0].init.id.name, "named");
 
--- a/js/src/tests/ecma_6/Symbol/well-known.js
+++ b/js/src/tests/ecma_6/Symbol/well-known.js
@@ -6,17 +6,18 @@ var names = [
     "iterator",
     "match",
     "replace",
     "search",
     "species",
     "hasInstance",
     "split",
     "toPrimitive",
-    "unscopables"
+    "unscopables",
+    "asyncIterator"
 ];
 
 for (var name of names) {
     // Well-known symbols exist.
     assertEq(typeof Symbol[name], "symbol");
 
     // They are never in the registry.
     assertEq(Symbol[name] !== Symbol.for("Symbol." + name), true);
--- a/js/src/tests/jstests.list
+++ b/js/src/tests/jstests.list
@@ -35,16 +35,19 @@ skip script test262/built-ins/ThrowTypeE
 
 #########################################################################
 # Test262 tests disabled when features are only conditionally available #
 #########################################################################
 
 skip-if(!Array.prototype.values) script test262/built-ins/Array/prototype/Symbol.iterator.js
 skip-if(!Array.prototype.values) include test262/built-ins/Array/prototype/values/jstests.list
 
+# Async generator is non-release-or-beta only.
+skip-if(release_or_beta) include test262/language/expressions/async-generators/jstests.list
+skip-if(release_or_beta) include test262/language/statements/async-generator/jstests.list
 
 #####################################
 # Test262 tests disabled on browser #
 #####################################
 
 # Defines a non-configurable property on the WindowProxy object.
 skip-if(!xulRuntime.shell) script test262/annexB/language/eval-code/direct/global-block-decl-eval-global-exsting-global-update.js
 skip-if(!xulRuntime.shell) script test262/annexB/language/eval-code/direct/global-if-decl-else-decl-a-eval-global-exsting-global-update.js
@@ -488,20 +491,16 @@ skip script test262/annexB/language/eval
 skip script test262/annexB/language/eval-code/indirect/global-switch-dflt-eval-global-exsting-global-init.js
 skip script test262/annexB/language/eval-code/indirect/global-if-decl-no-else-eval-global-exsting-global-init.js
 skip script test262/annexB/language/eval-code/indirect/global-if-decl-else-stmt-eval-global-exsting-global-init.js
 skip script test262/annexB/language/eval-code/indirect/global-if-decl-else-decl-b-eval-global-exsting-global-init.js
 skip script test262/annexB/language/eval-code/indirect/global-if-decl-else-decl-a-eval-global-exsting-global-init.js
 skip script test262/annexB/language/eval-code/indirect/global-block-decl-eval-global-exsting-global-init.js
 skip script test262/annexB/language/eval-code/indirect/global-switch-case-eval-global-exsting-global-init.js
 
-# https://bugzilla.mozilla.org/show_bug.cgi?id=1331092
-skip include test262/language/expressions/async-generators/jstests.list
-skip include test262/language/statements/async-generator/jstests.list
-
 # SIMD.
 skip script test262/built-ins/Simd/check.js
 skip script test262/built-ins/Simd/from.js
 skip script test262/built-ins/Simd/operators.js
 skip script test262/built-ins/Simd/replace_lane.js
 skip script test262/built-ins/Simd/shuffle.js
 skip script test262/built-ins/Simd/swizzle.js
 
@@ -866,8 +865,16 @@ skip script test262/intl402/NumberFormat
 skip script test262/intl402/DateTimeFormat/prototype/format/12.3.2_1_a_L15.js
 
 # https://bugzilla.mozilla.org/show_bug.cgi?id=1346080
 skip script test262/intl402/PluralRules/prototype/select/tainting.js
 
 # https://bugzilla.mozilla.org/show_bug.cgi?id=1340304
 skip-if(!xulRuntime.shell) script test262/language/module-code/instn-iee-err-circular.js
 skip-if(!xulRuntime.shell) script test262/language/module-code/instn-iee-err-circular-as.js
+
+# Async generators are now a thing.
+skip script test262/language/statements/async-function/early-errors-no-async-generator-n.js
+
+# Need to be rewritten to follow the change in https://github.com/tc39/proposal-async-iteration/pull/92
+skip script test262/language/statements/async-generator/yield-star-async-next.js
+skip script test262/language/statements/async-generator/yield-star-async-return.js
+skip script test262/language/statements/async-generator/yield-star-async-throw.js
--- a/js/src/tests/lib/manifest.py
+++ b/js/src/tests/lib/manifest.py
@@ -27,16 +27,17 @@ class XULInfo:
         self.isdebug = isdebug
         self.browserIsRemote = False
 
     def as_js(self):
         """Return JS that when executed sets up variables so that JS expression
         predicates on XUL build info evaluate properly."""
 
         return ('var xulRuntime = {{ OS: "{}", XPCOMABI: "{}", shell: true }};'
+                'var release_or_beta = getBuildConfiguration().release_or_beta;'
                 'var isDebugBuild={}; var Android={}; '
                 'var browserIsRemote={}'.format(
                     self.os,
                     self.abi,
                     str(self.isdebug).lower(),
                     str(self.os == "Android").lower(),
                     str(self.browserIsRemote).lower()))
 
new file mode 100644
--- /dev/null
+++ b/js/src/vm/AsyncIteration.cpp
@@ -0,0 +1,636 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * 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 "vm/AsyncIteration.h"
+
+#include "jsarray.h"
+#include "jscompartment.h"
+
+#include "builtin/Promise.h"
+#include "vm/GeneratorObject.h"
+#include "vm/GlobalObject.h"
+#include "vm/Interpreter.h"
+#include "vm/SelfHosting.h"
+
+#include "jscntxtinlines.h"
+#include "jsobjinlines.h"
+
+#include "vm/NativeObject-inl.h"
+
+using namespace js;
+using namespace js::gc;
+
+#define UNWRAPPED_ASYNC_WRAPPED_SLOT 1
+#define WRAPPED_ASYNC_UNWRAPPED_SLOT 0
+
+// Async Iteration proposal 2.3.10 Runtime Semantics: EvaluateBody.
+static bool
+WrappedAsyncGenerator(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    RootedFunction wrapped(cx, &args.callee().as<JSFunction>());
+    RootedValue unwrappedVal(cx, wrapped->getExtendedSlot(WRAPPED_ASYNC_UNWRAPPED_SLOT));
+    RootedFunction unwrapped(cx, &unwrappedVal.toObject().as<JSFunction>());
+    RootedValue thisValue(cx, args.thisv());
+
+    // Step 1.
+    RootedValue generatorVal(cx);
+    InvokeArgs args2(cx);
+    if (!args2.init(cx, argc))
+        return false;
+    for (size_t i = 0, len = argc; i < len; i++)
+        args2[i].set(args[i]);
+    if (!Call(cx, unwrappedVal, thisValue, args2, &generatorVal))
+        return false;
+
+    // Step 2.
+    Rooted<AsyncGeneratorObject*> asyncGenObj(
+        cx, AsyncGeneratorObject::create(cx, wrapped, generatorVal));
+    if (!asyncGenObj)
+        return false;
+
+    // Step 3 (skipped).
+    // Done in AsyncGeneratorObject::create and generator.
+
+    // Step 4.
+    args.rval().setObject(*asyncGenObj);
+    return true;
+}
+
+JSObject*
+js::WrapAsyncGeneratorWithProto(JSContext* cx, HandleFunction unwrapped, HandleObject proto)
+{
+    MOZ_ASSERT(unwrapped->isAsync());
+    MOZ_ASSERT(proto, "We need an explicit prototype to avoid the default"
+                      "%FunctionPrototype% fallback in NewFunctionWithProto().");
+
+    // Create a new function with AsyncGeneratorPrototype, reusing the name and
+    // the length of `unwrapped`.
+
+    RootedAtom funName(cx, unwrapped->explicitName());
+    uint16_t length;
+    if (!JSFunction::getLength(cx, unwrapped, &length))
+        return nullptr;
+
+    RootedFunction wrapped(cx, NewFunctionWithProto(cx, WrappedAsyncGenerator, length,
+                                                    JSFunction::NATIVE_FUN, nullptr,
+                                                    funName, proto,
+                                                    AllocKind::FUNCTION_EXTENDED,
+                                                    TenuredObject));
+    if (!wrapped)
+        return nullptr;
+
+    if (unwrapped->hasCompileTimeName())
+        wrapped->setCompileTimeName(unwrapped->compileTimeName());
+
+    // Link them to each other to make GetWrappedAsyncGenerator and
+    // GetUnwrappedAsyncGenerator work.
+    unwrapped->setExtendedSlot(UNWRAPPED_ASYNC_WRAPPED_SLOT, ObjectValue(*wrapped));
+    wrapped->setExtendedSlot(WRAPPED_ASYNC_UNWRAPPED_SLOT, ObjectValue(*unwrapped));
+
+    return wrapped;
+}
+
+JSObject*
+js::WrapAsyncGenerator(JSContext* cx, HandleFunction unwrapped)
+{
+    RootedObject proto(cx, GlobalObject::getOrCreateAsyncGenerator(cx, cx->global()));
+    if (!proto)
+        return nullptr;
+
+    return WrapAsyncGeneratorWithProto(cx, unwrapped, proto);
+}
+
+bool
+js::IsWrappedAsyncGenerator(JSFunction* fun)
+{
+    return fun->maybeNative() == WrappedAsyncGenerator;
+}
+
+JSFunction*
+js::GetWrappedAsyncGenerator(JSFunction* unwrapped)
+{
+    MOZ_ASSERT(unwrapped->isAsync());
+    return &unwrapped->getExtendedSlot(UNWRAPPED_ASYNC_WRAPPED_SLOT).toObject().as<JSFunction>();
+}
+
+JSFunction*
+js::GetUnwrappedAsyncGenerator(JSFunction* wrapped)
+{
+    MOZ_ASSERT(IsWrappedAsyncGenerator(wrapped));
+    JSFunction* unwrapped = &wrapped->getExtendedSlot(WRAPPED_ASYNC_UNWRAPPED_SLOT)
+                            .toObject().as<JSFunction>();
+    MOZ_ASSERT(unwrapped->isAsync());
+    return unwrapped;
+}
+
+static MOZ_MUST_USE bool
+AsyncGeneratorResume(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
+                     CompletionKind completionKind, HandleValue argument);
+
+// Async Iteration proposal 5.1.1 Await Fulfilled Functions.
+MOZ_MUST_USE bool
+js::AsyncGeneratorAwaitedFulfilled(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
+                                   HandleValue value)
+{
+    return AsyncGeneratorResume(cx, asyncGenObj, CompletionKind::Normal, value);
+}
+
+// Async Iteration proposal 5.1.2 Await Rejected Functions.
+MOZ_MUST_USE bool
+js::AsyncGeneratorAwaitedRejected(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
+                                  HandleValue reason)
+{
+    return AsyncGeneratorResume(cx, asyncGenObj, CompletionKind::Throw, reason);
+}
+
+const Class AsyncFromSyncIteratorObject::class_ = {
+    "AsyncFromSyncIteratorObject",
+    JSCLASS_HAS_RESERVED_SLOTS(AsyncFromSyncIteratorObject::Slots)
+};
+
+// Async Iteration proposal 6.1.3.1.
+JSObject*
+js::CreateAsyncFromSyncIterator(JSContext* cx, HandleObject iter)
+{
+    // Step 1 (implicit).
+    // Done in bytecode emitted by emitAsyncIterator.
+
+    // Steps 2-4.
+    return AsyncFromSyncIteratorObject::create(cx, iter);
+}
+
+// Async Iteration proposal 6.1.3.1 steps 2-4.
+/* static */ JSObject*
+AsyncFromSyncIteratorObject::create(JSContext* cx, HandleObject iter)
+{
+    // Step 2.
+    RootedObject proto(cx, GlobalObject::getOrCreateAsyncFromSyncIteratorPrototype(cx,
+                                                                                   cx->global()));
+    if (!proto)
+        return nullptr;
+
+    RootedObject obj(cx, NewNativeObjectWithGivenProto(cx, &class_, proto));
+    if (!obj)
+        return nullptr;
+
+    Handle<AsyncFromSyncIteratorObject*> asyncIter = obj.as<AsyncFromSyncIteratorObject>();
+
+    // Step 3.
+    asyncIter->setIterator(iter);
+
+    // Step 4.
+    return asyncIter;
+}
+
+// Async Iteration proposal 6.1.3.2.1 %AsyncFromSyncIteratorPrototype%.next.
+static bool
+AsyncFromSyncIteratorNext(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    return AsyncFromSyncIteratorMethod(cx, args, CompletionKind::Normal);
+}
+
+// Async Iteration proposal 6.1.3.2.2 %AsyncFromSyncIteratorPrototype%.return.
+static bool
+AsyncFromSyncIteratorReturn(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    return AsyncFromSyncIteratorMethod(cx, args, CompletionKind::Return);
+}
+
+// Async Iteration proposal 6.1.3.2.3 %AsyncFromSyncIteratorPrototype%.throw.
+static bool
+AsyncFromSyncIteratorThrow(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    return AsyncFromSyncIteratorMethod(cx, args, CompletionKind::Throw);
+}
+
+// Async Iteration proposal 6.4.1.2 AsyncGenerator.prototype.next.
+static bool
+AsyncGeneratorNext(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    // Steps 1-3.
+    return AsyncGeneratorEnqueue(cx, args.thisv(), CompletionKind::Normal, args.get(0),
+                                 args.rval());
+}
+
+// Async Iteration proposal 6.4.1.3 AsyncGenerator.prototype.return.
+static bool
+AsyncGeneratorReturn(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    // Steps 1-3.
+    return AsyncGeneratorEnqueue(cx, args.thisv(), CompletionKind::Return, args.get(0),
+                                 args.rval());
+}
+
+// Async Iteration proposal 6.4.1.4 AsyncGenerator.prototype.throw.
+static bool
+AsyncGeneratorThrow(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    // Steps 1-3.
+    return AsyncGeneratorEnqueue(cx, args.thisv(), CompletionKind::Throw, args.get(0),
+                                 args.rval());
+}
+
+const Class AsyncGeneratorObject::class_ = {
+    "AsyncGenerator",
+    JSCLASS_HAS_RESERVED_SLOTS(AsyncGeneratorObject::Slots)
+};
+
+// ES 2017 draft 9.1.13.
+template <typename ProtoGetter>
+static JSObject*
+OrdinaryCreateFromConstructor(JSContext* cx, HandleFunction fun,
+                              ProtoGetter protoGetter, const Class* clasp)
+{
+    // Step 1 (skipped).
+
+    // Step 2.
+    RootedValue protoVal(cx);
+    if (!GetProperty(cx, fun, fun, cx->names().prototype, &protoVal))
+        return nullptr;
+
+    RootedObject proto(cx, protoVal.isObject() ? &protoVal.toObject() : nullptr);
+    if (!proto) {
+        proto = protoGetter(cx, cx->global());
+        if (!proto)
+            return nullptr;
+    }
+
+    // Step 3.
+    return NewNativeObjectWithGivenProto(cx, clasp, proto);
+}
+
+/* static */ AsyncGeneratorObject*
+AsyncGeneratorObject::create(JSContext* cx, HandleFunction asyncGen, HandleValue generatorVal)
+{
+    MOZ_ASSERT(generatorVal.isObject());
+    MOZ_ASSERT(generatorVal.toObject().is<GeneratorObject>());
+
+    RootedObject obj(
+        cx, OrdinaryCreateFromConstructor(cx, asyncGen,
+                                          GlobalObject::getOrCreateAsyncGeneratorPrototype,
+                                          &class_));
+    if (!obj)
+        return nullptr;
+
+    Handle<AsyncGeneratorObject*> asyncGenObj = obj.as<AsyncGeneratorObject>();
+
+    // Async Iteration proposal 6.4.3.2 AsyncGeneratorStart.
+    // Step 6.
+    asyncGenObj->setGenerator(generatorVal);
+
+    // Step 7.
+    asyncGenObj->setSuspendedStart();
+
+    // Step 8.
+    asyncGenObj->clearSingleQueueRequest();
+
+    return asyncGenObj;
+}
+
+/* static */ MOZ_MUST_USE bool
+AsyncGeneratorObject::enqueueRequest(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
+                                     Handle<AsyncGeneratorRequest*> request)
+{
+    if (asyncGenObj->isSingleQueue()) {
+        if (asyncGenObj->isSingleQueueEmpty()) {
+            asyncGenObj->setSingleQueueRequest(request);
+            return true;
+        }
+
+        RootedArrayObject queue(cx, NewDenseEmptyArray(cx));
+        if (!queue)
+            return false;
+
+        if (!NewbornArrayPush(cx, queue, ObjectValue(*asyncGenObj->singleQueueRequest())))
+            return false;
+        if (!NewbornArrayPush(cx, queue, ObjectValue(*request)))
+            return false;
+
+        asyncGenObj->setQueue(queue);
+        return true;
+    }
+
+    RootedArrayObject queue(cx, asyncGenObj->queue());
+
+    FixedInvokeArgs<1> args(cx);
+    args[0].setObject(*request);
+    args.setThis(ObjectValue(*queue));
+    return CallJSNative(cx, array_push, args);
+}
+
+/* static */ AsyncGeneratorRequest*
+AsyncGeneratorObject::dequeueRequest(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj)
+{
+    if (asyncGenObj->isSingleQueue()) {
+        AsyncGeneratorRequest* request = asyncGenObj->singleQueueRequest();
+        asyncGenObj->clearSingleQueueRequest();
+        return request;
+    }
+
+    RootedArrayObject queue(cx, asyncGenObj->queue());
+
+    FixedInvokeArgs<0> args(cx);
+    args.setThis(ObjectValue(*queue));
+    if (!CallJSNative(cx, array_shift, args))
+        return nullptr;
+
+    return &args.rval().toObject().as<AsyncGeneratorRequest>();
+}
+
+/* static */ AsyncGeneratorRequest*
+AsyncGeneratorObject::peekRequest(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj)
+{
+    if (asyncGenObj->isSingleQueue())
+        return asyncGenObj->singleQueueRequest();
+
+    RootedArrayObject queue(cx, asyncGenObj->queue());
+
+    RootedValue requestVal(cx);
+    if (!GetElement(cx, queue, queue, 0, &requestVal))
+        return nullptr;
+
+    return &requestVal.toObject().as<AsyncGeneratorRequest>();
+}
+
+const Class AsyncGeneratorRequest::class_ = {
+    "AsyncGeneratorRequest",
+    JSCLASS_HAS_RESERVED_SLOTS(AsyncGeneratorRequest::Slots)
+};
+
+// Async Iteration proposal 6.4.3.1.
+/* static */ AsyncGeneratorRequest*
+AsyncGeneratorRequest::create(JSContext* cx, CompletionKind completionKind_,
+                              HandleValue completionValue_, HandleObject promise_)
+{
+    RootedObject obj(cx, NewNativeObjectWithGivenProto(cx, &class_, nullptr));
+    if (!obj)
+        return nullptr;
+
+    Handle<AsyncGeneratorRequest*> request = obj.as<AsyncGeneratorRequest>();
+    request->setCompletionKind(completionKind_);
+    request->setCompletionValue(completionValue_);
+    request->setPromise(promise_);
+    return request;
+}
+
+// Async Iteration proposal 6.4.3.2 steps 5.d-g.
+static MOZ_MUST_USE bool
+AsyncGeneratorReturned(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
+                       HandleValue value)
+{
+    // Step 5.d.
+    asyncGenObj->setCompleted();
+
+    // Step 5.e (done in bytecode).
+    // Step 5.f.i (implicit).
+
+    // Step 5.g.
+    return AsyncGeneratorResolve(cx, asyncGenObj, value, true);
+}
+
+// Async Iteration proposal 6.4.3.2 steps 5.d, f.
+static MOZ_MUST_USE bool
+AsyncGeneratorThrown(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj)
+{
+    // Step 5.d.
+    asyncGenObj->setCompleted();
+
+    // Not much we can do about uncatchable exceptions, so just bail.
+    if (!cx->isExceptionPending())
+        return false;
+
+    // Step 5.f.i.
+    RootedValue value(cx);
+    if (!GetAndClearException(cx, &value))
+        return false;
+
+    // Step 5.f.ii.
+    return AsyncGeneratorReject(cx, asyncGenObj, value);
+}
+
+// Async Iteration proposal 6.4.3.5.
+MOZ_MUST_USE bool
+js::AsyncGeneratorResumeNext(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj)
+{
+    // Step 1 (implicit).
+
+    // Steps 2-3.
+    MOZ_ASSERT(!asyncGenObj->isExecuting());
+
+    // Steps 4-5.
+    if (asyncGenObj->isQueueEmpty())
+        return true;
+
+    // Steps 6-7.
+    Rooted<AsyncGeneratorRequest*> request(
+        cx, AsyncGeneratorObject::peekRequest(cx, asyncGenObj));
+    if (!request)
+        return false;
+
+    // Step 8.
+    CompletionKind completionKind = request->completionKind();
+
+    // Step 9.
+    if (completionKind != CompletionKind::Normal) {
+        // Step 9.a.
+        if (asyncGenObj->isSuspendedStart())
+            asyncGenObj->setCompleted();
+
+        // Step 9.b.
+        if (asyncGenObj->isCompleted()) {
+            // Step 9.b.i.
+            RootedValue value(cx, request->completionValue());
+            if (completionKind == CompletionKind::Return)
+                return AsyncGeneratorResolve(cx, asyncGenObj, value, true);
+            // Step 9.b.ii.
+            return AsyncGeneratorReject(cx, asyncGenObj, value);
+        }
+    } else if (asyncGenObj->isCompleted()) {
+        // Step 10.
+        return AsyncGeneratorResolve(cx, asyncGenObj, UndefinedHandleValue, true);
+    }
+
+    // Step 11.
+    MOZ_ASSERT(asyncGenObj->isSuspendedStart() || asyncGenObj->isSuspendedYield());
+
+    // Step 15 (reordered).
+    asyncGenObj->setExecuting();
+
+    RootedValue argument(cx, request->completionValue());
+
+    // Steps 12-14, 16-20.
+    return AsyncGeneratorResume(cx, asyncGenObj, completionKind, argument);
+}
+
+// Async Iteration proposal 6.2.1.3 (partially).
+// Most steps are done in generator.
+static MOZ_MUST_USE bool
+AsyncGeneratorYield(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
+                    HandleValue value)
+{
+    // Step 5.
+    asyncGenObj->setSuspendedYield();
+
+    // Step 8.
+    return AsyncGeneratorResolve(cx, asyncGenObj, value, false);
+}
+
+// Async Iteration proposal 6.4.3.5 steps 12-14, 16-20.
+// Async Iteration proposal 6.2.1.2 step 10.
+// Async Iteration proposal 6.4.3.2 step 5.f-g.
+// Async Iteration proposal 5.1 steps 2-9.
+// Execution context switching is handled in generator.
+static MOZ_MUST_USE bool
+AsyncGeneratorResume(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
+                     CompletionKind completionKind, HandleValue argument)
+{
+    RootedValue generatorVal(cx, asyncGenObj->generatorVal());
+
+    // 6.4.3.5 steps 12-14, 16-20.
+    HandlePropertyName funName = completionKind == CompletionKind::Normal
+                                 ? cx->names().StarGeneratorNext
+                                 : completionKind == CompletionKind::Throw
+                                 ? cx->names().StarGeneratorThrow
+                                 : cx->names().StarGeneratorReturn;
+    FixedInvokeArgs<1> args(cx);
+    args[0].set(argument);
+    RootedValue result(cx);
+    if (!CallSelfHostedFunction(cx, funName, generatorVal, args, &result)) {
+        // 6.4.3.2 step 5.d, f.
+        return AsyncGeneratorThrown(cx, asyncGenObj);
+    }
+
+    if (asyncGenObj->generatorObj()->isAfterAwait())
+        return AsyncGeneratorAwait(cx, asyncGenObj, result);
+
+    // The following code corresponds to the following 3 cases:
+    //   * yield
+    //   * yield*
+    //   * return
+    // For yield and return, property access is done on an internal result
+    // object and it's not observable.
+    // For yield*, it's done on a possibly user-provided result object, and
+    // it's observable.
+
+    // 2.2.1 yield* steps 6.a.vii, 6.b.ii.7, 6.c.ix.
+    RootedObject resultObj(cx, &result.toObject());
+    RootedValue value(cx);
+    if (!GetProperty(cx, resultObj, resultObj, cx->names().value, &value))
+        return false;
+
+    if (asyncGenObj->generatorObj()->isAfterYield())
+        return AsyncGeneratorYield(cx, asyncGenObj, value);
+
+    // 6.4.3.2 step 5.d-g.
+    return AsyncGeneratorReturned(cx, asyncGenObj, value);
+}
+
+static const JSFunctionSpec async_iterator_proto_methods[] = {
+    JS_SELF_HOSTED_SYM_FN(asyncIterator, "AsyncIteratorIdentity", 0, 0),
+    JS_FS_END
+};
+
+static const JSFunctionSpec async_from_sync_iter_methods[] = {
+    JS_FN("next", AsyncFromSyncIteratorNext, 1, 0),
+    JS_FN("throw", AsyncFromSyncIteratorThrow, 1, 0),
+    JS_FN("return", AsyncFromSyncIteratorReturn, 1, 0),
+    JS_FS_END
+};
+
+static const JSFunctionSpec async_generator_methods[] = {
+    JS_FN("next", AsyncGeneratorNext, 1, 0),
+    JS_FN("throw", AsyncGeneratorThrow, 1, 0),
+    JS_FN("return", AsyncGeneratorReturn, 1, 0),
+    JS_FS_END
+};
+
+/* static */ MOZ_MUST_USE bool
+GlobalObject::initAsyncGenerators(JSContext* cx, Handle<GlobalObject*> global)
+{
+    if (global->getReservedSlot(ASYNC_ITERATOR_PROTO).isObject())
+        return true;
+
+    // Async Iteration proposal 6.1.2 %AsyncIteratorPrototype%.
+    RootedObject asyncIterProto(cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
+    if (!asyncIterProto)
+        return false;
+    if (!DefinePropertiesAndFunctions(cx, asyncIterProto, nullptr, async_iterator_proto_methods))
+        return false;
+
+    // Async Iteration proposal 6.1.3.2 %AsyncFromSyncIteratorPrototype%.
+    RootedObject asyncFromSyncIterProto(
+        cx, GlobalObject::createBlankPrototypeInheriting(cx, global, &PlainObject::class_,
+                                                         asyncIterProto));
+    if (!asyncFromSyncIterProto)
+        return false;
+    if (!DefinePropertiesAndFunctions(cx, asyncFromSyncIterProto, nullptr,
+                                      async_from_sync_iter_methods) ||
+        !DefineToStringTag(cx, asyncFromSyncIterProto, cx->names().AsyncFromSyncIterator))
+    {
+        return false;
+    }
+
+    // Async Iteration proposal 6.4.1 %AsyncGeneratorPrototype%.
+    RootedObject asyncGenProto(
+        cx, GlobalObject::createBlankPrototypeInheriting(cx, global, &PlainObject::class_,
+                                                         asyncIterProto));
+    if (!asyncGenProto)
+        return false;
+    if (!DefinePropertiesAndFunctions(cx, asyncGenProto, nullptr, async_generator_methods) ||
+        !DefineToStringTag(cx, asyncGenProto, cx->names().AsyncGenerator))
+    {
+        return false;
+    }
+
+    // Async Iteration proposal 6.3.3 %AsyncGenerator%.
+    RootedObject asyncGenerator(cx, NewSingletonObjectWithFunctionPrototype(cx, global));
+    if (!asyncGenerator)
+        return false;
+    if (!JSObject::setDelegate(cx, asyncGenerator))
+        return false;
+    if (!LinkConstructorAndPrototype(cx, asyncGenerator, asyncGenProto, JSPROP_READONLY,
+                                     JSPROP_READONLY) ||
+        !DefineToStringTag(cx, asyncGenerator, cx->names().AsyncGeneratorFunction))
+    {
+        return false;
+    }
+
+    RootedValue function(cx, global->getConstructor(JSProto_Function));
+    if (!function.toObjectOrNull())
+        return false;
+    RootedObject proto(cx, &function.toObject());
+    RootedAtom name(cx, cx->names().AsyncGeneratorFunction);
+
+    // Async Iteration proposal 6.3.2 %AsyncGeneratorFunction%.
+    RootedObject asyncGenFunction(
+        cx, NewFunctionWithProto(cx, AsyncGeneratorConstructor, 1, JSFunction::NATIVE_CTOR,
+                                 nullptr, name, proto, gc::AllocKind::FUNCTION, SingletonObject));
+    if (!asyncGenFunction)
+        return false;
+    if (!LinkConstructorAndPrototype(cx, asyncGenFunction, asyncGenerator,
+                                     JSPROP_PERMANENT | JSPROP_READONLY, JSPROP_READONLY))
+    {
+        return false;
+    }
+
+    global->setReservedSlot(ASYNC_ITERATOR_PROTO, ObjectValue(*asyncIterProto));
+    global->setReservedSlot(ASYNC_FROM_SYNC_ITERATOR_PROTO, ObjectValue(*asyncFromSyncIterProto));
+    global->setReservedSlot(ASYNC_GENERATOR, ObjectValue(*asyncGenerator));
+    global->setReservedSlot(ASYNC_GENERATOR_FUNCTION, ObjectValue(*asyncGenFunction));
+    global->setReservedSlot(ASYNC_GENERATOR_PROTO, ObjectValue(*asyncGenProto));
+    return true;
+}
new file mode 100644
--- /dev/null
+++ b/js/src/vm/AsyncIteration.h
@@ -0,0 +1,230 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * 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 vm_AsyncIteration_h
+#define vm_AsyncIteration_h
+
+#include "jscntxt.h"
+#include "jsobj.h"
+
+#include "builtin/Promise.h"
+#include "vm/GeneratorObject.h"
+
+namespace js {
+
+// Async generator consists of 2 functions, |wrapped| and |unwrapped|.
+// |unwrapped| is a generator function compiled from async generator script,
+// |await| behaves just like |yield| there.  |unwrapped| isn't exposed to user
+// script.
+// |wrapped| is a native function that is the value of async generator.
+
+JSObject*
+WrapAsyncGeneratorWithProto(JSContext* cx, HandleFunction unwrapped, HandleObject proto);
+
+JSObject*
+WrapAsyncGenerator(JSContext* cx, HandleFunction unwrapped);
+
+bool
+IsWrappedAsyncGenerator(JSFunction* fun);
+
+JSFunction*
+GetWrappedAsyncGenerator(JSFunction* unwrapped);
+
+JSFunction*
+GetUnwrappedAsyncGenerator(JSFunction* wrapped);
+
+MOZ_MUST_USE bool
+AsyncGeneratorAwaitedFulfilled(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
+                              HandleValue value);
+
+MOZ_MUST_USE bool
+AsyncGeneratorAwaitedRejected(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
+                             HandleValue reason);
+
+class AsyncGeneratorRequest : public NativeObject
+{
+  private:
+    enum AsyncGeneratorRequestSlots {
+        Slot_CompletionKind = 0,
+        Slot_CompletionValue,
+        Slot_Promise,
+        Slots,
+    };
+
+    void setCompletionKind(CompletionKind completionKind_) {
+        setFixedSlot(Slot_CompletionKind,
+                     Int32Value(static_cast<int32_t>(completionKind_)));
+    }
+    void setCompletionValue(HandleValue completionValue_) {
+        setFixedSlot(Slot_CompletionValue, completionValue_);
+    }
+    void setPromise(HandleObject promise_) {
+        setFixedSlot(Slot_Promise, ObjectValue(*promise_));
+    }
+
+  public:
+    static const Class class_;
+
+    static AsyncGeneratorRequest*
+    create(JSContext* cx, CompletionKind completionKind, HandleValue completionValue,
+           HandleObject promise);
+
+    CompletionKind completionKind() const {
+        return static_cast<CompletionKind>(getFixedSlot(Slot_CompletionKind).toInt32());
+    }
+    JS::Value completionValue() const {
+        return getFixedSlot(Slot_CompletionValue);
+    }
+    JSObject* promise() const {
+        return &getFixedSlot(Slot_Promise).toObject();
+    }
+};
+
+class AsyncGeneratorObject : public NativeObject
+{
+  private:
+    enum AsyncGeneratorObjectSlots {
+        Slot_State = 0,
+        Slot_Generator,
+        Slot_QueueOrRequest,
+        Slots
+    };
+
+    enum State {
+        State_SuspendedStart,
+        State_SuspendedYield,
+        State_Executing,
+        State_Completed
+    };
+
+    State state() const {
+        return static_cast<State>(getFixedSlot(Slot_State).toInt32());
+    }
+    void setState(State state_) {
+        setFixedSlot(Slot_State, Int32Value(state_));
+    }
+
+    void setGenerator(const Value& value) {
+        setFixedSlot(Slot_Generator, value);
+    }
+
+    // Queue is implemented in 2 ways.  If only one request is queued ever,
+    // request is stored directly to the slot.  Once 2 requests are queued, an
+    // array is created and requests are pushed into it, and the array is
+    // stored to the slot.
+
+    bool isSingleQueue() const {
+        return getFixedSlot(Slot_QueueOrRequest).isNull() ||
+               getFixedSlot(Slot_QueueOrRequest).toObject().is<AsyncGeneratorRequest>();
+    }
+    bool isSingleQueueEmpty() const {
+        return getFixedSlot(Slot_QueueOrRequest).isNull();
+    }
+    void setSingleQueueRequest(AsyncGeneratorRequest* request) {
+        setFixedSlot(Slot_QueueOrRequest, ObjectValue(*request));
+    }
+    void clearSingleQueueRequest() {
+        setFixedSlot(Slot_QueueOrRequest, NullHandleValue);
+    }
+    AsyncGeneratorRequest* singleQueueRequest() const {
+        return &getFixedSlot(Slot_QueueOrRequest).toObject().as<AsyncGeneratorRequest>();
+    }
+
+    ArrayObject* queue() const {
+        return &getFixedSlot(Slot_QueueOrRequest).toObject().as<ArrayObject>();
+    }
+    void setQueue(JSObject* queue_) {
+        setFixedSlot(Slot_QueueOrRequest, ObjectValue(*queue_));
+    }
+
+  public:
+    static const Class class_;
+
+    static AsyncGeneratorObject*
+    create(JSContext* cx, HandleFunction asyncGen, HandleValue generatorVal);
+
+    bool isSuspendedStart() const {
+        return state() == State_SuspendedStart;
+    }
+    bool isSuspendedYield() const {
+        return state() == State_SuspendedYield;
+    }
+    bool isExecuting() const {
+        return state() == State_Executing;
+    }
+    bool isCompleted() const {
+        return state() == State_Completed;
+    }
+
+    void setSuspendedStart() {
+        setState(State_SuspendedStart);
+    }
+    void setSuspendedYield() {
+        setState(State_SuspendedYield);
+    }
+    void setExecuting() {
+        setState(State_Executing);
+    }
+    void setCompleted() {
+        setState(State_Completed);
+    }
+
+    JS::Value generatorVal() const {
+        return getFixedSlot(Slot_Generator);
+    }
+    GeneratorObject* generatorObj() const {
+        return &getFixedSlot(Slot_Generator).toObject().as<GeneratorObject>();
+    }
+
+    static MOZ_MUST_USE bool
+    enqueueRequest(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
+                   Handle<AsyncGeneratorRequest*> request);
+
+    static AsyncGeneratorRequest*
+    dequeueRequest(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj);
+
+    static AsyncGeneratorRequest*
+    peekRequest(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj);
+
+    bool isQueueEmpty() const {
+        if (isSingleQueue())
+            return isSingleQueueEmpty();
+        return queue()->length() == 0;
+    }
+};
+
+JSObject*
+CreateAsyncFromSyncIterator(JSContext* cx, HandleObject iter);
+
+class AsyncFromSyncIteratorObject : public NativeObject
+{
+  private:
+    enum AsyncFromSyncIteratorObjectSlots {
+        Slot_Iterator = 0,
+        Slots
+    };
+
+    void setIterator(HandleObject iterator_) {
+        setFixedSlot(Slot_Iterator, ObjectValue(*iterator_));
+    }
+
+  public:
+    static const Class class_;
+
+    static JSObject*
+    create(JSContext* cx, HandleObject iter);
+
+    JSObject* iterator() const {
+        return &getFixedSlot(Slot_Iterator).toObject();
+    }
+};
+
+MOZ_MUST_USE bool
+AsyncGeneratorResumeNext(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj);
+
+} // namespace js
+
+#endif /* vm_AsyncIteration_h */
--- a/js/src/vm/CommonPropertyNames.h
+++ b/js/src/vm/CommonPropertyNames.h
@@ -25,17 +25,20 @@
     macro(ArraySpecies, ArraySpecies, "ArraySpecies") \
     macro(ArraySpeciesCreate, ArraySpeciesCreate, "ArraySpeciesCreate") \
     macro(ArrayToLocaleString, ArrayToLocaleString, "ArrayToLocaleString") \
     macro(ArrayType, ArrayType, "ArrayType") \
     macro(ArrayValues, ArrayValues, "ArrayValues") \
     macro(ArrayValuesAt, ArrayValuesAt, "ArrayValuesAt") \
     macro(as, as, "as") \
     macro(Async, Async, "Async") \
+    macro(AsyncFromSyncIterator, AsyncFromSyncIterator, "Async-from-Sync Iterator") \
     macro(AsyncFunction, AsyncFunction, "AsyncFunction") \
+    macro(AsyncGenerator, AsyncGenerator, "AsyncGenerator") \
+    macro(AsyncGeneratorFunction, AsyncGeneratorFunction, "AsyncGeneratorFunction") \
     macro(AsyncWrapped, AsyncWrapped, "AsyncWrapped") \
     macro(async, async, "async") \
     macro(await, await, "await") \
     macro(Bool8x16, Bool8x16, "Bool8x16") \
     macro(Bool16x8, Bool16x8, "Bool16x8") \
     macro(Bool32x4, Bool32x4, "Bool32x4") \
     macro(Bool64x2, Bool64x2, "Bool64x2") \
     macro(boundWithSpace, boundWithSpace, "bound ") \
@@ -314,16 +317,17 @@
     macro(shape, shape, "shape") \
     macro(size, size, "size") \
     macro(source, source, "source") \
     macro(SpeciesConstructor, SpeciesConstructor, "SpeciesConstructor") \
     macro(stack, stack, "stack") \
     macro(star, star, "*") \
     macro(starDefaultStar, starDefaultStar, "*default*") \
     macro(StarGeneratorNext, StarGeneratorNext, "StarGeneratorNext") \
+    macro(StarGeneratorReturn, StarGeneratorReturn, "StarGeneratorReturn") \
     macro(StarGeneratorThrow, StarGeneratorThrow, "StarGeneratorThrow") \
     macro(startTimestamp, startTimestamp, "startTimestamp") \
     macro(state, state, "state") \
     macro(static, static_, "static") \
     macro(std_Function_apply, std_Function_apply, "std_Function_apply") \
     macro(sticky, sticky, "sticky") \
     macro(StringIterator, StringIterator, "String Iterator") \
     macro(strings, strings, "strings") \
--- a/js/src/vm/GlobalObject.h
+++ b/js/src/vm/GlobalObject.h
@@ -93,16 +93,21 @@ class GlobalObject : public NativeObject
         ARRAY_ITERATOR_PROTO,
         STRING_ITERATOR_PROTO,
         LEGACY_GENERATOR_OBJECT_PROTO,
         STAR_GENERATOR_OBJECT_PROTO,
         STAR_GENERATOR_FUNCTION_PROTO,
         STAR_GENERATOR_FUNCTION,
         ASYNC_FUNCTION_PROTO,
         ASYNC_FUNCTION,
+        ASYNC_ITERATOR_PROTO,
+        ASYNC_FROM_SYNC_ITERATOR_PROTO,
+        ASYNC_GENERATOR,
+        ASYNC_GENERATOR_FUNCTION,
+        ASYNC_GENERATOR_PROTO,
         MAP_ITERATOR_PROTO,
         SET_ITERATOR_PROTO,
         COLLATOR_PROTO,
         NUMBER_FORMAT,
         NUMBER_FORMAT_PROTO,
         DATE_TIME_FORMAT,
         DATE_TIME_FORMAT_PROTO,
         PLURAL_RULES_PROTO,
@@ -624,16 +629,46 @@ class GlobalObject : public NativeObject
                                                    initAsyncFunction));
     }
 
     static JSObject*
     getOrCreateAsyncFunction(JSContext* cx, Handle<GlobalObject*> global) {
         return getOrCreateObject(cx, global, ASYNC_FUNCTION, initAsyncFunction);
     }
 
+    static NativeObject*
+    getOrCreateAsyncIteratorPrototype(JSContext* cx, Handle<GlobalObject*> global)
+    {
+        return MaybeNativeObject(getOrCreateObject(cx, global, ASYNC_ITERATOR_PROTO,
+                                                   initAsyncGenerators));
+    }
+
+    static NativeObject*
+    getOrCreateAsyncFromSyncIteratorPrototype(JSContext* cx, Handle<GlobalObject*> global) {
+        return MaybeNativeObject(getOrCreateObject(cx, global, ASYNC_FROM_SYNC_ITERATOR_PROTO,
+                                                   initAsyncGenerators));
+    }
+
+    static NativeObject*
+    getOrCreateAsyncGenerator(JSContext* cx, Handle<GlobalObject*> global) {
+        return MaybeNativeObject(getOrCreateObject(cx, global, ASYNC_GENERATOR,
+                                                   initAsyncGenerators));
+    }
+
+    static JSObject*
+    getOrCreateAsyncGeneratorFunction(JSContext* cx, Handle<GlobalObject*> global) {
+        return getOrCreateObject(cx, global, ASYNC_GENERATOR_FUNCTION, initAsyncGenerators);
+    }
+
+    static NativeObject*
+    getOrCreateAsyncGeneratorPrototype(JSContext* cx, Handle<GlobalObject*> global) {
+        return MaybeNativeObject(getOrCreateObject(cx, global, ASYNC_GENERATOR_PROTO,
+                                                   initAsyncGenerators));
+    }
+
     static JSObject*
     getOrCreateMapIteratorPrototype(JSContext* cx, Handle<GlobalObject*> global) {
         return getOrCreateObject(cx, global, MAP_ITERATOR_PROTO, initMapIteratorProto);
     }
 
     static JSObject*
     getOrCreateSetIteratorPrototype(JSContext* cx, Handle<GlobalObject*> global) {
         return getOrCreateObject(cx, global, SET_ITERATOR_PROTO, initSetIteratorProto);
@@ -770,16 +805,18 @@ class GlobalObject : public NativeObject
     static bool initStringIteratorProto(JSContext* cx, Handle<GlobalObject*> global);
 
     // Implemented in vm/GeneratorObject.cpp.
     static bool initLegacyGeneratorProto(JSContext* cx, Handle<GlobalObject*> global);
     static bool initStarGenerators(JSContext* cx, Handle<GlobalObject*> global);
 
     static bool initAsyncFunction(JSContext* cx, Handle<GlobalObject*> global);
 
+    static bool initAsyncGenerators(JSContext* cx, Handle<GlobalObject*> global);
+
     // Implemented in builtin/MapObject.cpp.
     static bool initMapIteratorProto(JSContext* cx, Handle<GlobalObject*> global);
     static bool initSetIteratorProto(JSContext* cx, Handle<GlobalObject*> global);
 
     // Implemented in Intl.cpp.
     static bool initIntlObject(JSContext* cx, Handle<GlobalObject*> global);
     static bool addPluralRulesConstructor(JSContext* cx, HandleObject intl);
 
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -34,16 +34,17 @@
 #include "jsstr.h"
 
 #include "builtin/Eval.h"
 #include "jit/AtomicOperations.h"
 #include "jit/BaselineJIT.h"
 #include "jit/Ion.h"
 #include "jit/IonAnalysis.h"
 #include "vm/AsyncFunction.h"
+#include "vm/AsyncIteration.h"
 #include "vm/Debugger.h"
 #include "vm/GeneratorObject.h"
 #include "vm/Opcodes.h"
 #include "vm/Scope.h"
 #include "vm/Shape.h"
 #include "vm/Stopwatch.h"
 #include "vm/TraceLogging.h"
 
@@ -1917,18 +1918,16 @@ CASE(EnableInterruptsPseudoOpcode)
     /* Commence executing the actual opcode. */
     SANITY_CHECKS();
     DISPATCH_TO(op);
 }
 
 /* Various 1-byte no-ops. */
 CASE(JSOP_NOP)
 CASE(JSOP_NOP_DESTRUCTURING)
-CASE(JSOP_UNUSED192)
-CASE(JSOP_UNUSED210)
 CASE(JSOP_UNUSED211)
 CASE(JSOP_UNUSED220)
 CASE(JSOP_UNUSED221)
 CASE(JSOP_UNUSED222)
 CASE(JSOP_UNUSED223)
 CASE(JSOP_CONDSWITCH)
 {
     MOZ_ASSERT(CodeSpec[*REGS.pc].length == 1);
@@ -3561,16 +3560,39 @@ CASE(JSOP_TOASYNC)
     JSObject* wrapped = WrapAsyncFunction(cx, unwrapped);
     if (!wrapped)
         goto error;
 
     REGS.sp[-1].setObject(*wrapped);
 }
 END_CASE(JSOP_TOASYNC)
 
+CASE(JSOP_TOASYNCGEN)
+{
+    ReservedRooted<JSFunction*> unwrapped(&rootFunction0,
+                                          &REGS.sp[-1].toObject().as<JSFunction>());
+    JSObject* wrapped = WrapAsyncGenerator(cx, unwrapped);
+    if (!wrapped)
+        goto error;
+
+    REGS.sp[-1].setObject(*wrapped);
+}
+END_CASE(JSOP_TOASYNCGEN)
+
+CASE(JSOP_TOASYNCITER)
+{
+    ReservedRooted<JSObject*> iter(&rootObject1, &REGS.sp[-1].toObject());
+    JSObject* asyncIter = CreateAsyncFromSyncIterator(cx, iter);
+    if (!asyncIter)
+        goto error;
+
+    REGS.sp[-1].setObject(*asyncIter);
+}
+END_CASE(JSOP_TOASYNCITER)
+
 CASE(JSOP_SETFUNNAME)
 {
     MOZ_ASSERT(REGS.stackDepth() >= 2);
     FunctionPrefixKind prefixKind = FunctionPrefixKind(GET_UINT8(REGS.pc));
     ReservedRooted<Value> name(&rootValue0, REGS.sp[-1]);
     ReservedRooted<JSFunction*> fun(&rootFunction0, &REGS.sp[-2].toObject().as<JSFunction>());
     if (!SetFunctionNameIfNoOwnName(cx, fun, name, prefixKind))
         goto error;
@@ -5082,16 +5104,20 @@ js::ThrowCheckIsObject(JSContext* cx, Ch
         break;
       case CheckIsObjectKind::IteratorThrow:
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                                   JSMSG_ITER_METHOD_RETURNED_PRIMITIVE, "throw");
         break;
       case CheckIsObjectKind::GetIterator:
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_GET_ITER_RETURNED_PRIMITIVE);
         break;
+      case CheckIsObjectKind::GetAsyncIterator:
+        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+                                  JSMSG_GET_ASYNC_ITER_RETURNED_PRIMITIVE);
+        break;
       default:
         MOZ_CRASH("Unknown kind");
     }
     return false;
 }
 
 bool
 js::ThrowCheckIsCallable(JSContext* cx, CheckIsCallableKind kind)
--- a/js/src/vm/Interpreter.h
+++ b/js/src/vm/Interpreter.h
@@ -551,17 +551,18 @@ ReportRuntimeLexicalError(JSContext* cx,
 // for redeclarations during runtime in JSOP_DEF{VAR,LET,CONST}.
 void
 ReportRuntimeRedeclaration(JSContext* cx, HandlePropertyName name, const char* redeclKind);
 
 enum class CheckIsObjectKind : uint8_t {
     IteratorNext,
     IteratorReturn,
     IteratorThrow,
-    GetIterator
+    GetIterator,
+    GetAsyncIterator
 };
 
 bool
 ThrowCheckIsObject(JSContext* cx, CheckIsObjectKind kind);
 
 enum class CheckIsCallableKind : uint8_t {
     IteratorReturn
 };
--- a/js/src/vm/Opcodes.h
+++ b/js/src/vm/Opcodes.h
@@ -1962,17 +1962,25 @@ 1234567890123456789012345678901234567890
      * Throw an exception if the value on top of the stack is not the TDZ
      * MagicValue. Used in derived class constructors.
      *   Category: Variables and Scopes
      *   Type: This
      *   Operands:
      *   Stack: this => this
      */ \
     macro(JSOP_CHECKTHISREINIT,191,"checkthisreinit",NULL,1,  1,  1,  JOF_BYTE) \
-    macro(JSOP_UNUSED192,     192,"unused192",   NULL,    1,  0,  0,  JOF_BYTE) \
+    /*
+     * Pops the top of stack value as 'unwrapped', converts it to async
+     * generator 'wrapped', and pushes 'wrapped' back on the stack.
+     *   Category: Statements
+     *   Type: Generator
+     *   Operands:
+     *   Stack: unwrapped => wrapped
+     */ \
+    macro(JSOP_TOASYNCGEN,    192, "toasyncgen", NULL,    1,  1,  1, JOF_BYTE) \
     \
     /*
      * Pops the top two values on the stack as 'propval' and 'obj', pushes
      * 'propval' property of 'obj' onto the stack.
      *
      * Like JSOP_GETELEM but for call context.
      *   Category: Literals
      *   Type: Object
@@ -2139,17 +2147,25 @@ 1234567890123456789012345678901234567890
      * Pops the generator and the return value 'promise', stops interpretation
      * and returns 'promise'. Pushes resolved value onto the stack.
      *   Category: Statements
      *   Type: Generator
      *   Operands: uint24_t yieldAndAwaitIndex
      *   Stack: promise, gen => resolved
      */ \
     macro(JSOP_AWAIT,         209, "await",        NULL,  4,  2,  1,  JOF_UINT24) \
-    macro(JSOP_UNUSED210,     210, "unused210",    NULL,  1,  0,  0,  JOF_BYTE) \
+    /*
+     * Pops the iterator from the top of the stack, and create async iterator
+     * from it and push the async iterator back onto the stack.
+     *   Category: Statements
+     *   Type: Generator
+     *   Operands:
+     *   Stack: iter => asynciter
+     */ \
+    macro(JSOP_TOASYNCITER,   210, "toasynciter",  NULL,  1,  1,  1,  JOF_BYTE) \
     macro(JSOP_UNUSED211,     211, "unused211",    NULL,  1,  0,  0,  JOF_BYTE) \
     /*
      * Initializes generator frame, creates a generator and pushes it on the
      * stack.
      *   Category: Statements
      *   Type: Generator
      *   Operands:
      *   Stack: => generator
--- a/js/xpconnect/src/XPCLocale.cpp
+++ b/js/xpconnect/src/XPCLocale.cpp
@@ -5,33 +5,28 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/Assertions.h"
 
 #include "jsapi.h"
 
 #include "nsCollationCID.h"
 #include "nsJSUtils.h"
-#include "nsIPlatformCharset.h"
 #include "nsICollation.h"
+#include "nsNativeCharsetUtils.h"
 #include "nsUnicharUtils.h"
 #include "nsComponentManagerUtils.h"
 #include "nsServiceManagerUtils.h"
-#include "mozilla/dom/EncodingUtils.h"
 #include "mozilla/intl/LocaleService.h"
-#include "mozilla/intl/OSPreferences.h"
 #include "mozilla/Preferences.h"
-#include "nsIUnicodeDecoder.h"
 
 #include "xpcpublic.h"
 
 using namespace JS;
-using mozilla::dom::EncodingUtils;
 using mozilla::intl::LocaleService;
-using mozilla::intl::OSPreferences;
 
 /**
  * JS locale callbacks implemented by XPCOM modules.  These are theoretically
  * safe for use on multiple threads.  Unfortunately, the intl code underlying
  * these XPCOM modules doesn't yet support this, so in practice
  * XPCLocaleCallbacks are limited to the main thread.
  */
 struct XPCLocaleCallbacks : public JSLocaleCallbacks
@@ -158,84 +153,42 @@ private:
 
     rval.setInt32(result);
     return true;
   }
 
   bool
   ToUnicode(JSContext* cx, const char* src, MutableHandleValue rval)
   {
-    nsresult rv;
-
-    if (!mDecoder) {
-      // This code is only used by our prioprietary toLocaleFormat method
-      // and should be removed once we get rid of it.
-      // toLocaleFormat is used in non-ICU scenarios where we don't have
-      // access to any other date/time than the OS one, so we have to also
-      // use the OS locale for unicode conversions.
-      // See bug 1349470 for more details.
-      nsAutoCString osLocale;
-      OSPreferences::GetInstance()->GetSystemLocale(osLocale);
-      NS_ConvertUTF8toUTF16 localeStr(osLocale);
-
-      nsCOMPtr<nsIPlatformCharset> platformCharset =
-        do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &rv);
-
-      if (NS_SUCCEEDED(rv)) {
-        nsAutoCString charset;
-        rv = platformCharset->GetDefaultCharsetForLocale(localeStr, charset);
-        if (NS_SUCCEEDED(rv)) {
-          mDecoder = EncodingUtils::DecoderForEncoding(charset);
-        }
-      }
-    }
-
-    int32_t srcLength = strlen(src);
-
-    if (mDecoder) {
-      int32_t unicharLength = srcLength;
-      char16_t* unichars =
-        (char16_t*)JS_malloc(cx, (srcLength + 1) * sizeof(char16_t));
-      if (unichars) {
-        rv = mDecoder->Convert(src, &srcLength, unichars, &unicharLength);
-        if (NS_SUCCEEDED(rv)) {
-          // terminate the returned string
-          unichars[unicharLength] = 0;
-
-          // nsIUnicodeDecoder::Convert may use fewer than srcLength PRUnichars
-          if (unicharLength + 1 < srcLength + 1) {
-            char16_t* shrunkUnichars =
-              (char16_t*)JS_realloc(cx, unichars,
-                                     (srcLength + 1) * sizeof(char16_t),
-                                     (unicharLength + 1) * sizeof(char16_t));
-            if (shrunkUnichars)
-              unichars = shrunkUnichars;
-          }
-          JSString* str = JS_NewUCString(cx, reinterpret_cast<char16_t*>(unichars), unicharLength);
-          if (str) {
-            rval.setString(str);
-            return true;
-          }
-        }
-        JS_free(cx, unichars);
-      }
+    // This code is only used by our prioprietary toLocaleFormat method
+    // and should be removed once we get rid of it.
+    // toLocaleFormat is used in non-ICU scenarios where we don't have
+    // access to any other date/time than the OS one, so we have to also
+    // use the OS locale for unicode conversions.
+    // See bug 1349470 for more details.
+    nsAutoString result;
+    NS_CopyNativeToUnicode(nsDependentCString(src), result);
+    JSString* ucstr =
+      JS_NewUCStringCopyN(cx, result.get(), result.Length());
+    if (ucstr) {
+      rval.setString(ucstr);
+      return true;
     }
 
     xpc::Throw(cx, NS_ERROR_OUT_OF_MEMORY);
     return false;
   }
 
   void AssertThreadSafety() const
   {
     MOZ_ASSERT(mThread == PR_GetCurrentThread(),
                "XPCLocaleCallbacks used unsafely!");
   }
 
   nsCOMPtr<nsICollation> mCollation;
-  nsCOMPtr<nsIUnicodeDecoder> mDecoder;
 #ifdef DEBUG
   PRThread* mThread;
 #endif
 };
 
 bool
 xpc_LocalizeContext(JSContext* cx)
 {
--- a/js/xpconnect/tests/chrome/chrome.ini
+++ b/js/xpconnect/tests/chrome/chrome.ini
@@ -109,8 +109,9 @@ skip-if = os == 'win' || os == 'mac' # b
 [test_watchpoints.xul]
 [test_weakmap_keys_preserved.xul]
 [test_weakmap_keys_preserved2.xul]
 [test_weakmaps.xul]
 [test_weakref.xul]
 [test_windowProxyDeadWrapper.html]
 [test_wrappers.xul]
 [test_xrayToJS.xul]
+[test_asyncIteration.xul]
new file mode 100644
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_asyncIteration.xul
@@ -0,0 +1,43 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+                 type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1331092
+-->
+<window title="Mozilla Bug 1331092"
+  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+  <!-- test results are displayed in the html:body -->
+  <body xmlns="http://www.w3.org/1999/xhtml">
+  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1331092"
+     target="_blank">Mozilla Bug 1331092</a>
+  </body>
+
+  <!-- test code goes here -->
+  <script type="application/javascript"><![CDATA[
+try {
+    eval(`
+async function* f() {
+}
+`);
+    ok(false, "should throw");
+} catch (e) {
+    ok(e instanceof SyntaxError, "Async Generator shouldn't be supported in chrome code");
+}
+
+try {
+    eval(`
+async function f() {
+  for await (let x of []) {
+  }
+}
+`);
+    ok(false, "should throw");
+} catch (e) {
+    ok(e instanceof SyntaxError, "for-await-of shouldn't be supported in chrome code");
+}
+  ]]></script>
+</window>
--- a/js/xpconnect/wrappers/XrayWrapper.h
+++ b/js/xpconnect/wrappers/XrayWrapper.h
@@ -59,17 +59,20 @@ enum XrayType {
 };
 
 class XrayTraits
 {
 public:
     constexpr XrayTraits() {}
 
     static JSObject* getTargetObject(JSObject* wrapper) {
-        return js::UncheckedUnwrap(wrapper, /* stopAtWindowProxy = */ false);
+        JSObject* target = js::UncheckedUnwrap(wrapper, /* stopAtWindowProxy = */ false);
+        if (target)
+            JS::ExposeObjectToActiveJS(target);
+        return target;
     }
 
     virtual bool resolveNativeProperty(JSContext* cx, JS::HandleObject wrapper,
                                        JS::HandleObject holder, JS::HandleId id,
                                        JS::MutableHandle<JS::PropertyDescriptor> desc) = 0;
     // NB: resolveOwnProperty may decide whether or not to cache what it finds
     // on the holder. If the result is not cached, the lookup will happen afresh
     // for each access, which is the right thing for things like dynamic NodeList
--- a/layout/generic/crashtests/crashtests.list
+++ b/layout/generic/crashtests/crashtests.list
@@ -20,17 +20,17 @@ load 309322-1.html # bug 1338007
 load 309322-2.html # bug 1338007
 load 309322-3.html
 load 309322-4.html
 load 310556-1.xhtml
 load 321224.xul
 load 322780-1.xul
 load 323381-1.html
 load 323381-2.html
-asserts-if(gtkWidget,1) asserts-if(Android&&asyncPan,1) asserts-if(stylo,0) load 323386-1.html # Bug 718883
+asserts-if(gtkWidget,1) asserts-if(Android&&asyncPan,1) asserts-if(stylo,0) asserts-if(winWidget,1) load 323386-1.html # Bug 718883
 load 323389-1.html
 load 323389-2.html
 load 323493-1.html
 load 323495-1.html
 load 324318-1.html
 load 328946-1.html
 load 331284-1.xhtml
 load 331292.html
--- a/layout/painting/moz.build
+++ b/layout/painting/moz.build
@@ -11,16 +11,17 @@ EXPORTS += [
     'ActiveLayerTracker.h',
     'DisplayItemClip.h',
     'DisplayItemClipChain.h',
     'DisplayItemScrollClip.h',
     'DisplayListClipState.h',
     'FrameLayerBuilder.h',
     'LayerState.h',
     'nsCSSRenderingBorders.h',
+    'nsCSSRenderingGradients.h',
     'nsDisplayItemTypes.h',
     'nsDisplayItemTypesList.h',
     'nsDisplayList.h',
     'nsDisplayListInvalidation.h',
     'nsImageRenderer.h',
 ]
 
 EXPORTS.mozilla += [
@@ -34,16 +35,17 @@ UNIFIED_SOURCES += [
     'DisplayItemClipChain.cpp',
     'DisplayItemScrollClip.cpp',
     'DisplayListClipState.cpp',
     'DottedCornerFinder.cpp',
     'FrameLayerBuilder.cpp',
     'MaskLayerImageCache.cpp',
     'nsCSSRendering.cpp',
     'nsCSSRenderingBorders.cpp',
+    'nsCSSRenderingGradients.cpp',
     'nsDisplayList.cpp',
     'nsDisplayListInvalidation.cpp',
     'nsImageRenderer.cpp',
     'PaintTracker.cpp',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
--- a/layout/painting/nsCSSRendering.cpp
+++ b/layout/painting/nsCSSRendering.cpp
@@ -413,27 +413,16 @@ protected:
       }
       MOZ_ASSERT_UNREACHABLE("None of the frames is a descendant of this rtc?");
     }
     MOZ_ASSERT_UNREACHABLE("Do we have any other type of line container?");
     return false;
   }
 };
 
-// A resolved color stop, with a specific position along the gradient line and
-// a color.
-struct ColorStop {
-  ColorStop(): mPosition(0), mIsMidpoint(false) {}
-  ColorStop(double aPosition, bool aIsMidPoint, const Color& aColor) :
-    mPosition(aPosition), mIsMidpoint(aIsMidPoint), mColor(aColor) {}
-  double mPosition; // along the gradient line; 0=start, 1=end
-  bool mIsMidpoint;
-  Color mColor;
-};
-
 /* Local functions */
 static nscolor MakeBevelColor(mozilla::Side whichSide, uint8_t style,
                               nscolor aBackgroundColor,
                               nscolor aBorderColor);
 
 static InlineBackgroundData* gInlineBGData = nullptr;
 
 // Initialize any static variables used by nsCSSRendering.
@@ -1877,55 +1866,54 @@ nsCSSRendering::PaintBoxShadowInner(nsPr
                               shadowOffset);
     renderContext->Restore();
   }
 }
 
 /* static */
 nsCSSRendering::PaintBGParams
 nsCSSRendering::PaintBGParams::ForAllLayers(nsPresContext& aPresCtx,
-                                            nsRenderingContext& aRenderingCtx,
                                             const nsRect& aDirtyRect,
                                             const nsRect& aBorderArea,
                                             nsIFrame *aFrame,
                                             uint32_t aPaintFlags,
                                             float aOpacity)
 {
   MOZ_ASSERT(aFrame);
 
-  PaintBGParams result(aPresCtx, aRenderingCtx, aDirtyRect, aBorderArea,
+  PaintBGParams result(aPresCtx, aDirtyRect, aBorderArea,
                        aFrame, aPaintFlags, -1, CompositionOp::OP_OVER,
                        aOpacity);
 
   return result;
 }
 
 /* static */
 nsCSSRendering::PaintBGParams
 nsCSSRendering::PaintBGParams::ForSingleLayer(nsPresContext& aPresCtx,
-                                              nsRenderingContext& aRenderingCtx,
                                               const nsRect& aDirtyRect,
                                               const nsRect& aBorderArea,
                                               nsIFrame *aFrame,
                                               uint32_t aPaintFlags,
                                               int32_t aLayer,
                                               CompositionOp aCompositionOp,
                                               float aOpacity)
 {
   MOZ_ASSERT(aFrame && (aLayer != -1));
 
-  PaintBGParams result(aPresCtx, aRenderingCtx, aDirtyRect, aBorderArea,
+  PaintBGParams result(aPresCtx, aDirtyRect, aBorderArea,
                        aFrame, aPaintFlags, aLayer, aCompositionOp,
                        aOpacity);
 
   return result;
 }
 
 DrawResult
-nsCSSRendering::PaintStyleImageLayer(const PaintBGParams& aParams)
+nsCSSRendering::PaintStyleImageLayer(const PaintBGParams& aParams,
+                                     nsRenderingContext& aRenderingCtx)
 {
   PROFILER_LABEL("nsCSSRendering", "PaintBackground",
     js::ProfileEntry::Category::GRAPHICS);
 
   NS_PRECONDITION(aParams.frame,
                   "Frame is expected to be provided to PaintBackground");
 
   nsStyleContext *sc;
@@ -1942,17 +1930,76 @@ nsCSSRendering::PaintStyleImageLayer(con
     nsIContent* content = aParams.frame->GetContent();
     if (!content || content->GetParent()) {
       return DrawResult::SUCCESS;
     }
 
     sc = aParams.frame->StyleContext();
   }
 
-  return PaintStyleImageLayerWithSC(aParams, sc, *aParams.frame->StyleBorder());
+  return PaintStyleImageLayerWithSC(aParams, aRenderingCtx, sc, *aParams.frame->StyleBorder());
+}
+
+bool
+nsCSSRendering::CanBuildWebRenderDisplayItemsForStyleImageLayer(nsPresContext& aPresCtx,
+                                                                nsIFrame *aFrame,
+                                                                const nsStyleBackground* aBackgroundStyle,
+                                                                int32_t aLayer)
+{
+  if (!aBackgroundStyle) {
+    return false;
+  }
+
+  MOZ_ASSERT(aFrame &&
+             aLayer >= 0 &&
+             (uint32_t)aLayer < aBackgroundStyle->mImage.mLayers.Length());
+
+  // We cannot draw native themed backgrounds
+  const nsStyleDisplay* displayData = aFrame->StyleDisplay();
+  if (displayData->UsedAppearance()) {
+    nsITheme *theme = aPresCtx.GetTheme();
+    if (theme && theme->ThemeSupportsWidget(&aPresCtx,
+                                            aFrame,
+                                            displayData->UsedAppearance())) {
+      return false;
+    }
+  }
+
+  // We only support painting gradients for a single style image layer
+  return aBackgroundStyle->mImage.mLayers[aLayer].mImage.GetType() == eStyleImageType_Gradient;
+}
+
+void
+nsCSSRendering::BuildWebRenderDisplayItemsForStyleImageLayer(const PaintBGParams& aParams,
+                                                             mozilla::wr::DisplayListBuilder& aBuilder,
+                                                             mozilla::layers::WebRenderDisplayItemLayer* aLayer)
+{
+  NS_PRECONDITION(aParams.frame,
+                  "Frame is expected to be provided to BuildWebRenderDisplayItemsForStyleImageLayer");
+
+  nsStyleContext *sc;
+  if (!FindBackground(aParams.frame, &sc)) {
+    // We don't want to bail out if moz-appearance is set on a root
+    // node. If it has a parent content node, bail because it's not
+    // a root, otherwise keep going in order to let the theme stuff
+    // draw the background. The canvas really should be drawing the
+    // bg, but there's no way to hook that up via css.
+    if (!aParams.frame->StyleDisplay()->UsedAppearance()) {
+      return;
+    }
+
+    nsIContent* content = aParams.frame->GetContent();
+    if (!content || content->GetParent()) {
+      return;
+    }
+
+    sc = aParams.frame->StyleContext();
+  }
+
+  return BuildWebRenderDisplayItemsForStyleImageLayerWithSC(aParams, aBuilder, aLayer, sc, *aParams.frame->StyleBorder());
 }
 
 static bool
 IsOpaqueBorderEdge(const nsStyleBorder& aBorder, mozilla::Side aSide)
 {
   if (aBorder.GetComputedBorder().Side(aSide) == 0)
     return true;
   switch (aBorder.GetBorderStyle(aSide)) {
@@ -2395,959 +2442,16 @@ nsCSSRendering::DetermineBackgroundColor
       bg->BottomLayer().mImage.IsOpaque() &&
       bg->BottomLayer().mBlendMode == NS_STYLE_BLEND_NORMAL) {
     aDrawBackgroundColor = false;
   }
 
   return bgColor;
 }
 
-static gfxFloat
-ConvertGradientValueToPixels(const nsStyleCoord& aCoord,
-                             gfxFloat aFillLength,
-                             int32_t aAppUnitsPerPixel)
-{
-  switch (aCoord.GetUnit()) {
-    case eStyleUnit_Percent:
-      return aCoord.GetPercentValue() * aFillLength;
-    case eStyleUnit_Coord:
-      return NSAppUnitsToFloatPixels(aCoord.GetCoordValue(), aAppUnitsPerPixel);
-    case eStyleUnit_Calc: {
-      const nsStyleCoord::Calc *calc = aCoord.GetCalcValue();
-      return calc->mPercent * aFillLength +
-             NSAppUnitsToFloatPixels(calc->mLength, aAppUnitsPerPixel);
-    }
-    default:
-      NS_WARNING("Unexpected coord unit");
-      return 0;
-  }
-}
-
-// Given a box with size aBoxSize and origin (0,0), and an angle aAngle,
-// and a starting point for the gradient line aStart, find the endpoint of
-// the gradient line --- the intersection of the gradient line with a line
-// perpendicular to aAngle that passes through the farthest corner in the
-// direction aAngle.
-static gfxPoint
-ComputeGradientLineEndFromAngle(const gfxPoint& aStart,
-                                double aAngle,
-                                const gfxSize& aBoxSize)
-{
-  double dx = cos(-aAngle);
-  double dy = sin(-aAngle);
-  gfxPoint farthestCorner(dx > 0 ? aBoxSize.width : 0,
-                          dy > 0 ? aBoxSize.height : 0);
-  gfxPoint delta = farthestCorner - aStart;
-  double u = delta.x*dy - delta.y*dx;
-  return farthestCorner + gfxPoint(-u*dy, u*dx);
-}
-
-// Compute the start and end points of the gradient line for a linear gradient.
-static void
-ComputeLinearGradientLine(nsPresContext* aPresContext,
-                          nsStyleGradient* aGradient,
-                          const gfxSize& aBoxSize,
-                          gfxPoint* aLineStart,
-                          gfxPoint* aLineEnd)
-{
-  if (aGradient->mBgPosX.GetUnit() == eStyleUnit_None) {
-    double angle;
-    if (aGradient->mAngle.IsAngleValue()) {
-      angle = aGradient->mAngle.GetAngleValueInRadians();
-      if (!aGradient->mLegacySyntax) {
-        angle = M_PI_2 - angle;
-      }
-    } else {
-      angle = -M_PI_2; // defaults to vertical gradient starting from top
-    }
-    gfxPoint center(aBoxSize.width/2, aBoxSize.height/2);
-    *aLineEnd = ComputeGradientLineEndFromAngle(center, angle, aBoxSize);
-    *aLineStart = gfxPoint(aBoxSize.width, aBoxSize.height) - *aLineEnd;
-  } else if (!aGradient->mLegacySyntax) {
-    float xSign = aGradient->mBgPosX.GetPercentValue() * 2 - 1;
-    float ySign = 1 - aGradient->mBgPosY.GetPercentValue() * 2;
-    double angle = atan2(ySign * aBoxSize.width, xSign * aBoxSize.height);
-    gfxPoint center(aBoxSize.width/2, aBoxSize.height/2);
-    *aLineEnd = ComputeGradientLineEndFromAngle(center, angle, aBoxSize);
-    *aLineStart = gfxPoint(aBoxSize.width, aBoxSize.height) - *aLineEnd;
-  } else {
-    int32_t appUnitsPerPixel = aPresContext->AppUnitsPerDevPixel();
-    *aLineStart = gfxPoint(
-      ConvertGradientValueToPixels(aGradient->mBgPosX, aBoxSize.width,
-                                   appUnitsPerPixel),
-      ConvertGradientValueToPixels(aGradient->mBgPosY, aBoxSize.height,
-                                   appUnitsPerPixel));
-    if (aGradient->mAngle.IsAngleValue()) {
-      MOZ_ASSERT(aGradient->mLegacySyntax);
-      double angle = aGradient->mAngle.GetAngleValueInRadians();
-      *aLineEnd = ComputeGradientLineEndFromAngle(*aLineStart, angle, aBoxSize);
-    } else {
-      // No angle, the line end is just the reflection of the start point
-      // through the center of the box
-      *aLineEnd = gfxPoint(aBoxSize.width, aBoxSize.height) - *aLineStart;
-    }
-  }
-}
-
-// Compute the start and end points of the gradient line for a radial gradient.
-// Also returns the horizontal and vertical radii defining the circle or
-// ellipse to use.
-static void
-ComputeRadialGradientLine(nsPresContext* aPresContext,
-                          nsStyleGradient* aGradient,
-                          const gfxSize& aBoxSize,
-                          gfxPoint* aLineStart,
-                          gfxPoint* aLineEnd,
-                          double* aRadiusX,
-                          double* aRadiusY)
-{
-  if (aGradient->mBgPosX.GetUnit() == eStyleUnit_None) {
-    // Default line start point is the center of the box
-    *aLineStart = gfxPoint(aBoxSize.width/2, aBoxSize.height/2);
-  } else {
-    int32_t appUnitsPerPixel = aPresContext->AppUnitsPerDevPixel();
-    *aLineStart = gfxPoint(
-      ConvertGradientValueToPixels(aGradient->mBgPosX, aBoxSize.width,
-                                   appUnitsPerPixel),
-      ConvertGradientValueToPixels(aGradient->mBgPosY, aBoxSize.height,
-                                   appUnitsPerPixel));
-  }
-
-  // Compute gradient shape: the x and y radii of an ellipse.
-  double radiusX, radiusY;
-  double leftDistance = Abs(aLineStart->x);
-  double rightDistance = Abs(aBoxSize.width - aLineStart->x);
-  double topDistance = Abs(aLineStart->y);
-  double bottomDistance = Abs(aBoxSize.height - aLineStart->y);
-  switch (aGradient->mSize) {
-  case NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE:
-    radiusX = std::min(leftDistance, rightDistance);
-    radiusY = std::min(topDistance, bottomDistance);
-    if (aGradient->mShape == NS_STYLE_GRADIENT_SHAPE_CIRCULAR) {
-      radiusX = radiusY = std::min(radiusX, radiusY);
-    }
-    break;
-  case NS_STYLE_GRADIENT_SIZE_CLOSEST_CORNER: {
-    // Compute x and y distances to nearest corner
-    double offsetX = std::min(leftDistance, rightDistance);
-    double offsetY = std::min(topDistance, bottomDistance);
-    if (aGradient->mShape == NS_STYLE_GRADIENT_SHAPE_CIRCULAR) {
-      radiusX = radiusY = NS_hypot(offsetX, offsetY);
-    } else {
-      // maintain aspect ratio
-      radiusX = offsetX*M_SQRT2;
-      radiusY = offsetY*M_SQRT2;
-    }
-    break;
-  }
-  case NS_STYLE_GRADIENT_SIZE_FARTHEST_SIDE:
-    radiusX = std::max(leftDistance, rightDistance);
-    radiusY = std::max(topDistance, bottomDistance);
-    if (aGradient->mShape == NS_STYLE_GRADIENT_SHAPE_CIRCULAR) {
-      radiusX = radiusY = std::max(radiusX, radiusY);
-    }
-    break;
-  case NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER: {
-    // Compute x and y distances to nearest corner
-    double offsetX = std::max(leftDistance, rightDistance);
-    double offsetY = std::max(topDistance, bottomDistance);
-    if (aGradient->mShape == NS_STYLE_GRADIENT_SHAPE_CIRCULAR) {
-      radiusX = radiusY = NS_hypot(offsetX, offsetY);
-    } else {
-      // maintain aspect ratio
-      radiusX = offsetX*M_SQRT2;
-      radiusY = offsetY*M_SQRT2;
-    }
-    break;
-  }
-  case NS_STYLE_GRADIENT_SIZE_EXPLICIT_SIZE: {
-    int32_t appUnitsPerPixel = aPresContext->AppUnitsPerDevPixel();
-    radiusX = ConvertGradientValueToPixels(aGradient->mRadiusX,
-                                           aBoxSize.width, appUnitsPerPixel);
-    radiusY = ConvertGradientValueToPixels(aGradient->mRadiusY,
-                                           aBoxSize.height, appUnitsPerPixel);
-    break;
-  }
-  default:
-    radiusX = radiusY = 0;
-    MOZ_ASSERT(false, "unknown radial gradient sizing method");
-  }
-  *aRadiusX = radiusX;
-  *aRadiusY = radiusY;
-
-  double angle;
-  if (aGradient->mAngle.IsAngleValue()) {
-    angle = aGradient->mAngle.GetAngleValueInRadians();
-  } else {
-    // Default angle is 0deg
-    angle = 0.0;
-  }
-
-  // The gradient line end point is where the gradient line intersects
-  // the ellipse.
-  *aLineEnd = *aLineStart + gfxPoint(radiusX*cos(-angle), radiusY*sin(-angle));
-}
-
-
-static float Interpolate(float aF1, float aF2, float aFrac)
-{
-  return aF1 + aFrac * (aF2 - aF1);
-}
-
-// Returns aFrac*aC2 + (1 - aFrac)*C1. The interpolation is done
-// in unpremultiplied space, which is what SVG gradients and cairo
-// gradients expect.
-static Color
-InterpolateColor(const Color& aC1, const Color& aC2, float aFrac)
-{
-  double other = 1 - aFrac;
-  return Color(aC2.r*aFrac + aC1.r*other,
-               aC2.g*aFrac + aC1.g*other,
-               aC2.b*aFrac + aC1.b*other,
-               aC2.a*aFrac + aC1.a*other);
-}
-
-static nscoord
-FindTileStart(nscoord aDirtyCoord, nscoord aTilePos, nscoord aTileDim)
-{
-  NS_ASSERTION(aTileDim > 0, "Non-positive tile dimension");
-  double multiples = floor(double(aDirtyCoord - aTilePos)/aTileDim);
-  return NSToCoordRound(multiples*aTileDim + aTilePos);
-}
-
-static gfxFloat
-LinearGradientStopPositionForPoint(const gfxPoint& aGradientStart,
-                                   const gfxPoint& aGradientEnd,
-                                   const gfxPoint& aPoint)
-{
-  gfxPoint d = aGradientEnd - aGradientStart;
-  gfxPoint p = aPoint - aGradientStart;
-  /**
-   * Compute a parameter t such that a line perpendicular to the
-   * d vector, passing through aGradientStart + d*t, also
-   * passes through aPoint.
-   *
-   * t is given by
-   *   (p.x - d.x*t)*d.x + (p.y - d.y*t)*d.y = 0
-   *
-   * Solving for t we get
-   *   numerator = d.x*p.x + d.y*p.y
-   *   denominator = d.x^2 + d.y^2
-   *   t = numerator/denominator
-   *
-   * In nsCSSRendering::PaintGradient we know the length of d
-   * is not zero.
-   */
-  double numerator = d.x * p.x + d.y * p.y;
-  double denominator = d.x * d.x + d.y * d.y;
-  return numerator / denominator;
-}
-
-static bool
-RectIsBeyondLinearGradientEdge(const gfxRect& aRect,
-                               const gfxMatrix& aPatternMatrix,
-                               const nsTArray<ColorStop>& aStops,
-                               const gfxPoint& aGradientStart,
-                               const gfxPoint& aGradientEnd,
-                               Color* aOutEdgeColor)
-{
-  gfxFloat topLeft = LinearGradientStopPositionForPoint(
-    aGradientStart, aGradientEnd, aPatternMatrix.Transform(aRect.TopLeft()));
-  gfxFloat topRight = LinearGradientStopPositionForPoint(
-    aGradientStart, aGradientEnd, aPatternMatrix.Transform(aRect.TopRight()));
-  gfxFloat bottomLeft = LinearGradientStopPositionForPoint(
-    aGradientStart, aGradientEnd, aPatternMatrix.Transform(aRect.BottomLeft()));
-  gfxFloat bottomRight = LinearGradientStopPositionForPoint(
-    aGradientStart, aGradientEnd, aPatternMatrix.Transform(aRect.BottomRight()));
-
-  const ColorStop& firstStop = aStops[0];
-  if (topLeft < firstStop.mPosition && topRight < firstStop.mPosition &&
-      bottomLeft < firstStop.mPosition && bottomRight < firstStop.mPosition) {
-    *aOutEdgeColor = firstStop.mColor;
-    return true;
-  }
-
-  const ColorStop& lastStop = aStops.LastElement();
-  if (topLeft >= lastStop.mPosition && topRight >= lastStop.mPosition &&
-      bottomLeft >= lastStop.mPosition && bottomRight >= lastStop.mPosition) {
-    *aOutEdgeColor = lastStop.mColor;
-    return true;
-  }
-
-  return false;
-}
-
-static void ResolveMidpoints(nsTArray<ColorStop>& stops)
-{
-  for (size_t x = 1; x < stops.Length() - 1;) {
-    if (!stops[x].mIsMidpoint) {
-      x++;
-      continue;
-    }
-
-    Color color1 = stops[x-1].mColor;
-    Color color2 = stops[x+1].mColor;
-    float offset1 = stops[x-1].mPosition;
-    float offset2 = stops[x+1].mPosition;
-    float offset = stops[x].mPosition;
-    // check if everything coincides. If so, ignore the midpoint.
-    if (offset - offset1 == offset2 - offset) {
-      stops.RemoveElementAt(x);
-      continue;
-    }
-
-    // Check if we coincide with the left colorstop.
-    if (offset1 == offset) {
-      // Morph the midpoint to a regular stop with the color of the next
-      // color stop.
-      stops[x].mColor = color2;
-      stops[x].mIsMidpoint = false;
-      continue;
-    }
-
-    // Check if we coincide with the right colorstop.
-    if (offset2 == offset) {
-      // Morph the midpoint to a regular stop with the color of the previous
-      // color stop.
-      stops[x].mColor = color1;
-      stops[x].mIsMidpoint = false;
-      continue;
-    }
-
-    float midpoint = (offset - offset1) / (offset2 - offset1);
-    ColorStop newStops[9];
-    if (midpoint > .5f) {
-      for (size_t y = 0; y < 7; y++) {
-        newStops[y].mPosition = offset1 + (offset - offset1) * (7 + y) / 13;
-      }
-
-      newStops[7].mPosition = offset + (offset2 - offset) / 3;
-      newStops[8].mPosition = offset + (offset2 - offset) * 2 / 3;
-    } else {
-      newStops[0].mPosition = offset1 + (offset - offset1) / 3;
-      newStops[1].mPosition = offset1 + (offset - offset1) * 2 / 3;
-
-      for (size_t y = 0; y < 7; y++) {
-        newStops[y+2].mPosition = offset + (offset2 - offset) * y / 13;
-      }
-    }
-    // calculate colors
-
-    for (size_t y = 0; y < 9; y++) {
-      // Calculate the intermediate color stops per the formula of the CSS images
-      // spec. http://dev.w3.org/csswg/css-images/#color-stop-syntax
-      // 9 points were chosen since it is the minimum number of stops that always
-      // give the smoothest appearace regardless of midpoint position and difference
-      // in luminance of the end points.
-      float relativeOffset = (newStops[y].mPosition - offset1) / (offset2 - offset1);
-      float multiplier = powf(relativeOffset, logf(.5f) / logf(midpoint));
-
-      gfx::Float red = color1.r + multiplier * (color2.r - color1.r);
-      gfx::Float green = color1.g + multiplier * (color2.g - color1.g);
-      gfx::Float blue = color1.b + multiplier * (color2.b - color1.b);
-      gfx::Float alpha = color1.a + multiplier * (color2.a - color1.a);
-
-      newStops[y].mColor = Color(red, green, blue, alpha);
-    }
-
-    stops.ReplaceElementsAt(x, 1, newStops, 9);
-    x += 9;
-  }
-}
-
-static Color
-Premultiply(const Color& aColor)
-{
-  gfx::Float a = aColor.a;
-  return Color(aColor.r * a, aColor.g * a, aColor.b * a, a);
-}
-
-static Color
-Unpremultiply(const Color& aColor)
-{
-  gfx::Float a = aColor.a;
-  return (a > 0.f)
-       ? Color(aColor.r / a, aColor.g / a, aColor.b / a, a)
-       : aColor;
-}
-
-static Color
-TransparentColor(Color aColor) {
-  aColor.a = 0;
-  return aColor;
-}
-
-// Adjusts and adds color stops in such a way that drawing the gradient with
-// unpremultiplied interpolation looks nearly the same as if it were drawn with
-// premultiplied interpolation.
-static const float kAlphaIncrementPerGradientStep = 0.1f;
-static void
-ResolvePremultipliedAlpha(nsTArray<ColorStop>& aStops)
-{
-  for (size_t x = 1; x < aStops.Length(); x++) {
-    const ColorStop leftStop = aStops[x - 1];
-    const ColorStop rightStop = aStops[x];
-
-    // if the left and right stop have the same alpha value, we don't need
-    // to do anything
-    if (leftStop.mColor.a == rightStop.mColor.a) {
-      continue;
-    }
-
-    // Is the stop on the left 100% transparent? If so, have it adopt the color
-    // of the right stop
-    if (leftStop.mColor.a == 0) {
-      aStops[x - 1].mColor = TransparentColor(rightStop.mColor);
-      continue;
-    }
-
-    // Is the stop on the right completely transparent?
-    // If so, duplicate it and assign it the color on the left.
-    if (rightStop.mColor.a == 0) {
-      ColorStop newStop = rightStop;
-      newStop.mColor = TransparentColor(leftStop.mColor);
-      aStops.InsertElementAt(x, newStop);
-      x++;
-      continue;
-    }
-
-    // Now handle cases where one or both of the stops are partially transparent.
-    if (leftStop.mColor.a != 1.0f || rightStop.mColor.a != 1.0f) {
-      Color premulLeftColor = Premultiply(leftStop.mColor);
-      Color premulRightColor = Premultiply(rightStop.mColor);
-      // Calculate how many extra steps. We do a step per 10% transparency.
-      size_t stepCount = NSToIntFloor(fabsf(leftStop.mColor.a - rightStop.mColor.a) / kAlphaIncrementPerGradientStep);
-      for (size_t y = 1; y < stepCount; y++) {
-        float frac = static_cast<float>(y) / stepCount;
-        ColorStop newStop(Interpolate(leftStop.mPosition, rightStop.mPosition, frac), false,
-                          Unpremultiply(InterpolateColor(premulLeftColor, premulRightColor, frac)));
-        aStops.InsertElementAt(x, newStop);
-        x++;
-      }
-    }
-  }
-}
-
-static ColorStop
-InterpolateColorStop(const ColorStop& aFirst, const ColorStop& aSecond,
-                     double aPosition, const Color& aDefault)
-{
-  MOZ_ASSERT(aFirst.mPosition <= aPosition);
-  MOZ_ASSERT(aPosition <= aSecond.mPosition);
-
-  double delta = aSecond.mPosition - aFirst.mPosition;
-
-  if (delta < 1e-6) {
-    return ColorStop(aPosition, false, aDefault);
-  }
-
-  return ColorStop(aPosition, false,
-                   Unpremultiply(InterpolateColor(Premultiply(aFirst.mColor),
-                                                  Premultiply(aSecond.mColor),
-                                                  (aPosition - aFirst.mPosition) / delta)));
-}
-
-// Clamp and extend the given ColorStop array in-place to fit exactly into the
-// range [0, 1].
-static void
-ClampColorStops(nsTArray<ColorStop>& aStops)
-{
-  MOZ_ASSERT(aStops.Length() > 0);
-
-  // If all stops are outside the range, then get rid of everything and replace
-  // with a single colour.
-  if (aStops.Length() < 2 || aStops[0].mPosition > 1 ||
-      aStops.LastElement().mPosition < 0) {
-    Color c = aStops[0].mPosition > 1 ? aStops[0].mColor : aStops.LastElement().mColor;
-    aStops.Clear();
-    aStops.AppendElement(ColorStop(0, false, c));
-    return;
-  }
-
-  // Create the 0 and 1 points if they fall in the range of |aStops|, and discard
-  // all stops outside the range [0, 1].
-  // XXX: If we have stops positioned at 0 or 1, we only keep the innermost of
-  // those stops. This should be fine for the current user(s) of this function.
-  for (size_t i = aStops.Length() - 1; i > 0; i--) {
-    if (aStops[i - 1].mPosition < 1 && aStops[i].mPosition >= 1) {
-      // Add a point to position 1.
-      aStops[i] = InterpolateColorStop(aStops[i - 1], aStops[i],
-                                       /* aPosition = */ 1,
-                                       aStops[i - 1].mColor);
-      // Remove all the elements whose position is greater than 1.
-      aStops.RemoveElementsAt(i + 1, aStops.Length() - (i + 1));
-    }
-    if (aStops[i - 1].mPosition <= 0 && aStops[i].mPosition > 0) {
-      // Add a point to position 0.
-      aStops[i - 1] = InterpolateColorStop(aStops[i - 1], aStops[i],
-                                           /* aPosition = */ 0,
-                                           aStops[i].mColor);
-      // Remove all of the preceding stops -- they are all negative.
-      aStops.RemoveElementsAt(0, i - 1);
-      break;
-    }
-  }
-
-  MOZ_ASSERT(aStops[0].mPosition >= -1e6);
-  MOZ_ASSERT(aStops.LastElement().mPosition - 1 <= 1e6);
-
-  // The end points won't exist yet if they don't fall in the original range of
-  // |aStops|. Create them if needed.
-  if (aStops[0].mPosition > 0) {
-    aStops.InsertElementAt(0, ColorStop(0, false, aStops[0].mColor));
-  }
-  if (aStops.LastElement().mPosition < 1) {
-    aStops.AppendElement(ColorStop(1, false, aStops.LastElement().mColor));
-  }
-}
-
-void
-nsCSSRendering::PaintGradient(nsPresContext* aPresContext,
-                              gfxContext& aContext,
-                              nsStyleGradient* aGradient,
-                              const nsRect& aDirtyRect,
-                              const nsRect& aDest,
-                              const nsRect& aFillArea,
-                              const nsSize& aRepeatSize,
-                              const CSSIntRect& aSrc,
-                              const nsSize& aIntrinsicSize,
-                              float aOpacity)
-{
-  PROFILER_LABEL("nsCSSRendering", "PaintGradient",
-    js::ProfileEntry::Category::GRAPHICS);
-
-  Telemetry::AutoTimer<Telemetry::GRADIENT_DURATION, Telemetry::Microsecond> gradientTimer;
-  if (aDest.IsEmpty() || aFillArea.IsEmpty()) {
-    return;
-  }
-
-  nscoord appUnitsPerDevPixel = aPresContext->AppUnitsPerDevPixel();
-  gfxSize srcSize = gfxSize(gfxFloat(aIntrinsicSize.width)/appUnitsPerDevPixel,
-                            gfxFloat(aIntrinsicSize.height)/appUnitsPerDevPixel);
-
-  bool cellContainsFill = aDest.Contains(aFillArea);
-
-  // Compute "gradient line" start and end relative to the intrinsic size of
-  // the gradient.
-  gfxPoint lineStart, lineEnd;
-  double radiusX = 0, radiusY = 0; // for radial gradients only
-  if (aGradient->mShape == NS_STYLE_GRADIENT_SHAPE_LINEAR) {
-    ComputeLinearGradientLine(aPresContext, aGradient, srcSize,
-                              &lineStart, &lineEnd);
-  } else {
-    ComputeRadialGradientLine(aPresContext, aGradient, srcSize,
-                              &lineStart, &lineEnd, &radiusX, &radiusY);
-  }
-  // Avoid sending Infs or Nans to downwind draw targets.
-  if (!lineStart.IsFinite() || !lineEnd.IsFinite()) {
-    lineStart = lineEnd = gfxPoint(0, 0);
-  }
-  gfxFloat lineLength = NS_hypot(lineEnd.x - lineStart.x,
-                                  lineEnd.y - lineStart.y);
-
-  MOZ_ASSERT(aGradient->mStops.Length() >= 2,
-             "The parser should reject gradients with less than two stops");
-
-  // Build color stop array and compute stop positions
-  nsTArray<ColorStop> stops;
-  // If there is a run of stops before stop i that did not have specified
-  // positions, then this is the index of the first stop in that run, otherwise
-  // it's -1.
-  int32_t firstUnsetPosition = -1;
-  for (uint32_t i = 0; i < aGradient->mStops.Length(); ++i) {
-    const nsStyleGradientStop& stop = aGradient->mStops[i];
-    double position;
-    switch (stop.mLocation.GetUnit()) {
-    case eStyleUnit_None:
-      if (i == 0) {
-        // First stop defaults to position 0.0
-        position = 0.0;
-      } else if (i == aGradient->mStops.Length() - 1) {
-        // Last stop defaults to position 1.0
-        position = 1.0;
-      } else {
-        // Other stops with no specified position get their position assigned
-        // later by interpolation, see below.
-        // Remeber where the run of stops with no specified position starts,
-        // if it starts here.
-        if (firstUnsetPosition < 0) {
-          firstUnsetPosition = i;
-        }
-        stops.AppendElement(ColorStop(0, stop.mIsInterpolationHint,
-                                      Color::FromABGR(stop.mColor)));
-        continue;
-      }
-      break;
-    case eStyleUnit_Percent:
-      position = stop.mLocation.GetPercentValue();
-      break;
-    case eStyleUnit_Coord:
-      position = lineLength < 1e-6 ? 0.0 :
-          stop.mLocation.GetCoordValue() / appUnitsPerDevPixel / lineLength;
-      break;
-    case eStyleUnit_Calc:
-      nsStyleCoord::Calc *calc;
-      calc = stop.mLocation.GetCalcValue();
-      position = calc->mPercent +
-          ((lineLength < 1e-6) ? 0.0 :
-          (NSAppUnitsToFloatPixels(calc->mLength, appUnitsPerDevPixel) / lineLength));
-      break;
-    default:
-      MOZ_ASSERT(false, "Unknown stop position type");
-    }
-
-    if (i > 0) {
-      // Prevent decreasing stop positions by advancing this position
-      // to the previous stop position, if necessary
-      position = std::max(position, stops[i - 1].mPosition);
-    }
-    stops.AppendElement(ColorStop(position, stop.mIsInterpolationHint,
-                                  Color::FromABGR(stop.mColor)));
-    if (firstUnsetPosition > 0) {
-      // Interpolate positions for all stops that didn't have a specified position
-      double p = stops[firstUnsetPosition - 1].mPosition;
-      double d = (stops[i].mPosition - p)/(i - firstUnsetPosition + 1);
-      for (uint32_t j = firstUnsetPosition; j < i; ++j) {
-        p += d;
-        stops[j].mPosition = p;
-      }
-      firstUnsetPosition = -1;
-    }
-  }
-
-  // If a non-repeating linear gradient is axis-aligned and there are no gaps
-  // between tiles, we can optimise away most of the work by converting to a
-  // repeating linear gradient and filling the whole destination rect at once.
-  bool forceRepeatToCoverTiles =
-    aGradient->mShape == NS_STYLE_GRADIENT_SHAPE_LINEAR &&
-    (lineStart.x == lineEnd.x) != (lineStart.y == lineEnd.y) &&
-    aRepeatSize.width == aDest.width && aRepeatSize.height == aDest.height &&
-    !aGradient->mRepeating && !aSrc.IsEmpty() && !cellContainsFill;
-
-  gfxMatrix matrix;
-  if (forceRepeatToCoverTiles) {
-    // Length of the source rectangle along the gradient axis.
-    double rectLen;
-    // The position of the start of the rectangle along the gradient.
-    double offset;
-
-    // The gradient line is "backwards". Flip the line upside down to make
-    // things easier, and then rotate the matrix to turn everything back the
-    // right way up.
-    if (lineStart.x > lineEnd.x || lineStart.y > lineEnd.y) {
-      std::swap(lineStart, lineEnd);
-      matrix.Scale(-1, -1);
-    }
-
-    // Fit the gradient line exactly into the source rect.
-    // aSrc is relative to aIntrinsincSize.
-    // srcRectDev will be relative to srcSize, so in the same coordinate space
-    // as lineStart / lineEnd.
-    gfxRect srcRectDev = nsLayoutUtils::RectToGfxRect(
-      CSSPixel::ToAppUnits(aSrc), appUnitsPerDevPixel);
-    if (lineStart.x != lineEnd.x) {
-      rectLen = srcRectDev.width;
-      offset = (srcRectDev.x - lineStart.x) / lineLength;
-      lineStart.x = srcRectDev.x;
-      lineEnd.x = srcRectDev.XMost();
-    } else {
-      rectLen = srcRectDev.height;
-      offset = (srcRectDev.y - lineStart.y) / lineLength;
-      lineStart.y = srcRectDev.y;
-      lineEnd.y = srcRectDev.YMost();
-    }
-
-    // Adjust gradient stop positions for the new gradient line.
-    double scale = lineLength / rectLen;
-    for (size_t i = 0; i < stops.Length(); i++) {
-      stops[i].mPosition = (stops[i].mPosition - offset) * fabs(scale);
-    }
-
-    // Clamp or extrapolate gradient stops to exactly [0, 1].
-    ClampColorStops(stops);
-
-    lineLength = rectLen;
-  }
-
-  // Eliminate negative-position stops if the gradient is radial.
-  double firstStop = stops[0].mPosition;
-  if (aGradient->mShape != NS_STYLE_GRADIENT_SHAPE_LINEAR && firstStop < 0.0) {
-    if (aGradient->mRepeating) {
-      // Choose an instance of the repeated pattern that gives us all positive
-      // stop-offsets.
-      double lastStop = stops[stops.Length() - 1].mPosition;
-      double stopDelta = lastStop - firstStop;
-      // If all the stops are in approximately the same place then logic below
-      // will kick in that makes us draw just the last stop color, so don't
-      // try to do anything in that case. We certainly need to avoid
-      // dividing by zero.
-      if (stopDelta >= 1e-6) {
-        double instanceCount = ceil(-firstStop/stopDelta);
-        // Advance stops by instanceCount multiples of the period of the
-        // repeating gradient.
-        double offset = instanceCount*stopDelta;
-        for (uint32_t i = 0; i < stops.Length(); i++) {
-          stops[i].mPosition += offset;
-        }
-      }
-    } else {
-      // Move negative-position stops to position 0.0. We may also need
-      // to set the color of the stop to the color the gradient should have
-      // at the center of the ellipse.
-      for (uint32_t i = 0; i < stops.Length(); i++) {
-        double pos = stops[i].mPosition;
-        if (pos < 0.0) {
-          stops[i].mPosition = 0.0;
-          // If this is the last stop, we don't need to adjust the color,
-          // it will fill the entire area.
-          if (i < stops.Length() - 1) {
-            double nextPos = stops[i + 1].mPosition;
-            // If nextPos is approximately equal to pos, then we don't
-            // need to adjust the color of this stop because it's
-            // not going to be displayed.
-            // If nextPos is negative, we don't need to adjust the color of
-            // this stop since it's not going to be displayed because
-            // nextPos will also be moved to 0.0.
-            if (nextPos >= 0.0 && nextPos - pos >= 1e-6) {
-              // Compute how far the new position 0.0 is along the interval
-              // between pos and nextPos.
-              // XXX Color interpolation (in cairo, too) should use the
-              // CSS 'color-interpolation' property!
-              float frac = float((0.0 - pos)/(nextPos - pos));
-              stops[i].mColor =
-                InterpolateColor(stops[i].mColor, stops[i + 1].mColor, frac);
-            }
-          }
-        }
-      }
-    }
-    firstStop = stops[0].mPosition;
-    MOZ_ASSERT(firstStop >= 0.0, "Failed to fix stop offsets");
-  }
-
-  if (aGradient->mShape != NS_STYLE_GRADIENT_SHAPE_LINEAR && !aGradient->mRepeating) {
-    // Direct2D can only handle a particular class of radial gradients because
-    // of the way the it specifies gradients. Setting firstStop to 0, when we
-    // can, will help us stay on the fast path. Currently we don't do this
-    // for repeating gradients but we could by adjusting the stop collection
-    // to start at 0
-    firstStop = 0;
-  }
-
-  double lastStop = stops[stops.Length() - 1].mPosition;
-  // Cairo gradients must have stop positions in the range [0, 1]. So,
-  // stop positions will be normalized below by subtracting firstStop and then
-  // multiplying by stopScale.
-  double stopScale;
-  double stopOrigin = firstStop;
-  double stopEnd = lastStop;
-  double stopDelta = lastStop - firstStop;
-  bool zeroRadius = aGradient->mShape != NS_STYLE_GRADIENT_SHAPE_LINEAR &&
-                      (radiusX < 1e-6 || radiusY < 1e-6);
-  if (stopDelta < 1e-6 || lineLength < 1e-6 || zeroRadius) {
-    // Stops are all at the same place. Map all stops to 0.0.
-    // For repeating radial gradients, or for any radial gradients with
-    // a zero radius, we need to fill with the last stop color, so just set
-    // both radii to 0.
-    if (aGradient->mRepeating || zeroRadius) {
-      radiusX = radiusY = 0.0;
-    }
-    stopDelta = 0.0;
-    lastStop = firstStop;
-  }
-
-  // Don't normalize non-repeating or degenerate gradients below 0..1
-  // This keeps the gradient line as large as the box and doesn't
-  // lets us avoiding having to get padding correct for stops
-  // at 0 and 1
-  if (!aGradient->mRepeating || stopDelta == 0.0) {
-    stopOrigin = std::min(stopOrigin, 0.0);
-    stopEnd = std::max(stopEnd, 1.0);
-  }
-  stopScale = 1.0/(stopEnd - stopOrigin);
-
-  // Create the gradient pattern.
-  RefPtr<gfxPattern> gradientPattern;
-  gfxPoint gradientStart;
-  gfxPoint gradientEnd;
-  if (aGradient->mShape == NS_STYLE_GRADIENT_SHAPE_LINEAR) {
-    // Compute the actual gradient line ends we need to pass to cairo after
-    // stops have been normalized.
-    gradientStart = lineStart + (lineEnd - lineStart)*stopOrigin;
-    gradientEnd = lineStart + (lineEnd - lineStart)*stopEnd;
-    gfxPoint gradientStopStart = lineStart + (lineEnd - lineStart)*firstStop;
-    gfxPoint gradientStopEnd = lineStart + (lineEnd - lineStart)*lastStop;
-
-    if (stopDelta == 0.0) {
-      // Stops are all at the same place. For repeating gradients, this will
-      // just paint the last stop color. We don't need to do anything.
-      // For non-repeating gradients, this should render as two colors, one
-      // on each "side" of the gradient line segment, which is a point. All
-      // our stops will be at 0.0; we just need to set the direction vector
-      // correctly.
-      gradientEnd = gradientStart + (lineEnd - lineStart);
-      gradientStopEnd = gradientStopStart + (lineEnd - lineStart);
-    }
-
-    gradientPattern = new gfxPattern(gradientStart.x, gradientStart.y,
-                                      gradientEnd.x, gradientEnd.y);
-  } else {
-    NS_ASSERTION(firstStop >= 0.0,
-                  "Negative stops not allowed for radial gradients");
-
-    // To form an ellipse, we'll stretch a circle vertically, if necessary.
-    // So our radii are based on radiusX.
-    double innerRadius = radiusX*stopOrigin;
-    double outerRadius = radiusX*stopEnd;
-    if (stopDelta == 0.0) {
-      // Stops are all at the same place.  See above (except we now have
-      // the inside vs. outside of an ellipse).
-      outerRadius = innerRadius + 1;
-    }
-    gradientPattern = new gfxPattern(lineStart.x, lineStart.y, innerRadius,
-                                     lineStart.x, lineStart.y, outerRadius);
-    if (radiusX != radiusY) {
-      // Stretch the circles into ellipses vertically by setting a transform
-      // in the pattern.
-      // Recall that this is the transform from user space to pattern space.
-      // So to stretch the ellipse by factor of P vertically, we scale
-      // user coordinates by 1/P.
-      matrix.Translate(lineStart);
-      matrix.Scale(1.0, radiusX/radiusY);
-      matrix.Translate(-lineStart);
-    }
-  }
-  // Use a pattern transform to take account of source and dest rects
-  matrix.Translate(gfxPoint(aPresContext->CSSPixelsToDevPixels(aSrc.x),
-                            aPresContext->CSSPixelsToDevPixels(aSrc.y)));
-  matrix.Scale(gfxFloat(aPresContext->CSSPixelsToAppUnits(aSrc.width))/aDest.width,
-               gfxFloat(aPresContext->CSSPixelsToAppUnits(aSrc.height))/aDest.height);
-  gradientPattern->SetMatrix(matrix);
-
-  if (stopDelta == 0.0) {
-    // Non-repeating gradient with all stops in same place -> just add
-    // first stop and last stop, both at position 0.
-    // Repeating gradient with all stops in the same place, or radial
-    // gradient with radius of 0 -> just paint the last stop color.
-    // We use firstStop offset to keep |stops| with same units (will later normalize to 0).
-    Color firstColor(stops[0].mColor);
-    Color lastColor(stops.LastElement().mColor);
-    stops.Clear();
-
-    if (!aGradient->mRepeating && !zeroRadius) {
-      stops.AppendElement(ColorStop(firstStop, false, firstColor));
-    }
-    stops.AppendElement(ColorStop(firstStop, false, lastColor));
-  }
-
-  ResolveMidpoints(stops);
-  ResolvePremultipliedAlpha(stops);
-
-  bool isRepeat = aGradient->mRepeating || forceRepeatToCoverTiles;
-
-  // Now set normalized color stops in pattern.
-  // Offscreen gradient surface cache (not a tile):
-  // On some backends (e.g. D2D), the GradientStops object holds an offscreen surface
-  // which is a lookup table used to evaluate the gradient. This surface can use
-  // much memory (ram and/or GPU ram) and can be expensive to create. So we cache it.
-  // The cache key correlates 1:1 with the arguments for CreateGradientStops (also the implied backend type)
-  // Note that GradientStop is a simple struct with a stop value (while GradientStops has the surface).
-  nsTArray<gfx::GradientStop> rawStops(stops.Length());
-  rawStops.SetLength(stops.Length());
-  for(uint32_t i = 0; i < stops.Length(); i++) {
-    rawStops[i].color = stops[i].mColor;
-    rawStops[i].color.a *= aOpacity;
-    rawStops[i].offset = stopScale * (stops[i].mPosition - stopOrigin);
-  }
-  RefPtr<mozilla::gfx::GradientStops> gs =
-    gfxGradientCache::GetOrCreateGradientStops(aContext.GetDrawTarget(),
-                                               rawStops,
-                                               isRepeat ? gfx::ExtendMode::REPEAT : gfx::ExtendMode::CLAMP);
-  gradientPattern->SetColorStops(gs);
-
-  // Paint gradient tiles. This isn't terribly efficient, but doing it this
-  // way is simple and sure to get pixel-snapping right. We could speed things
-  // up by drawing tiles into temporary surfaces and copying those to the
-  // destination, but after pixel-snapping tiles may not all be the same size.
-  nsRect dirty;
-  if (!dirty.IntersectRect(aDirtyRect, aFillArea))
-    return;
-
-  gfxRect areaToFill =
-    nsLayoutUtils::RectToGfxRect(aFillArea, appUnitsPerDevPixel);
-  gfxRect dirtyAreaToFill = nsLayoutUtils::RectToGfxRect(dirty, appUnitsPerDevPixel);
-  dirtyAreaToFill.RoundOut();
-
-  gfxMatrix ctm = aContext.CurrentMatrix();
-  bool isCTMPreservingAxisAlignedRectangles = ctm.PreservesAxisAlignedRectangles();
-
-  // xStart/yStart are the top-left corner of the top-left tile.
-  nscoord xStart = FindTileStart(dirty.x, aDest.x, aRepeatSize.width);
-  nscoord yStart = FindTileStart(dirty.y, aDest.y, aRepeatSize.height);
-  nscoord xEnd = forceRepeatToCoverTiles ? xStart + aDest.width : dirty.XMost();
-  nscoord yEnd = forceRepeatToCoverTiles ? yStart + aDest.height : dirty.YMost();
-
-  // x and y are the top-left corner of the tile to draw
-  for (nscoord y = yStart; y < yEnd; y += aRepeatSize.height) {
-    for (nscoord x = xStart; x < xEnd; x += aRepeatSize.width) {
-      // The coordinates of the tile
-      gfxRect tileRect = nsLayoutUtils::RectToGfxRect(
-                      nsRect(x, y, aDest.width, aDest.height),
-                      appUnitsPerDevPixel);
-      // The actual area to fill with this tile is the intersection of this
-      // tile with the overall area we're supposed to be filling
-      gfxRect fillRect =
-        forceRepeatToCoverTiles ? areaToFill : tileRect.Intersect(areaToFill);
-      // Try snapping the fill rect. Snap its top-left and bottom-right
-      // independently to preserve the orientation.
-      gfxPoint snappedFillRectTopLeft = fillRect.TopLeft();
-      gfxPoint snappedFillRectTopRight = fillRect.TopRight();
-      gfxPoint snappedFillRectBottomRight = fillRect.BottomRight();
-      // Snap three points instead of just two to ensure we choose the
-      // correct orientation if there's a reflection.
-      if (isCTMPreservingAxisAlignedRectangles &&
-          aContext.UserToDevicePixelSnapped(snappedFillRectTopLeft, true) &&
-          aContext.UserToDevicePixelSnapped(snappedFillRectBottomRight, true) &&
-          aContext.UserToDevicePixelSnapped(snappedFillRectTopRight, true)) {
-        if (snappedFillRectTopLeft.x == snappedFillRectBottomRight.x ||
-            snappedFillRectTopLeft.y == snappedFillRectBottomRight.y) {
-          // Nothing to draw; avoid scaling by zero and other weirdness that
-          // could put the context in an error state.
-          continue;
-        }
-        // Set the context's transform to the transform that maps fillRect to
-        // snappedFillRect. The part of the gradient that was going to
-        // exactly fill fillRect will fill snappedFillRect instead.
-        gfxMatrix transform = gfxUtils::TransformRectToRect(fillRect,
-            snappedFillRectTopLeft, snappedFillRectTopRight,
-            snappedFillRectBottomRight);
-        aContext.SetMatrix(transform);
-      }
-      aContext.NewPath();
-      aContext.Rectangle(fillRect);
-
-      gfxRect dirtyFillRect = fillRect.Intersect(dirtyAreaToFill);
-      gfxRect fillRectRelativeToTile = dirtyFillRect - tileRect.TopLeft();
-      Color edgeColor;
-      if (aGradient->mShape == NS_STYLE_GRADIENT_SHAPE_LINEAR && !isRepeat &&
-          RectIsBeyondLinearGradientEdge(fillRectRelativeToTile, matrix, stops,
-                                         gradientStart, gradientEnd, &edgeColor)) {
-        edgeColor.a *= aOpacity;
-        aContext.SetColor(edgeColor);
-      } else {
-        aContext.SetMatrix(
-          aContext.CurrentMatrix().Copy().Translate(tileRect.TopLeft()));
-        aContext.SetPattern(gradientPattern);
-      }
-      aContext.Fill();
-      aContext.SetMatrix(ctm);
-    }
-  }
-}
-
 static CompositionOp
 DetermineCompositionOp(const nsCSSRendering::PaintBGParams& aParams,
                        const nsStyleImageLayers& aLayers,
                        uint32_t aLayerIndex)
 {
   if (aParams.layer >= 0) {
     // When drawing a single layer, use the specified composition op.
     return aParams.compositionOp;
@@ -3364,16 +2468,17 @@ DetermineCompositionOp(const nsCSSRender
     return nsCSSRendering::GetGFXCompositeMode(layer.mComposite);
   }
 
   return nsCSSRendering::GetGFXBlendMode(layer.mBlendMode);
 }
 
 DrawResult
 nsCSSRendering::PaintStyleImageLayerWithSC(const PaintBGParams& aParams,
+                                           nsRenderingContext& aRenderingCtx,
                                            nsStyleContext *aBackgroundSC,
                                            const nsStyleBorder& aBorder)
 {
   NS_PRECONDITION(aParams.frame,
                   "Frame is expected to be provided to PaintBackground");
 
   // If we're drawing all layers, aCompositonOp is ignored, so make sure that
   // it was left at its default value.
@@ -3389,17 +2494,17 @@ nsCSSRendering::PaintStyleImageLayerWith
     if (theme && theme->ThemeSupportsWidget(&aParams.presCtx,
                                             aParams.frame,
                                             displayData->UsedAppearance())) {
       nsRect drawing(aParams.borderArea);
       theme->GetWidgetOverflow(aParams.presCtx.DeviceContext(),
                                aParams.frame, displayData->UsedAppearance(),
                                &drawing);
       drawing.IntersectRect(drawing, aParams.dirtyRect);
-      theme->DrawWidgetBackground(&aParams.renderingCtx, aParams.frame,
+      theme->DrawWidgetBackground(&aRenderingCtx, aParams.frame,
                                   displayData->UsedAppearance(), aParams.borderArea,
                                   drawing);
       return DrawResult::SUCCESS;
     }
   }
 
   // For canvas frames (in the CSS sense) we draw the background color using
   // a solid color item that gets added in nsLayoutUtils::PaintFrame,
@@ -3438,17 +2543,17 @@ nsCSSRendering::PaintStyleImageLayerWith
     return DrawResult::SUCCESS;
 
   // The 'bgClipArea' (used only by the image tiling logic, far below)
   // is the caller-provided aParams.bgClipRect if any, or else the area
   // determined by the value of 'background-clip' in
   // SetupCurrentBackgroundClip.  (Arguably it should be the
   // intersection, but that breaks the table painter -- in particular,
   // taking the intersection breaks reftests/bugs/403249-1[ab].)
-  gfxContext* ctx = aParams.renderingCtx.ThebesContext();
+  gfxContext* ctx = aRenderingCtx.ThebesContext();
   nscoord appUnitsPerPixel = aParams.presCtx.AppUnitsPerDevPixel();
   ImageLayerClipState clipState;
   if (aParams.bgClipRect) {
     clipState.mBGClipArea = *aParams.bgClipRect;
     clipState.mCustomClip = true;
     clipState.mHasRoundedCorners = false;
     SetupDirtyRects(clipState.mBGClipArea, aParams.dirtyRect, appUnitsPerPixel,
                     &clipState.mDirtyRectInAppUnits,
@@ -3581,31 +2686,86 @@ nsCSSRendering::PaintStyleImageLayerWith
         NS_ASSERTION(ctx->CurrentOp() == CompositionOp::OP_OVER,
                      "It is assumed the initial op is OP_OVER, when it is "
                      "restored later");
         ctx->SetOp(co);
       }
 
       result &=
         state.mImageRenderer.DrawLayer(&aParams.presCtx,
-                                       aParams.renderingCtx,
+                                       aRenderingCtx,
                                        state.mDestArea, state.mFillArea,
                                        state.mAnchor + paintBorderArea.TopLeft(),
                                        clipState.mDirtyRectInAppUnits,
                                        state.mRepeatSize, aParams.opacity);
 
       if (co != CompositionOp::OP_OVER) {
         ctx->SetOp(CompositionOp::OP_OVER);
       }
     }
   }
 
   return result;
 }
 
+void
+nsCSSRendering::BuildWebRenderDisplayItemsForStyleImageLayerWithSC(const PaintBGParams& aParams,
+                                                                   mozilla::wr::DisplayListBuilder& aBuilder,
+                                                                   mozilla::layers::WebRenderDisplayItemLayer* aLayer,
+                                                                   nsStyleContext *aBackgroundSC,
+                                                                   const nsStyleBorder& aBorder)
+{
+  MOZ_ASSERT(CanBuildWebRenderDisplayItemsForStyleImageLayer(aParams.presCtx,
+                                                             aParams.frame,
+                                                             aBackgroundSC->StyleBackground(),
+                                                             aParams.layer));
+
+  MOZ_ASSERT(!(aParams.paintFlags & PAINTBG_MASK_IMAGE));
+
+  nscoord appUnitsPerPixel = aParams.presCtx.AppUnitsPerDevPixel();
+  ImageLayerClipState clipState;
+
+  clipState.mBGClipArea = *aParams.bgClipRect;
+  clipState.mCustomClip = true;
+  clipState.mHasRoundedCorners = false;
+  SetupDirtyRects(clipState.mBGClipArea, aParams.dirtyRect, appUnitsPerPixel,
+                  &clipState.mDirtyRectInAppUnits,
+                  &clipState.mDirtyRectInDevPx);
+
+  // Compute the outermost boundary of the area that might be painted.
+  // Same coordinate space as aParams.borderArea & aParams.bgClipRect.
+  Sides skipSides = aParams.frame->GetSkipSides();
+  nsRect paintBorderArea =
+    ::BoxDecorationRectForBackground(aParams.frame, aParams.borderArea,
+                                     skipSides, &aBorder);
+
+  const nsStyleImageLayers& layers = aBackgroundSC->StyleBackground()->mImage;
+  const nsStyleImageLayers::Layer& layer = layers.mLayers[aParams.layer];
+
+  // Skip the following layer painting code if we found the dirty region is
+  // empty or the current layer is not selected for drawing.
+  if (clipState.mDirtyRectInDevPx.IsEmpty()) {
+    return;
+  }
+
+  nsBackgroundLayerState state =
+    PrepareImageLayer(&aParams.presCtx, aParams.frame,
+                      aParams.paintFlags, paintBorderArea,
+                      clipState.mBGClipArea, layer, nullptr);
+
+  if (!state.mFillArea.IsEmpty()) {
+    state.mImageRenderer.BuildWebRenderDisplayItemsForLayer(&aParams.presCtx,
+                                   aBuilder, aLayer,
+                                   state.mDestArea, state.mFillArea,
+                                   state.mAnchor + paintBorderArea.TopLeft(),
+                                   clipState.mDirtyRectInAppUnits,
+                                   state.mRepeatSize, aParams.opacity);
+  }
+}
+
 nsRect
 nsCSSRendering::ComputeImageLayerPositioningArea(nsPresContext* aPresContext,
                                                  nsIFrame* aForFrame,
                                                  const nsRect& aBorderArea,
                                                  const nsStyleImageLayers::Layer& aLayer,
                                                  nsIFrame** aAttachedToFrame,
                                                  bool* aOutIsTransformedFixed)
 {
--- a/layout/painting/nsCSSRendering.h
+++ b/layout/painting/nsCSSRendering.h
@@ -28,18 +28,23 @@ namespace mozilla {
 
 namespace gfx {
 struct Color;
 class DrawTarget;
 } // namespace gfx
 
 namespace layers {
 class ImageContainer;
+class WebRenderDisplayItemLayer;
 } // namespace layers
 
+namespace wr {
+class DisplayListBuilder;
+} // namespace wr
+
 enum class PaintBorderFlags : uint8_t
 {
   SYNC_DECODE_IMAGES = 1 << 0
 };
 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(PaintBorderFlags)
 
 } // namespace mozilla
 
@@ -397,86 +402,96 @@ struct nsCSSRendering {
      * When this flag is passed, painting will read properties of mask-image
      * style, instead of background-image.
      */
     PAINTBG_MASK_IMAGE = 0x08
   };
 
   struct PaintBGParams {
     nsPresContext& presCtx;
-    nsRenderingContext& renderingCtx;
     nsRect dirtyRect;
     nsRect borderArea;
     nsIFrame* frame;
     uint32_t paintFlags;
     nsRect* bgClipRect = nullptr;
     int32_t layer;                  // -1 means painting all layers; other
                                     // value means painting one specific
                                     // layer only.
     CompositionOp compositionOp;
     float opacity;
 
     static PaintBGParams ForAllLayers(nsPresContext& aPresCtx,
-                                      nsRenderingContext& aRenderingCtx,
                                       const nsRect& aDirtyRect,
                                       const nsRect& aBorderArea,
                                       nsIFrame *aFrame,
                                       uint32_t aPaintFlags,
                                       float aOpacity = 1.0);
     static PaintBGParams ForSingleLayer(nsPresContext& aPresCtx,
-                                        nsRenderingContext& aRenderingCtx,
                                         const nsRect& aDirtyRect,
                                         const nsRect& aBorderArea,
                                         nsIFrame *aFrame,
                                         uint32_t aPaintFlags,
                                         int32_t aLayer,
                                         CompositionOp aCompositionOp  = CompositionOp::OP_OVER,
                                         float aOpacity = 1.0);
 
   private:
     PaintBGParams(nsPresContext& aPresCtx,
-                  nsRenderingContext& aRenderingCtx,
                   const nsRect& aDirtyRect,
                   const nsRect& aBorderArea,
                   nsIFrame* aFrame,
                   uint32_t aPaintFlags,
                   int32_t aLayer,
                   CompositionOp aCompositionOp,
                   float aOpacity)
      : presCtx(aPresCtx),
-       renderingCtx(aRenderingCtx),
        dirtyRect(aDirtyRect),
        borderArea(aBorderArea),
        frame(aFrame),
        paintFlags(aPaintFlags),
        layer(aLayer),
        compositionOp(aCompositionOp),
        opacity(aOpacity) {}
   };
 
-  static DrawResult PaintStyleImageLayer(const PaintBGParams& aParams);
-
+  static DrawResult PaintStyleImageLayer(const PaintBGParams& aParams,
+                                         nsRenderingContext& aRenderingCtx);
 
   /**
    * Same as |PaintStyleImageLayer|, except using the provided style structs.
    * This short-circuits the code that ensures that the root element's
    * {background|mask} is drawn on the canvas.
    * The aLayer parameter allows you to paint a single layer of the
    * {background|mask}.
    * The default value for aLayer, -1, means that all layers will be painted.
    * The background color will only be painted if the back-most layer is also
    * being painted and (aParams.paintFlags & PAINTBG_MASK_IMAGE) is false.
    * aCompositionOp is only respected if a single layer is specified (aLayer != -1).
    * If all layers are painted, the image layer's blend mode (or the mask
    * layer's composition mode) will be used.
    */
   static DrawResult PaintStyleImageLayerWithSC(const PaintBGParams& aParams,
+                                               nsRenderingContext& aRenderingCtx,
                                                nsStyleContext *mBackgroundSC,
                                                const nsStyleBorder& aBorder);
 
+  static bool CanBuildWebRenderDisplayItemsForStyleImageLayer(nsPresContext& aPresCtx,
+                                                              nsIFrame *aFrame,
+                                                              const nsStyleBackground* aBackgroundStyle,
+                                                              int32_t aLayer);
+  static void BuildWebRenderDisplayItemsForStyleImageLayer(const PaintBGParams& aParams,
+                                                           mozilla::wr::DisplayListBuilder& aBuilder,
+                                                           mozilla::layers::WebRenderDisplayItemLayer* aLayer);
+
+  static void BuildWebRenderDisplayItemsForStyleImageLayerWithSC(const PaintBGParams& aParams,
+                                                                 mozilla::wr::DisplayListBuilder& aBuilder,
+                                                                 mozilla::layers::WebRenderDisplayItemLayer* aLayer,
+                                                                 nsStyleContext *mBackgroundSC,
+                                                                 const nsStyleBorder& aBorder);
+
   /**
    * Returns the rectangle covered by the given background layer image, taking
    * into account background positioning, sizing, and repetition, but not
    * clipping.
    */
   static nsRect GetBackgroundLayerRect(nsPresContext* aPresContext,
                                        nsIFrame* aForFrame,
                                        const nsRect& aBorderArea,
copy from layout/painting/nsCSSRendering.cpp
copy to layout/painting/nsCSSRenderingGradients.cpp
--- a/layout/painting/nsCSSRendering.cpp
+++ b/layout/painting/nsCSSRenderingGradients.cpp
@@ -1,2409 +1,46 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 // vim:cindent:ts=2:et:sw=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/. */
 
 /* utility functions for drawing borders and backgrounds */
 
-#include <ctime>
+#include "nsCSSRenderingGradients.h"
 
 #include "gfx2DGlue.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/Helpers.h"
-#include "mozilla/gfx/PathHelpers.h"
-#include "mozilla/HashFunctions.h"
 #include "mozilla/MathAlgorithms.h"
 
-#include "BorderConsts.h"
 #include "nsStyleConsts.h"
 #include "nsPresContext.h"
-#include "nsIFrame.h"
-#include "nsIFrameInlines.h"
 #include "nsPoint.h"
 #include "nsRect.h"
-#include "nsIPresShell.h"
-#include "nsFrameManager.h"
 #include "nsStyleContext.h"
-#include "nsGkAtoms.h"
-#include "nsCSSAnonBoxes.h"
-#include "nsIContent.h"
-#include "nsIDocumentInlines.h"
-#include "nsIScrollableFrame.h"
-#include "imgIRequest.h"
-#include "imgIContainer.h"
-#include "ImageOps.h"
-#include "nsCSSRendering.h"
 #include "nsCSSColorUtils.h"
-#include "nsITheme.h"
-#include "nsThemeConstants.h"
-#include "nsLayoutUtils.h"
-#include "nsBlockFrame.h"
 #include "gfxContext.h"
 #include "nsRenderingContext.h"
 #include "nsStyleStructInlines.h"
-#include "nsCSSFrameConstructor.h"
 #include "nsCSSProps.h"
-#include "nsContentUtils.h"
-#include "nsSVGEffects.h"
-#include "nsSVGIntegrationUtils.h"
-#include "gfxDrawable.h"
-#include "GeckoProfiler.h"
-#include "nsCSSRenderingBorders.h"
-#include "mozilla/css/ImageLoader.h"
-#include "ImageContainer.h"
 #include "mozilla/Telemetry.h"
 #include "gfxUtils.h"
 #include "gfxGradientCache.h"
-#include "nsInlineFrame.h"
-#include "nsRubyTextContainerFrame.h"
-#include <algorithm>
-#include "SVGImageContext.h"
+
+#include "mozilla/layers/WebRenderLayerManager.h"
+#include "mozilla/webrender/WebRenderTypes.h"
+#include "mozilla/webrender/WebRenderAPI.h"
+#include "Units.h"
 
 using namespace mozilla;
-using namespace mozilla::css;
 using namespace mozilla::gfx;
-using namespace mozilla::image;
-using mozilla::CSSSizeOrRatio;
-
-static int gFrameTreeLockCount = 0;
-
-// To avoid storing this data on nsInlineFrame (bloat) and to avoid
-// recalculating this for each frame in a continuation (perf), hold
-// a cache of various coordinate information that we need in order
-// to paint inline backgrounds.
-struct InlineBackgroundData
-{
-  InlineBackgroundData()
-      : mFrame(nullptr), mLineContainer(nullptr)
-  {
-  }
-
-  ~InlineBackgroundData()
-  {
-  }
-
-  void Reset()
-  {
-    mBoundingBox.SetRect(0,0,0,0);
-    mContinuationPoint = mLineContinuationPoint = mUnbrokenMeasure = 0;
-    mFrame = mLineContainer = nullptr;
-    mPIStartBorderData.Reset();
-  }
-
-  /**
-   * Return a continuous rect for (an inline) aFrame relative to the
-   * continuation that draws the left-most part of the background.
-   * This is used when painting backgrounds.
-   */
-  nsRect GetContinuousRect(nsIFrame* aFrame)
-  {
-    MOZ_ASSERT(static_cast<nsInlineFrame*>(do_QueryFrame(aFrame)));
-
-    SetFrame(aFrame);
-
-    nscoord pos; // an x coordinate if writing-mode is horizontal;
-                 // y coordinate if vertical
-    if (mBidiEnabled) {
-      pos = mLineContinuationPoint;
-
-      // Scan continuations on the same line as aFrame and accumulate the widths
-      // of frames that are to the left (if this is an LTR block) or right
-      // (if it's RTL) of the current one.
-      bool isRtlBlock = (mLineContainer->StyleVisibility()->mDirection ==
-                           NS_STYLE_DIRECTION_RTL);
-      nscoord curOffset = mVertical ? aFrame->GetOffsetTo(mLineContainer).y
-                                    : aFrame->GetOffsetTo(mLineContainer).x;
-
-      // If the continuation is fluid we know inlineFrame is not on the same line.
-      // If it's not fluid, we need to test further to be sure.
-      nsIFrame* inlineFrame = aFrame->GetPrevContinuation();
-      while (inlineFrame && !inlineFrame->GetNextInFlow() &&
-             AreOnSameLine(aFrame, inlineFrame)) {
-        nscoord frameOffset = mVertical
-          ? inlineFrame->GetOffsetTo(mLineContainer).y
-          : inlineFrame->GetOffsetTo(mLineContainer).x;
-        if (isRtlBlock == (frameOffset >= curOffset)) {
-          pos += mVertical
-               ? inlineFrame->GetSize().height
-               : inlineFrame->GetSize().width;
-        }
-        inlineFrame = inlineFrame->GetPrevContinuation();
-      }
-
-      inlineFrame = aFrame->GetNextContinuation();
-      while (inlineFrame && !inlineFrame->GetPrevInFlow() &&
-             AreOnSameLine(aFrame, inlineFrame)) {
-        nscoord frameOffset = mVertical
-          ? inlineFrame->GetOffsetTo(mLineContainer).y
-          : inlineFrame->GetOffsetTo(mLineContainer).x;
-        if (isRtlBlock == (frameOffset >= curOffset)) {
-          pos += mVertical
-                 ? inlineFrame->GetSize().height
-                 : inlineFrame->GetSize().width;
-        }
-        inlineFrame = inlineFrame->GetNextContinuation();
-      }
-      if (isRtlBlock) {
-        // aFrame itself is also to the right of its left edge, so add its width.
-        pos += mVertical ? aFrame->GetSize().height : aFrame->GetSize().width;
-        // pos is now the distance from the left [top] edge of aFrame to the right [bottom] edge
-        // of the unbroken content. Change it to indicate the distance from the
-        // left [top] edge of the unbroken content to the left [top] edge of aFrame.
-        pos = mUnbrokenMeasure - pos;
-      }
-    } else {
-      pos = mContinuationPoint;
-    }
-
-    // Assume background-origin: border and return a rect with offsets
-    // relative to (0,0).  If we have a different background-origin,
-    // then our rect should be deflated appropriately by our caller.
-    return mVertical
-      ? nsRect(0, -pos, mFrame->GetSize().width, mUnbrokenMeasure)
-      : nsRect(-pos, 0, mUnbrokenMeasure, mFrame->GetSize().height);
-  }
-
-  /**
-   * Return a continuous rect for (an inline) aFrame relative to the
-   * continuation that should draw the left[top]-border.  This is used when painting
-   * borders and clipping backgrounds.  This may NOT be the same continuous rect
-   * as for drawing backgrounds; the continuation with the left[top]-border might be
-   * somewhere in the middle of that rect (e.g. BIDI), in those cases we need
-   * the reverse background order starting at the left[top]-border continuation.
-   */
-  nsRect GetBorderContinuousRect(nsIFrame* aFrame, nsRect aBorderArea)
-  {
-    // Calling GetContinuousRect(aFrame) here may lead to Reset/Init which
-    // resets our mPIStartBorderData so we save it ...
-    PhysicalInlineStartBorderData saved(mPIStartBorderData);
-    nsRect joinedBorderArea = GetContinuousRect(aFrame);
-    if (!saved.mIsValid || saved.mFrame != mPIStartBorderData.mFrame) {
-      if (aFrame == mPIStartBorderData.mFrame) {
-        if (mVertical) {
-          mPIStartBorderData.SetCoord(joinedBorderArea.y);
-        } else {
-          mPIStartBorderData.SetCoord(joinedBorderArea.x);
-        }
-      } else if (mPIStartBorderData.mFrame) {
-        if (mVertical) {
-          mPIStartBorderData.SetCoord(GetContinuousRect(mPIStartBorderData.mFrame).y);
-        } else {
-          mPIStartBorderData.SetCoord(GetContinuousRect(mPIStartBorderData.mFrame).x);
-        }
-      }
-    } else {
-      // ... and restore it when possible.
-      mPIStartBorderData.mCoord = saved.mCoord;
-    }
-    if (mVertical) {
-      if (joinedBorderArea.y > mPIStartBorderData.mCoord) {
-        joinedBorderArea.y =
-          -(mUnbrokenMeasure + joinedBorderArea.y - aBorderArea.height);
-      } else {
-        joinedBorderArea.y -= mPIStartBorderData.mCoord;
-      }
-    } else {
-      if (joinedBorderArea.x > mPIStartBorderData.mCoord) {
-        joinedBorderArea.x =
-          -(mUnbrokenMeasure + joinedBorderArea.x - aBorderArea.width);
-      } else {
-        joinedBorderArea.x -= mPIStartBorderData.mCoord;
-      }
-    }
-    return joinedBorderArea;
-  }
-
-  nsRect GetBoundingRect(nsIFrame* aFrame)
-  {
-    SetFrame(aFrame);
-
-    // Move the offsets relative to (0,0) which puts the bounding box into
-    // our coordinate system rather than our parent's.  We do this by
-    // moving it the back distance from us to the bounding box.
-    // This also assumes background-origin: border, so our caller will
-    // need to deflate us if needed.
-    nsRect boundingBox(mBoundingBox);
-    nsPoint point = mFrame->GetPosition();
-    boundingBox.MoveBy(-point.x, -point.y);
-
-    return boundingBox;
-  }
-
-protected:
-  // This is a coordinate on the inline axis, but is not a true logical inline-
-  // coord because it is always measured from left to right (if horizontal) or
-  // from top to bottom (if vertical), ignoring any bidi RTL directionality.
-  // We'll call this "physical inline start", or PIStart for short.
-  struct PhysicalInlineStartBorderData {
-    nsIFrame* mFrame;   // the continuation that may have a left-border
-    nscoord   mCoord;   // cached GetContinuousRect(mFrame).x or .y
-    bool      mIsValid; // true if mCoord is valid
-    void Reset() { mFrame = nullptr; mIsValid = false; }
-    void SetCoord(nscoord aCoord) { mCoord = aCoord; mIsValid = true; }
-  };
-
-  nsIFrame*      mFrame;
-  nsIFrame*      mLineContainer;
-  nsRect         mBoundingBox;
-  nscoord        mContinuationPoint;
-  nscoord        mUnbrokenMeasure;
-  nscoord        mLineContinuationPoint;
-  PhysicalInlineStartBorderData mPIStartBorderData;
-  bool           mBidiEnabled;
-  bool           mVertical;
-
-  void SetFrame(nsIFrame* aFrame)
-  {
-    NS_PRECONDITION(aFrame, "Need a frame");
-    NS_ASSERTION(gFrameTreeLockCount > 0,
-                 "Can't call this when frame tree is not locked");
-
-    if (aFrame == mFrame) {
-      return;
-    }
-
-    nsIFrame *prevContinuation = GetPrevContinuation(aFrame);
-
-    if (!prevContinuation || mFrame != prevContinuation) {
-      // Ok, we've got the wrong frame.  We have to start from scratch.
-      Reset();
-      Init(aFrame);
-      return;
-    }
-
-    // Get our last frame's size and add its width to our continuation
-    // point before we cache the new frame.
-    mContinuationPoint += mVertical ? mFrame->GetSize().height
-                                    : mFrame->GetSize().width;
-
-    // If this a new line, update mLineContinuationPoint.
-    if (mBidiEnabled &&
-        (aFrame->GetPrevInFlow() || !AreOnSameLine(mFrame, aFrame))) {
-       mLineContinuationPoint = mContinuationPoint;
-    }
-
-    mFrame = aFrame;
-  }
-
-  nsIFrame* GetPrevContinuation(nsIFrame* aFrame)
-  {
-    nsIFrame* prevCont = aFrame->GetPrevContinuation();
-    if (!prevCont &&
-        (aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) {
-      nsIFrame* block =
-        aFrame->Properties().Get(nsIFrame::IBSplitPrevSibling());
-      if (block) {
-        // The {ib} properties are only stored on first continuations
-        NS_ASSERTION(!block->GetPrevContinuation(),
-                     "Incorrect value for IBSplitPrevSibling");
-        prevCont =
-          block->Properties().Get(nsIFrame::IBSplitPrevSibling());
-        NS_ASSERTION(prevCont, "How did that happen?");
-      }
-    }
-    return prevCont;
-  }
-
-  nsIFrame* GetNextContinuation(nsIFrame* aFrame)
-  {
-    nsIFrame* nextCont = aFrame->GetNextContinuation();
-    if (!nextCont &&
-        (aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) {
-      // The {ib} properties are only stored on first continuations
-      aFrame = aFrame->FirstContinuation();
-      nsIFrame* block = aFrame->Properties().Get(nsIFrame::IBSplitSibling());
-      if (block) {
-        nextCont = block->Properties().Get(nsIFrame::IBSplitSibling());
-        NS_ASSERTION(nextCont, "How did that happen?");
-      }
-    }
-    return nextCont;
-  }
-
-  void Init(nsIFrame* aFrame)
-  {
-    mPIStartBorderData.Reset();
-    mBidiEnabled = aFrame->PresContext()->BidiEnabled();
-    if (mBidiEnabled) {
-      // Find the line container frame
-      mLineContainer = aFrame;
-      while (mLineContainer &&
-             mLineContainer->IsFrameOfType(nsIFrame::eLineParticipant)) {
-        mLineContainer = mLineContainer->GetParent();
-      }
-
-      MOZ_ASSERT(mLineContainer, "Cannot find line containing frame.");
-      MOZ_ASSERT(mLineContainer != aFrame, "line container frame "
-                 "should be an ancestor of the target frame.");
-    }
-
-    mVertical = aFrame->GetWritingMode().IsVertical();
-
-    // Start with the previous flow frame as our continuation point
-    // is the total of the widths of the previous frames.
-    nsIFrame* inlineFrame = GetPrevContinuation(aFrame);
-    while (inlineFrame) {
-      if (!mPIStartBorderData.mFrame &&
-          !(mVertical ? inlineFrame->GetSkipSides().Top()
-                      : inlineFrame->GetSkipSides().Left())) {
-        mPIStartBorderData.mFrame = inlineFrame;
-      }
-      nsRect rect = inlineFrame->GetRect();
-      mContinuationPoint += mVertical ? rect.height : rect.width;
-      if (mBidiEnabled && !AreOnSameLine(aFrame, inlineFrame)) {
-        mLineContinuationPoint += mVertical ? rect.height : rect.width;
-      }
-      mUnbrokenMeasure += mVertical ? rect.height : rect.width;
-      mBoundingBox.UnionRect(mBoundingBox, rect);
-      inlineFrame = GetPrevContinuation(inlineFrame);
-    }
-
-    // Next add this frame and subsequent frames to the bounding box and
-    // unbroken width.
-    inlineFrame = aFrame;
-    while (inlineFrame) {
-      if (!mPIStartBorderData.mFrame &&
-          !(mVertical ? inlineFrame->GetSkipSides().Top()
-                      : inlineFrame->GetSkipSides().Left())) {
-        mPIStartBorderData.mFrame = inlineFrame;
-      }
-      nsRect rect = inlineFrame->GetRect();
-      mUnbrokenMeasure += mVertical ? rect.height : rect.width;
-      mBoundingBox.UnionRect(mBoundingBox, rect);
-      inlineFrame = GetNextContinuation(inlineFrame);
-    }
-
-    mFrame = aFrame;
-  }
-
-  bool AreOnSameLine(nsIFrame* aFrame1, nsIFrame* aFrame2) {
-    if (nsBlockFrame* blockFrame = do_QueryFrame(mLineContainer)) {
-      bool isValid1, isValid2;
-      nsBlockInFlowLineIterator it1(blockFrame, aFrame1, &isValid1);
-      nsBlockInFlowLineIterator it2(blockFrame, aFrame2, &isValid2);
-      return isValid1 && isValid2 &&
-        // Make sure aFrame1 and aFrame2 are in the same continuation of
-        // blockFrame.
-        it1.GetContainer() == it2.GetContainer() &&
-        // And on the same line in it
-        it1.GetLine() == it2.GetLine();
-    }
-    if (nsRubyTextContainerFrame* rtcFrame = do_QueryFrame(mLineContainer)) {
-      nsBlockFrame* block = nsLayoutUtils::FindNearestBlockAncestor(rtcFrame);
-      // Ruby text container can only hold one line of text, so if they
-      // are in the same continuation, they are in the same line. Since
-      // ruby text containers are bidi isolate, they are never split for
-      // bidi reordering, which means being in different continuation
-      // indicates being in different lines.
-      for (nsIFrame* frame = rtcFrame->FirstContinuation();
-           frame; frame = frame->GetNextContinuation()) {
-        bool isDescendant1 =
-          nsLayoutUtils::IsProperAncestorFrame(frame, aFrame1, block);
-        bool isDescendant2 =
-          nsLayoutUtils::IsProperAncestorFrame(frame, aFrame2, block);
-        if (isDescendant1 && isDescendant2) {
-          return true;
-        }
-        if (isDescendant1 || isDescendant2) {
-          return false;
-        }
-      }
-      MOZ_ASSERT_UNREACHABLE("None of the frames is a descendant of this rtc?");
-    }
-    MOZ_ASSERT_UNREACHABLE("Do we have any other type of line container?");
-    return false;
-  }
-};
-
-// A resolved color stop, with a specific position along the gradient line and
-// a color.
-struct ColorStop {
-  ColorStop(): mPosition(0), mIsMidpoint(false) {}
-  ColorStop(double aPosition, bool aIsMidPoint, const Color& aColor) :
-    mPosition(aPosition), mIsMidpoint(aIsMidPoint), mColor(aColor) {}
-  double mPosition; // along the gradient line; 0=start, 1=end
-  bool mIsMidpoint;
-  Color mColor;
-};
-
-/* Local functions */
-static nscolor MakeBevelColor(mozilla::Side whichSide, uint8_t style,
-                              nscolor aBackgroundColor,
-                              nscolor aBorderColor);
-
-static InlineBackgroundData* gInlineBGData = nullptr;
-
-// Initialize any static variables used by nsCSSRendering.
-void nsCSSRendering::Init()
-{
-  NS_ASSERTION(!gInlineBGData, "Init called twice");
-  gInlineBGData = new InlineBackgroundData();
-}
-
-// Clean up any global variables used by nsCSSRendering.
-void nsCSSRendering::Shutdown()
-{
-  delete gInlineBGData;
-  gInlineBGData = nullptr;
-}
-
-/**
- * Make a bevel color
- */
-static nscolor
-MakeBevelColor(mozilla::Side whichSide, uint8_t style,
-               nscolor aBackgroundColor, nscolor aBorderColor)
-{
-
-  nscolor colors[2];
-  nscolor theColor;
-
-  // Given a background color and a border color
-  // calculate the color used for the shading
-  NS_GetSpecial3DColors(colors, aBackgroundColor, aBorderColor);
-
-  if ((style == NS_STYLE_BORDER_STYLE_OUTSET) ||
-      (style == NS_STYLE_BORDER_STYLE_RIDGE)) {
-    // Flip colors for these two border styles
-    switch (whichSide) {
-    case eSideBottom: whichSide = eSideTop;    break;
-    case eSideRight:  whichSide = eSideLeft;   break;
-    case eSideTop:    whichSide = eSideBottom; break;
-    case eSideLeft:   whichSide = eSideRight;  break;
-    }
-  }
-
-  switch (whichSide) {
-  case eSideBottom:
-    theColor = colors[1];
-    break;
-  case eSideRight:
-    theColor = colors[1];
-    break;
-  case eSideTop:
-    theColor = colors[0];
-    break;
-  case eSideLeft:
-  default:
-    theColor = colors[0];
-    break;
-  }
-  return theColor;
-}
-
-static bool
-GetRadii(nsIFrame* aForFrame, const nsStyleBorder& aBorder,
-         const nsRect& aOrigBorderArea, const nsRect& aBorderArea,
-         nscoord aRadii[8])
-{
-  bool haveRoundedCorners;
-  nsSize sz = aBorderArea.Size();
-  nsSize frameSize = aForFrame->GetSize();
-  if (&aBorder == aForFrame->StyleBorder() &&
-      frameSize == aOrigBorderArea.Size()) {
-    haveRoundedCorners = aForFrame->GetBorderRadii(sz, sz, Sides(), aRadii);
-   } else {
-    haveRoundedCorners =
-      nsIFrame::ComputeBorderRadii(aBorder.mBorderRadius, frameSize, sz, Sides(), aRadii);
-  }
-
-  return haveRoundedCorners;
-}
-
-static bool
-GetRadii(nsIFrame* aForFrame, const nsStyleBorder& aBorder,
-         const nsRect& aOrigBorderArea, const nsRect& aBorderArea,
-         RectCornerRadii* aBgRadii)
-{
-  nscoord radii[8];
-  bool haveRoundedCorners = GetRadii(aForFrame, aBorder, aOrigBorderArea, aBorderArea, radii);
-
-  if (haveRoundedCorners) {
-    auto d2a = aForFrame->PresContext()->AppUnitsPerDevPixel();
-    nsCSSRendering::ComputePixelRadii(radii, d2a, aBgRadii);
-  }
-  return haveRoundedCorners;
-}
-
-static nsRect
-JoinBoxesForBlockAxisSlice(nsIFrame* aFrame, const nsRect& aBorderArea)
-{
-  // Inflate the block-axis size as if our continuations were laid out
-  // adjacent in that axis.  Note that we don't touch the inline size.
-  nsRect borderArea = aBorderArea;
-  nscoord bSize = 0;
-  auto wm = aFrame->GetWritingMode();
-  nsIFrame* f = aFrame->GetNextContinuation();
-  for (; f; f = f->GetNextContinuation()) {
-    MOZ_ASSERT(!(f->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT),
-               "anonymous ib-split block shouldn't have border/background");
-    bSize += f->BSize(wm);
-  }
-  (wm.IsVertical() ? borderArea.width : borderArea.height) += bSize;
-  bSize = 0;
-  f = aFrame->GetPrevContinuation();
-  for (; f; f = f->GetPrevContinuation()) {
-    MOZ_ASSERT(!(f->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT),
-               "anonymous ib-split block shouldn't have border/background");
-    bSize += f->BSize(wm);
-  }
-  (wm.IsVertical() ? borderArea.x : borderArea.y) -= bSize;
-  (wm.IsVertical() ? borderArea.width : borderArea.height) += bSize;
-  return borderArea;
-}
-
-/**
- * Inflate aBorderArea which is relative to aFrame's origin to calculate
- * a hypothetical non-split frame area for all the continuations.
- * See "Joining Boxes for 'slice'" in
- * http://dev.w3.org/csswg/css-break/#break-decoration
- */
-enum InlineBoxOrder { eForBorder, eForBackground };
-static nsRect
-JoinBoxesForSlice(nsIFrame* aFrame, const nsRect& aBorderArea,
-                  InlineBoxOrder aOrder)
-{
-  if (static_cast<nsInlineFrame*>(do_QueryFrame(aFrame))) {
-    return (aOrder == eForBorder
-            ? gInlineBGData->GetBorderContinuousRect(aFrame, aBorderArea)
-            : gInlineBGData->GetContinuousRect(aFrame)) +
-      aBorderArea.TopLeft();
-  }
-  return JoinBoxesForBlockAxisSlice(aFrame, aBorderArea);
-}
-
-static bool
-IsBoxDecorationSlice(const nsStyleBorder& aStyleBorder)
-{
-  return aStyleBorder.mBoxDecorationBreak == StyleBoxDecorationBreak::Slice;
-}
-
-static nsRect
-BoxDecorationRectForBorder(nsIFrame* aFrame, const nsRect& aBorderArea,
-                           Sides aSkipSides,
-                           const nsStyleBorder* aStyleBorder = nullptr)
-{
-  if (!aStyleBorder) {
-    aStyleBorder = aFrame->StyleBorder();
-  }
-  // If aSkipSides.IsEmpty() then there are no continuations, or it's
-  // a ::first-letter that wants all border sides on the first continuation.
-  return ::IsBoxDecorationSlice(*aStyleBorder) && !aSkipSides.IsEmpty()
-           ? ::JoinBoxesForSlice(aFrame, aBorderArea, eForBorder)
-           : aBorderArea;
-}
-
-static nsRect
-BoxDecorationRectForBackground(nsIFrame* aFrame, const nsRect& aBorderArea,
-                               Sides aSkipSides,
-                               const nsStyleBorder* aStyleBorder = nullptr)
-{
-  if (!aStyleBorder) {
-    aStyleBorder = aFrame->StyleBorder();
-  }
-  // If aSkipSides.IsEmpty() then there are no continuations, or it's
-  // a ::first-letter that wants all border sides on the first continuation.
-  return ::IsBoxDecorationSlice(*aStyleBorder) && !aSkipSides.IsEmpty()
-           ? ::JoinBoxesForSlice(aFrame, aBorderArea, eForBackground)
-           : aBorderArea;
-}
-
-//----------------------------------------------------------------------
-// Thebes Border Rendering Code Start
-
-/*
- * Compute the float-pixel radii that should be used for drawing
- * this border/outline, given the various input bits.
- */
-/* static */ void
-nsCSSRendering::ComputePixelRadii(const nscoord *aAppUnitsRadii,
-                                  nscoord aAppUnitsPerPixel,
-                                  RectCornerRadii *oBorderRadii)
-{
-  Float radii[8];
-  NS_FOR_CSS_HALF_CORNERS(corner)
-    radii[corner] = Float(aAppUnitsRadii[corner]) / aAppUnitsPerPixel;
-
-  (*oBorderRadii)[C_TL] = Size(radii[eCornerTopLeftX],
-                               radii[eCornerTopLeftY]);
-  (*oBorderRadii)[C_TR] = Size(radii[eCornerTopRightX],
-                               radii[eCornerTopRightY]);
-  (*oBorderRadii)[C_BR] = Size(radii[eCornerBottomRightX],
-                               radii[eCornerBottomRightY]);
-  (*oBorderRadii)[C_BL] = Size(radii[eCornerBottomLeftX],
-                               radii[eCornerBottomLeftY]);
-}
-
-DrawResult
-nsCSSRendering::PaintBorder(nsPresContext* aPresContext,
-                            nsRenderingContext& aRenderingContext,
-                            nsIFrame* aForFrame,
-                            const nsRect& aDirtyRect,
-                            const nsRect& aBorderArea,
-                            nsStyleContext* aStyleContext,
-                            PaintBorderFlags aFlags,
-                            Sides aSkipSides)
-{
-  PROFILER_LABEL("nsCSSRendering", "PaintBorder",
-    js::ProfileEntry::Category::GRAPHICS);
-
-  nsStyleContext *styleIfVisited = aStyleContext->GetStyleIfVisited();
-  const nsStyleBorder *styleBorder = aStyleContext->StyleBorder();
-  // Don't check RelevantLinkVisited here, since we want to take the
-  // same amount of time whether or not it's true.
-  if (!styleIfVisited) {
-    return PaintBorderWithStyleBorder(aPresContext, aRenderingContext, aForFrame,
-                                      aDirtyRect, aBorderArea, *styleBorder,
-                                      aStyleContext, aFlags, aSkipSides);
-  }
-
-  nsStyleBorder newStyleBorder(*styleBorder);
-
-  NS_FOR_CSS_SIDES(side) {
-    nscolor color = aStyleContext->
-      GetVisitedDependentColor(nsStyleBorder::BorderColorFieldFor(side));
-    newStyleBorder.mBorderColor[side] = StyleComplexColor::FromColor(color);
-  }
-  return PaintBorderWithStyleBorder(aPresContext, aRenderingContext, aForFrame,
-                                    aDirtyRect, aBorderArea, newStyleBorder,
-                                    aStyleContext, aFlags, aSkipSides);
-}
-
-Maybe<nsCSSBorderRenderer>
-nsCSSRendering::CreateBorderRenderer(nsPresContext* aPresContext,
-                                     DrawTarget* aDrawTarget,
-                                     nsIFrame* aForFrame,
-                                     const nsRect& aDirtyRect,
-                                     const nsRect& aBorderArea,
-                                     nsStyleContext* aStyleContext,
-                                     Sides aSkipSides)
-{
-  nsStyleContext *styleIfVisited = aStyleContext->GetStyleIfVisited();
-  const nsStyleBorder *styleBorder = aStyleContext->StyleBorder();
-  // Don't check RelevantLinkVisited here, since we want to take the
-  // same amount of time whether or not it's true.
-  if (!styleIfVisited) {
-    return CreateBorderRendererWithStyleBorder(aPresContext, aDrawTarget,
-                                               aForFrame, aDirtyRect,
-                                               aBorderArea, *styleBorder,
-                                               aStyleContext, aSkipSides);
-  }
-
-  nsStyleBorder newStyleBorder(*styleBorder);
-
-  NS_FOR_CSS_SIDES(side) {
-    nscolor color = aStyleContext->
-      GetVisitedDependentColor(nsStyleBorder::BorderColorFieldFor(side));
-    newStyleBorder.mBorderColor[side] = StyleComplexColor::FromColor(color);
-  }
-  return CreateBorderRendererWithStyleBorder(aPresContext, aDrawTarget,
-                                             aForFrame, aDirtyRect, aBorderArea,
-                                             newStyleBorder, aStyleContext,
-                                             aSkipSides);
-}
-
-nsCSSBorderRenderer
-ConstructBorderRenderer(nsPresContext* aPresContext,
-                        nsStyleContext* aStyleContext,
-                        DrawTarget* aDrawTarget,
-                        nsIFrame* aForFrame,
-                        const nsRect& aDirtyRect,
-                        const nsRect& aBorderArea,
-                        const nsStyleBorder& aStyleBorder,
-                        Sides aSkipSides,
-                        bool* aNeedsClip)
-{
-  nsMargin border = aStyleBorder.GetComputedBorder();
-
-  // Get our style context's color struct.
-  const nsStyleColor* ourColor = aStyleContext->StyleColor();
-
-  // In NavQuirks mode we want to use the parent's context as a starting point
-  // for determining the background color.
-  bool quirks = aPresContext->CompatibilityMode() == eCompatibility_NavQuirks;
-  nsIFrame* bgFrame = nsCSSRendering::FindNonTransparentBackgroundFrame(aForFrame, quirks);
-  nsStyleContext* bgContext = bgFrame->StyleContext();
-  nscolor bgColor = bgContext->
-    GetVisitedDependentColor(&nsStyleBackground::mBackgroundColor);
-
-  // Compute the outermost boundary of the area that might be painted.
-  // Same coordinate space as aBorderArea & aBGClipRect.
-  nsRect joinedBorderArea =
-    ::BoxDecorationRectForBorder(aForFrame, aBorderArea, aSkipSides, &aStyleBorder);
-  RectCornerRadii bgRadii;
-  ::GetRadii(aForFrame, aStyleBorder, aBorderArea, joinedBorderArea, &bgRadii);
-
-  PrintAsFormatString(" joinedBorderArea: %d %d %d %d\n", joinedBorderArea.x, joinedBorderArea.y,
-     joinedBorderArea.width, joinedBorderArea.height);
-
-  // start drawing
-  if (::IsBoxDecorationSlice(aStyleBorder)) {
-    if (joinedBorderArea.IsEqualEdges(aBorderArea)) {
-      // No need for a clip, just skip the sides we don't want.
-      border.ApplySkipSides(aSkipSides);
-    } else {
-      // We're drawing borders around the joined continuation boxes so we need
-      // to clip that to the slice that we want for this frame.
-      *aNeedsClip = true;
-    }
-  } else {
-    MOZ_ASSERT(joinedBorderArea.IsEqualEdges(aBorderArea),
-               "Should use aBorderArea for box-decoration-break:clone");
-    MOZ_ASSERT(aForFrame->GetSkipSides().IsEmpty() ||
-               IS_TRUE_OVERFLOW_CONTAINER(aForFrame),
-               "Should not skip sides for box-decoration-break:clone except "
-               "::first-letter/line continuations or other frame types that "
-               "don't have borders but those shouldn't reach this point. "
-               "Overflow containers do reach this point though.");
-    border.ApplySkipSides(aSkipSides);
-  }
-
-  // Convert to dev pixels.
-  nscoord twipsPerPixel = aPresContext->DevPixelsToAppUnits(1);
-  Rect joinedBorderAreaPx = NSRectToRect(joinedBorderArea, twipsPerPixel);
-  Float borderWidths[4] = { Float(border.top) / twipsPerPixel,
-                                   Float(border.right) / twipsPerPixel,
-                                   Float(border.bottom) / twipsPerPixel,
-                                   Float(border.left) / twipsPerPixel };
-  Rect dirtyRect = NSRectToRect(aDirtyRect, twipsPerPixel);
-
-  uint8_t borderStyles[4];
-  nscolor borderColors[4];
-  nsBorderColors* compositeColors[4];
-
-  // pull out styles, colors, composite colors
-  NS_FOR_CSS_SIDES (i) {
-    borderStyles[i] = aStyleBorder.GetBorderStyle(i);
-    borderColors[i] = ourColor->CalcComplexColor(aStyleBorder.mBorderColor[i]);
-    aStyleBorder.GetCompositeColors(i, &compositeColors[i]);
-  }
-
-  PrintAsFormatString(" borderStyles: %d %d %d %d\n", borderStyles[0], borderStyles[1], borderStyles[2], borderStyles[3]);
-
-  nsIDocument* document = nullptr;
-  nsIContent* content = aForFrame->GetContent();
-  if (content) {
-    document = content->OwnerDoc();
-  }
-
-  return nsCSSBorderRenderer(aPresContext,
-                             document,
-                             aDrawTarget,
-                             dirtyRect,
-                             joinedBorderAreaPx,
-                             borderStyles,
-                             borderWidths,
-                             bgRadii,
-                             borderColors,
-                             compositeColors,
-                             bgColor);
-}
-
-
-DrawResult
-nsCSSRendering::PaintBorderWithStyleBorder(nsPresContext* aPresContext,
-                                           nsRenderingContext& aRenderingContext,
-                                           nsIFrame* aForFrame,
-                                           const nsRect& aDirtyRect,
-                                           const nsRect& aBorderArea,
-                                           const nsStyleBorder& aStyleBorder,
-                                           nsStyleContext* aStyleContext,
-                                           PaintBorderFlags aFlags,
-                                           Sides aSkipSides)
-{
-  DrawTarget& aDrawTarget = *aRenderingContext.GetDrawTarget();
-
-  PrintAsStringNewline("++ PaintBorder");
-
-  // Check to see if we have an appearance defined.  If so, we let the theme
-  // renderer draw the border.  DO not get the data from aForFrame, since the passed in style context
-  // may be different!  Always use |aStyleContext|!
-  const nsStyleDisplay* displayData = aStyleContext->StyleDisplay();
-  if (displayData->UsedAppearance()) {
-    nsITheme *theme = aPresContext->GetTheme();
-    if (theme &&
-        theme->ThemeSupportsWidget(aPresContext, aForFrame,
-                                   displayData->UsedAppearance())) {
-      return DrawResult::SUCCESS; // Let the theme handle it.
-    }
-  }
-
-  if (aStyleBorder.IsBorderImageLoaded()) {
-    DrawResult result;
-
-    uint32_t irFlags = 0;
-    if (aFlags & PaintBorderFlags::SYNC_DECODE_IMAGES) {
-      irFlags |= nsImageRenderer::FLAG_SYNC_DECODE_IMAGES;
-    }
-
-    Maybe<nsCSSBorderImageRenderer> renderer =
-      nsCSSBorderImageRenderer::CreateBorderImageRenderer(aPresContext, aForFrame, aBorderArea,
-                                                          aStyleBorder, aDirtyRect, aSkipSides,
-                                                          irFlags, &result);
-    if (!renderer) {
-      return result;
-    }
-
-    return renderer->DrawBorderImage(aPresContext, aRenderingContext,
-                                     aForFrame, aDirtyRect);
-  }
-
-  DrawResult result = DrawResult::SUCCESS;
-
-  // If we had a border-image, but it wasn't loaded, then we should return
-  // DrawResult::NOT_READY; we'll want to try again if we do a paint with sync
-  // decoding enabled.
-  if (aStyleBorder.mBorderImageSource.GetType() != eStyleImageType_Null) {
-    result = DrawResult::NOT_READY;
-  }
-
-  nsMargin border = aStyleBorder.GetComputedBorder();
-  if (0 == border.left && 0 == border.right &&
-      0 == border.top  && 0 == border.bottom) {
-    // Empty border area
-    return result;
-  }
-
-  bool needsClip = false;
-  nsCSSBorderRenderer br = ConstructBorderRenderer(aPresContext,
-                                                   aStyleContext,
-                                                   &aDrawTarget,
-                                                   aForFrame,
-                                                   aDirtyRect,
-                                                   aBorderArea,
-                                                   aStyleBorder,
-                                                   aSkipSides,
-                                                   &needsClip);
-  if (needsClip) {
-    aDrawTarget.PushClipRect(
-        NSRectToSnappedRect(aBorderArea,
-                            aForFrame->PresContext()->AppUnitsPerDevPixel(),
-                            aDrawTarget));
-  }
-
-  br.DrawBorders();
-
-  if (needsClip) {
-    aDrawTarget.PopClip();
-  }
-
-  PrintAsStringNewline();
-
-  return result;
-}
-
-Maybe<nsCSSBorderRenderer>
-nsCSSRendering::CreateBorderRendererWithStyleBorder(nsPresContext* aPresContext,
-                                                    DrawTarget* aDrawTarget,
-                                                    nsIFrame* aForFrame,
-                                                    const nsRect& aDirtyRect,
-                                                    const nsRect& aBorderArea,
-                                                    const nsStyleBorder& aStyleBorder,
-                                                    nsStyleContext* aStyleContext,
-                                                    Sides aSkipSides)
-{
-  const nsStyleDisplay* displayData = aStyleContext->StyleDisplay();
-  if (displayData->UsedAppearance()) {
-    nsITheme *theme = aPresContext->GetTheme();
-    if (theme &&
-        theme->ThemeSupportsWidget(aPresContext, aForFrame,
-                                   displayData->UsedAppearance())) {
-      return Nothing();
-    }
-  }
-
-  if (aStyleBorder.mBorderImageSource.GetType() != eStyleImageType_Null) {
-    return Nothing();
-  }
-
-  nsMargin border = aStyleBorder.GetComputedBorder();
-  if (0 == border.left && 0 == border.right &&
-      0 == border.top  && 0 == border.bottom) {
-    // Empty border area
-    return Nothing();
-  }
-
-  bool needsClip = false;
-  nsCSSBorderRenderer br = ConstructBorderRenderer(aPresContext,
-                                                   aStyleContext,
-                                                   aDrawTarget,
-                                                   aForFrame,
-                                                   aDirtyRect,
-                                                   aBorderArea,
-                                                   aStyleBorder,
-                                                   aSkipSides,
-                                                   &needsClip);
-  if (needsClip) {
-    return Nothing();
-  }
-  return Some(br);
-}
-
-static nsRect
-GetOutlineInnerRect(nsIFrame* aFrame)
-{
-  nsRect* savedOutlineInnerRect =
-    aFrame->Properties().Get(nsIFrame::OutlineInnerRectProperty());
-  if (savedOutlineInnerRect)
-    return *savedOutlineInnerRect;
-  NS_NOTREACHED("we should have saved a frame property");
-  return nsRect(nsPoint(0, 0), aFrame->GetSize());
-}
-
-Maybe<nsCSSBorderRenderer>
-nsCSSRendering::CreateBorderRendererForOutline(nsPresContext* aPresContext,
-                                               nsRenderingContext* aRenderingContext,
-                                               nsIFrame* aForFrame,
-                                               const nsRect& aDirtyRect,
-                                               const nsRect& aBorderArea,
-                                               nsStyleContext* aStyleContext)
-{
-  nscoord             twipsRadii[8];
-
-  // Get our style context's color struct.
-  const nsStyleOutline* ourOutline = aStyleContext->StyleOutline();
-
-  if (!ourOutline->ShouldPaintOutline()) {
-    // Empty outline
-    return Nothing();
-  }
-
-  nsIFrame* bgFrame = nsCSSRendering::FindNonTransparentBackgroundFrame
-    (aForFrame, false);
-  nsStyleContext* bgContext = bgFrame->StyleContext();
-  nscolor bgColor = bgContext->
-    GetVisitedDependentColor(&nsStyleBackground::mBackgroundColor);
-
-  nsRect innerRect;
-  if (
-#ifdef MOZ_XUL
-      aStyleContext->GetPseudoType() == CSSPseudoElementType::XULTree
-#else
-      false
-#endif
-     ) {
-    innerRect = aBorderArea;
-  } else {
-    innerRect = GetOutlineInnerRect(aForFrame) + aBorderArea.TopLeft();
-  }
-  nscoord offset = ourOutline->mOutlineOffset;
-  innerRect.Inflate(offset, offset);
-  // If the dirty rect is completely inside the border area (e.g., only the
-  // content is being painted), then we can skip out now
-  // XXX this isn't exactly true for rounded borders, where the inside curves may
-  // encroach into the content area.  A safer calculation would be to
-  // shorten insideRect by the radius one each side before performing this test.
-  if (innerRect.Contains(aDirtyRect))
-    return Nothing();
-
-  nscoord width = ourOutline->GetOutlineWidth();
-
-  nsRect outerRect = innerRect;
-  outerRect.Inflate(width, width);
-
-  // get the radius for our outline
-  nsIFrame::ComputeBorderRadii(ourOutline->mOutlineRadius, aBorderArea.Size(),
-                               outerRect.Size(), Sides(), twipsRadii);
-
-  // Get our conversion values
-  nscoord twipsPerPixel = aPresContext->DevPixelsToAppUnits(1);
-
-  // get the outer rectangles
-  Rect oRect(NSRectToRect(outerRect, twipsPerPixel));
-
-  // convert the radii
-  nsMargin outlineMargin(width, width, width, width);
-  RectCornerRadii outlineRadii;
-  ComputePixelRadii(twipsRadii, twipsPerPixel, &outlineRadii);
-
-  uint8_t outlineStyle = ourOutline->mOutlineStyle;
-  if (outlineStyle == NS_STYLE_BORDER_STYLE_AUTO) {
-    if (nsLayoutUtils::IsOutlineStyleAutoEnabled()) {
-      nsITheme* theme = aPresContext->GetTheme();
-      if (theme && theme->ThemeSupportsWidget(aPresContext, aForFrame,
-                                              NS_THEME_FOCUS_OUTLINE)) {
-        theme->DrawWidgetBackground(aRenderingContext, aForFrame,
-                                    NS_THEME_FOCUS_OUTLINE, innerRect,
-                                    aDirtyRect);
-        return Nothing();
-      }
-    }
-    if (width == 0) {
-      return Nothing(); // empty outline
-    }
-    // http://dev.w3.org/csswg/css-ui/#outline
-    // "User agents may treat 'auto' as 'solid'."
-    outlineStyle = NS_STYLE_BORDER_STYLE_SOLID;
-  }
-
-  uint8_t outlineStyles[4] = { outlineStyle, outlineStyle,
-                               outlineStyle, outlineStyle };
-
-  // This handles treating the initial color as 'currentColor'; if we
-  // ever want 'invert' back we'll need to do a bit of work here too.
-  nscolor outlineColor =
-    aStyleContext->GetVisitedDependentColor(&nsStyleOutline::mOutlineColor);
-  nscolor outlineColors[4] = { outlineColor,
-                               outlineColor,
-                               outlineColor,
-                               outlineColor };
-
-  // convert the border widths
-  Float outlineWidths[4] = { Float(width / twipsPerPixel),
-                             Float(width / twipsPerPixel),
-                             Float(width / twipsPerPixel),
-                             Float(width / twipsPerPixel) };
-  Rect dirtyRect = NSRectToRect(aDirtyRect, twipsPerPixel);
-
-  nsIDocument* document = nullptr;
-  nsIContent* content = aForFrame->GetContent();
-  if (content) {
-    document = content->OwnerDoc();
-  }
-
-  DrawTarget* dt = aRenderingContext ? aRenderingContext->GetDrawTarget() : nullptr;
-  nsCSSBorderRenderer br(aPresContext,
-                         document,
-                         dt,
-                         dirtyRect,
-                         oRect,
-                         outlineStyles,
-                         outlineWidths,
-                         outlineRadii,
-                         outlineColors,
-                         nullptr,
-                         bgColor);
-
-  return Some(br);
-}
-
-void
-nsCSSRendering::PaintOutline(nsPresContext* aPresContext,
-                             nsRenderingContext& aRenderingContext,
-                             nsIFrame* aForFrame,
-                             const nsRect& aDirtyRect,
-                             const nsRect& aBorderArea,
-                             nsStyleContext* aStyleContext)
-{
-  Maybe<nsCSSBorderRenderer> br = CreateBorderRendererForOutline(aPresContext,
-                                                                 &aRenderingContext,
-                                                                 aForFrame,
-                                                                 aDirtyRect,
-                                                                 aBorderArea,
-                                                                 aStyleContext);
-  if (!br) {
-    return;
-  }
-
-  // start drawing
-  br->DrawBorders();
-
-  PrintAsStringNewline();
-}
-
-void
-nsCSSRendering::PaintFocus(nsPresContext* aPresContext,
-                           DrawTarget* aDrawTarget,
-                           const nsRect& aFocusRect,
-                           nscolor aColor)
-{
-  nscoord oneCSSPixel = nsPresContext::CSSPixelsToAppUnits(1);
-  nscoord oneDevPixel = aPresContext->DevPixelsToAppUnits(1);
-
-  Rect focusRect(NSRectToRect(aFocusRect, oneDevPixel));
-
-  RectCornerRadii focusRadii;
-  {
-    nscoord twipsRadii[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
-    ComputePixelRadii(twipsRadii, oneDevPixel, &focusRadii);
-  }
-  Float focusWidths[4] = { Float(oneCSSPixel / oneDevPixel),
-                           Float(oneCSSPixel / oneDevPixel),
-                           Float(oneCSSPixel / oneDevPixel),
-                           Float(oneCSSPixel / oneDevPixel) };
-
-  uint8_t focusStyles[4] = { NS_STYLE_BORDER_STYLE_DOTTED,
-                             NS_STYLE_BORDER_STYLE_DOTTED,
-                             NS_STYLE_BORDER_STYLE_DOTTED,
-                             NS_STYLE_BORDER_STYLE_DOTTED };
-  nscolor focusColors[4] = { aColor, aColor, aColor, aColor };
-
-  // Because this renders a dotted border, the background color
-  // should not be used.  Therefore, we provide a value that will
-  // be blatantly wrong if it ever does get used.  (If this becomes
-  // something that CSS can style, this function will then have access
-  // to a style context and can use the same logic that PaintBorder
-  // and PaintOutline do.)
-  nsCSSBorderRenderer br(aPresContext,
-                         nullptr,
-                         aDrawTarget,
-                         focusRect,
-                         focusRect,
-                         focusStyles,
-                         focusWidths,
-                         focusRadii,
-                         focusColors,
-                         nullptr,
-                         NS_RGB(255, 0, 0));
-  br.DrawBorders();
-
-  PrintAsStringNewline();
-}
-
-// Thebes Border Rendering Code End
-//----------------------------------------------------------------------
-
-
-//----------------------------------------------------------------------
-
-/**
- * Helper for ComputeObjectAnchorPoint; parameters are the same as for
- * that function, except they're for a single coordinate / a single size
- * dimension. (so, x/width vs. y/height)
- */
-static void
-ComputeObjectAnchorCoord(const Position::Coord& aCoord,
-                         const nscoord aOriginBounds,
-                         const nscoord aImageSize,
-                         nscoord* aTopLeftCoord,
-                         nscoord* aAnchorPointCoord)
-{
-  *aAnchorPointCoord = aCoord.mLength;
-  *aTopLeftCoord = aCoord.mLength;
-
-  if (aCoord.mHasPercent) {
-    // Adjust aTopLeftCoord by the specified % of the extra space.
-    nscoord extraSpace = aOriginBounds - aImageSize;
-    *aTopLeftCoord += NSToCoordRound(aCoord.mPercent * extraSpace);
-
-    // The anchor-point doesn't care about our image's size; just the size
-    // of the region we're rendering into.
-    *aAnchorPointCoord += NSToCoordRound(aCoord.mPercent * aOriginBounds);
-  }
-}
-
-void
-nsImageRenderer::ComputeObjectAnchorPoint(
-  const Position& aPos,
-  const nsSize& aOriginBounds,
-  const nsSize& aImageSize,
-  nsPoint* aTopLeft,
-  nsPoint* aAnchorPoint)
-{
-  ComputeObjectAnchorCoord(aPos.mXPosition,
-                           aOriginBounds.width, aImageSize.width,
-                           &aTopLeft->x, &aAnchorPoint->x);
-
-  ComputeObjectAnchorCoord(aPos.mYPosition,
-                           aOriginBounds.height, aImageSize.height,
-                           &aTopLeft->y, &aAnchorPoint->y);
-}
-
-nsIFrame*
-nsCSSRendering::FindNonTransparentBackgroundFrame(nsIFrame* aFrame,
-                                                  bool aStartAtParent /*= false*/)
-{
-  NS_ASSERTION(aFrame, "Cannot find NonTransparentBackgroundFrame in a null frame");
-
-  nsIFrame* frame = nullptr;
-  if (aStartAtParent) {
-    frame = nsLayoutUtils::GetParentOrPlaceholderFor(aFrame);
-  }
-  if (!frame) {
-    frame = aFrame;
-  }
-
-  while (frame) {
-    // No need to call GetVisitedDependentColor because it always uses
-    // this alpha component anyway.
-    if (NS_GET_A(frame->StyleBackground()->BackgroundColor(frame)) > 0) {
-      break;
-    }
-
-    if (frame->IsThemed())
-      break;
-
-    nsIFrame* parent = nsLayoutUtils::GetParentOrPlaceholderFor(frame);
-    if (!parent)
-      break;
-
-    frame = parent;
-  }
-  return frame;
-}
-
-// Returns true if aFrame is a canvas frame.
-// We need to treat the viewport as canvas because, even though
-// it does not actually paint a background, we need to get the right
-// background style so we correctly detect transparent documents.
-bool
-nsCSSRendering::IsCanvasFrame(nsIFrame* aFrame)
-{
-  nsIAtom* frameType = aFrame->GetType();
-  return frameType == nsGkAtoms::canvasFrame ||
-         frameType == nsGkAtoms::rootFrame ||
-         frameType == nsGkAtoms::pageContentFrame ||
-         frameType == nsGkAtoms::viewportFrame;
-}
-
-nsIFrame*
-nsCSSRendering::FindBackgroundStyleFrame(nsIFrame* aForFrame)
-{
-  const nsStyleBackground* result = aForFrame->StyleBackground();
-
-  // Check if we need to do propagation from BODY rather than HTML.
-  if (!result->IsTransparent(aForFrame)) {
-    return aForFrame;
-  }
-
-  nsIContent* content = aForFrame->GetContent();
-  // The root element content can't be null. We wouldn't know what
-  // frame to create for aFrame.
-  // Use |OwnerDoc| so it works during destruction.
-  if (!content) {
-    return aForFrame;
-  }
-
-  nsIDocument* document = content->OwnerDoc();
-
-  dom::Element* bodyContent = document->GetBodyElement();
-  // We need to null check the body node (bug 118829) since
-  // there are cases, thanks to the fix for bug 5569, where we
-  // will reflow a document with no body.  In particular, if a
-  // SCRIPT element in the head blocks the parser and then has a
-  // SCRIPT that does "document.location.href = 'foo'", then
-  // nsParser::Terminate will call |DidBuildModel| methods
-  // through to the content sink, which will call |StartLayout|
-  // and thus |Initialize| on the pres shell.  See bug 119351
-  // for the ugly details.
-  if (!bodyContent) {
-    return aForFrame;
-  }
-
-  nsIFrame *bodyFrame = bodyContent->GetPrimaryFrame();
-  if (!bodyFrame) {
-    return aForFrame;
-  }
-
-  return nsLayoutUtils::GetStyleFrame(bodyFrame);
-}
-
-/**
- * |FindBackground| finds the correct style data to use to paint the
- * background.  It is responsible for handling the following two
- * statements in section 14.2 of CSS2:
- *
- *   The background of the box generated by the root element covers the
- *   entire canvas.
- *
- *   For HTML documents, however, we recommend that authors specify the
- *   background for the BODY element rather than the HTML element. User
- *   agents should observe the following precedence rules to fill in the
- *   background: if the value of the 'background' property for the HTML
- *   element is different from 'transparent' then use it, else use the
- *   value of the 'background' property for the BODY element. If the
- *   resulting value is 'transparent', the rendering is undefined.
- *
- * Thus, in our implementation, it is responsible for ensuring that:
- *  + we paint the correct background on the |nsCanvasFrame|,
- *    |nsRootBoxFrame|, or |nsPageFrame|,
- *  + we don't paint the background on the root element, and
- *  + we don't paint the background on the BODY element in *some* cases,
- *    and for SGML-based HTML documents only.
- *
- * |FindBackground| returns true if a background should be painted, and
- * the resulting style context to use for the background information
- * will be filled in to |aBackground|.
- */
-nsStyleContext*
-nsCSSRendering::FindRootFrameBackground(nsIFrame* aForFrame)
-{
-  return FindBackgroundStyleFrame(aForFrame)->StyleContext();
-}
-
-inline bool
-FindElementBackground(nsIFrame* aForFrame, nsIFrame* aRootElementFrame,
-                      nsStyleContext** aBackgroundSC)
-{
-  if (aForFrame == aRootElementFrame) {
-    // We must have propagated our background to the viewport or canvas. Abort.
-    return false;
-  }
-
-  *aBackgroundSC = aForFrame->StyleContext();
-
-  // Return true unless the frame is for a BODY element whose background
-  // was propagated to the viewport.
-
-  nsIContent* content = aForFrame->GetContent();
-  if (!content || content->NodeInfo()->NameAtom() != nsGkAtoms::body)
-    return true; // not frame for a "body" element
-  // It could be a non-HTML "body" element but that's OK, we'd fail the
-  // bodyContent check below
-
-  if (aForFrame->StyleContext()->GetPseudo())
-    return true; // A pseudo-element frame.
-
-  // We should only look at the <html> background if we're in an HTML document
-  nsIDocument* document = content->OwnerDoc();
-
-  dom::Element* bodyContent = document->GetBodyElement();
-  if (bodyContent != content)
-    return true; // this wasn't the background that was propagated
-
-  // This can be called even when there's no root element yet, during frame
-  // construction, via nsLayoutUtils::FrameHasTransparency and
-  // nsContainerFrame::SyncFrameViewProperties.
-  if (!aRootElementFrame)
-    return true;
-
-  const nsStyleBackground* htmlBG = aRootElementFrame->StyleBackground();
-  return !htmlBG->IsTransparent(aRootElementFrame);
-}
-
-bool
-nsCSSRendering::FindBackground(nsIFrame* aForFrame,
-                               nsStyleContext** aBackgroundSC)
-{
-  nsIFrame* rootElementFrame =
-    aForFrame->PresContext()->PresShell()->FrameConstructor()->GetRootElementStyleFrame();
-  if (IsCanvasFrame(aForFrame)) {
-    *aBackgroundSC = FindCanvasBackground(aForFrame, rootElementFrame);
-    return true;
-  } else {
-    return FindElementBackground(aForFrame, rootElementFrame, aBackgroundSC);
-  }
-}
-
-void
-nsCSSRendering::BeginFrameTreesLocked()
-{
-  ++gFrameTreeLockCount;
-}
-
-void
-nsCSSRendering::EndFrameTreesLocked()
-{
-  NS_ASSERTION(gFrameTreeLockCount > 0, "Unbalanced EndFrameTreeLocked");
-  --gFrameTreeLockCount;
-  if (gFrameTreeLockCount == 0) {
-    gInlineBGData->Reset();
-  }
-}
-
-bool
-nsCSSRendering::HasBoxShadowNativeTheme(nsIFrame* aFrame,
-                                        bool& aMaybeHasBorderRadius)
-{
-  const nsStyleDisplay* styleDisplay = aFrame->StyleDisplay();
-  nsITheme::Transparency transparency;
-  if (aFrame->IsThemed(styleDisplay, &transparency)) {
-    aMaybeHasBorderRadius = false;
-    // For opaque (rectangular) theme widgets we can take the generic
-    // border-box path with border-radius disabled.
-    return transparency != nsITheme::eOpaque;
-  }
-
-  aMaybeHasBorderRadius = true;
-  return false;
-}
-
-gfx::Color
-nsCSSRendering::GetShadowColor(nsCSSShadowItem* aShadow,
-                               nsIFrame* aFrame,
-                               float aOpacity)
-{
-  // Get the shadow color; if not specified, use the foreground color
-  nscolor shadowColor;
-  if (aShadow->mHasColor)
-    shadowColor = aShadow->mColor;
-  else
-    shadowColor = aFrame->StyleColor()->mColor;
-
-  Color color = Color::FromABGR(shadowColor);
-  color.a *= aOpacity;
-  return color;
-}
-
-nsRect
-nsCSSRendering::GetShadowRect(const nsRect aFrameArea,
-                              bool aNativeTheme,
-                              nsIFrame* aForFrame)
-{
-  nsRect frameRect = aNativeTheme ?
-    aForFrame->GetVisualOverflowRectRelativeToSelf() + aFrameArea.TopLeft() :
-    aFrameArea;
-  Sides skipSides = aForFrame->GetSkipSides();
-  frameRect = ::BoxDecorationRectForBorder(aForFrame, frameRect, skipSides);
-
-  // Explicitly do not need to account for the spread radius here
-  // Webrender does it for us or PaintBoxShadow will for non-WR
-  return frameRect;
-}
-
-bool
-nsCSSRendering::GetBorderRadii(const nsRect& aFrameRect,
-                               const nsRect& aBorderRect,
-                               nsIFrame* aFrame,
-                               RectCornerRadii& aOutRadii)
-{
-  const nscoord twipsPerPixel = aFrame->PresContext()->DevPixelsToAppUnits(1);
-  nscoord twipsRadii[8];
-  NS_ASSERTION(aBorderRect.Size() == aFrame->VisualBorderRectRelativeToSelf().Size(),
-              "unexpected size");
-  nsSize sz = aFrameRect.Size();
-  bool hasBorderRadius = aFrame->GetBorderRadii(sz, sz, Sides(), twipsRadii);
-  if (hasBorderRadius) {
-    ComputePixelRadii(twipsRadii, twipsPerPixel, &aOutRadii);
-  }
-
-  return hasBorderRadius;
-}
-
-void