diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp
index 3ef29154057e7032f34980ff2fdd04946c096a91..f889808fe8fb7b9e1245467751ecb3c61c887e2a 100644
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -63,7 +63,6 @@
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/ErrorEvent.h"
 #include "mozilla/dom/FetchUtil.h"
-#include "mozilla/dom/RootedDictionary.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/SerializedStackHolder.h"
 #include "mozilla/CycleCollectedJSRuntime.h"
diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py
index acbf49e9e7c6b56ab1e1ac6914a784db6e46e043..a2c3ebcc1406ee4e5c988c41db99255ccfa49ee5 100644
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -35,6 +35,7 @@ from Configuration import (
     getAllTypes,
     Descriptor,
     MemberIsLegacyUnforgeable,
+    iteratorNativeType,
 )
 
 AUTOGENERATED_WARNING_COMMENT = (
@@ -1854,9 +1855,6 @@ class CGAbstractMethod(CGThing):
     arguments.
 
     canRunScript should be True to generate a MOZ_CAN_RUN_SCRIPT annotation.
-
-    signatureOnly should be True to only declare the signature (either in
-                  the header, or if static is True in the cpp file).
     """
 
     def __init__(
@@ -1870,7 +1868,6 @@ class CGAbstractMethod(CGThing):
         static=False,
         templateArgs=None,
         canRunScript=False,
-        signatureOnly=False,
     ):
         CGThing.__init__(self)
         self.descriptor = descriptor
@@ -1882,7 +1879,6 @@ class CGAbstractMethod(CGThing):
         self.static = static
         self.templateArgs = templateArgs
         self.canRunScript = canRunScript
-        self.signatureOnly = signatureOnly
 
     def _argstring(self, declare):
         return ", ".join([a.declare() if declare else a.define() for a in self.args])
@@ -1906,7 +1902,9 @@ class CGAbstractMethod(CGThing):
         maybeNewline = " " if self.inline else "\n"
         return " ".join(decorators) + maybeNewline
 
-    def signature(self):
+    def declare(self):
+        if self.inline:
+            return self._define(True)
         return "%s%s%s(%s);\n" % (
             self._template(),
             self._decorators(),
@@ -1914,13 +1912,6 @@ class CGAbstractMethod(CGThing):
             self._argstring(True),
         )
 
-    def declare(self):
-        if self.static:
-            return ""
-        if self.inline:
-            return self._define(True)
-        return self.signature()
-
     def indent_body(self, body):
         """
         Indent the code returned by self.definition_body(). Most classes
@@ -1937,12 +1928,7 @@ class CGAbstractMethod(CGThing):
         )
 
     def define(self):
-        if self.signatureOnly:
-            if self.static:
-                # self.static makes us not output anything in the header, so output the signature here.
-                return self.signature()
-            return ""
-        return "" if (self.inline and not self.static) else self._define()
+        return "" if self.inline else self._define()
 
     def definition_prologue(self, fromDeclare):
         error_reporting_label = self.error_reporting_label()
@@ -2027,6 +2013,10 @@ class CGAbstractStaticMethod(CGAbstractMethod):
             canRunScript=canRunScript,
         )
 
+    def declare(self):
+        # We only have implementation
+        return ""
+
 
 class CGAbstractClassHook(CGAbstractStaticMethod):
     """
@@ -2050,6 +2040,14 @@ class CGAbstractClassHook(CGAbstractStaticMethod):
         assert False  # Override me!
 
 
+class CGGetJSClassMethod(CGAbstractMethod):
+    def __init__(self, descriptor):
+        CGAbstractMethod.__init__(self, descriptor, "GetJSClass", "const JSClass*", [])
+
+    def definition_body(self):
+        return "return sClass.ToJSClass();\n"
+
+
 class CGAddPropertyHook(CGAbstractClassHook):
     """
     A hook for addProperty, used to preserve our wrapper from GC.
@@ -3566,7 +3564,7 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
     """
 
     def __init__(
-        self, descriptor, properties, haveUnscopables, haveLegacyWindowAliases, static
+        self, descriptor, properties, haveUnscopables, haveLegacyWindowAliases
     ):
         args = [
             Argument("JSContext*", "aCx"),
@@ -3575,7 +3573,7 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
             Argument("bool", "aDefineOnGlobal"),
         ]
         CGAbstractMethod.__init__(
-            self, descriptor, "CreateInterfaceObjects", "void", args, static=static
+            self, descriptor, "CreateInterfaceObjects", "void", args
         )
         self.properties = properties
         self.haveUnscopables = haveUnscopables
@@ -3948,7 +3946,7 @@ class CGGetProtoObjectHandleMethod(CGAbstractMethod):
     A method for getting the interface prototype object.
     """
 
-    def __init__(self, descriptor, static, signatureOnly=False):
+    def __init__(self, descriptor):
         CGAbstractMethod.__init__(
             self,
             descriptor,
@@ -3956,8 +3954,6 @@ class CGGetProtoObjectHandleMethod(CGAbstractMethod):
             "JS::Handle<JSObject*>",
             [Argument("JSContext*", "aCx")],
             inline=True,
-            static=static,
-            signatureOnly=signatureOnly,
         )
 
     def definition_body(self):
@@ -4308,7 +4304,7 @@ class CGDeserializer(CGAbstractMethod):
         )
 
 
-def CreateBindingJSObject(descriptor):
+def CreateBindingJSObject(descriptor, properties):
     objDecl = "BindingJSObjectCreator<%s> creator(aCx);\n" % descriptor.nativeType
 
     # We don't always need to root obj, but there are a variety
@@ -4501,17 +4497,9 @@ def CopyUnforgeablePropertiesToInstance(descriptor, failureCode):
 
 
 def AssertInheritanceChain(descriptor):
-    # We can skip the reinterpret_cast check for the descriptor's nativeType
-    # if aObject is a pointer of that type.
-    asserts = fill(
-        """
-        static_assert(std::is_same_v<decltype(aObject), ${nativeType}*>);
-        """,
-        nativeType=descriptor.nativeType,
-    )
+    asserts = ""
     iface = descriptor.interface
-    while iface.parent:
-        iface = iface.parent
+    while iface:
         desc = descriptor.getDescriptor(iface.identifier.name)
         asserts += (
             "MOZ_ASSERT(static_cast<%s*>(aObject) == \n"
@@ -4519,6 +4507,7 @@ def AssertInheritanceChain(descriptor):
             '           "Multiple inheritance for %s is broken.");\n'
             % (desc.nativeType, desc.nativeType, desc.nativeType)
         )
+        iface = iface.parent
     asserts += "MOZ_ASSERT(ToSupportsIsCorrect(aObject));\n"
     return asserts
 
@@ -4544,26 +4533,16 @@ def InitMemberSlots(descriptor, failureCode):
     )
 
 
-def DeclareProto(descriptor, noGivenProto=False):
+def DeclareProto(descriptor):
     """
     Declare the canonicalProto and proto we have for our wrapping operation.
     """
-    getCanonical = dedent(
+    preamble = dedent(
         """
-        JS::Handle<JSObject*> ${canonicalProto} = GetProtoObjectHandle(aCx);
-        if (!${canonicalProto}) {
+        JS::Handle<JSObject*> canonicalProto = GetProtoObjectHandle(aCx);
+        if (!canonicalProto) {
           return false;
         }
-        """
-    )
-
-    if noGivenProto:
-        return fill(getCanonical, canonicalProto="proto")
-
-    getCanonical = fill(getCanonical, canonicalProto="canonicalProto")
-
-    preamble = getCanonical + dedent(
-        """
         JS::Rooted<JSObject*> proto(aCx);
         """
     )
@@ -4600,9 +4579,11 @@ def DeclareProto(descriptor, noGivenProto=False):
 class CGWrapWithCacheMethod(CGAbstractMethod):
     """
     Create a wrapper JSObject for a given native that implements nsWrapperCache.
+
+    properties should be a PropertyArrays instance.
     """
 
-    def __init__(self, descriptor):
+    def __init__(self, descriptor, properties):
         assert descriptor.interface.hasInterfacePrototypeObject()
         args = [
             Argument("JSContext*", "aCx"),
@@ -4612,6 +4593,7 @@ class CGWrapWithCacheMethod(CGAbstractMethod):
             Argument("JS::MutableHandle<JSObject*>", "aReflector"),
         ]
         CGAbstractMethod.__init__(self, descriptor, "Wrap", "bool", args)
+        self.properties = properties
 
     def definition_body(self):
         failureCode = dedent(
@@ -4695,7 +4677,7 @@ class CGWrapWithCacheMethod(CGAbstractMethod):
             nativeType=self.descriptor.nativeType,
             assertInheritance=AssertInheritanceChain(self.descriptor),
             declareProto=DeclareProto(self.descriptor),
-            createObject=CreateBindingJSObject(self.descriptor),
+            createObject=CreateBindingJSObject(self.descriptor, self.properties),
             unforgeable=CopyUnforgeablePropertiesToInstance(
                 self.descriptor, failureCode
             ),
@@ -4736,48 +4718,29 @@ class CGWrapNonWrapperCacheMethod(CGAbstractMethod):
     """
     Create a wrapper JSObject for a given native that does not implement
     nsWrapperCache.
+
+    properties should be a PropertyArrays instance.
     """
 
-    def __init__(self, descriptor, static=False, signatureOnly=False):
+    def __init__(self, descriptor, properties):
         # XXX can we wrap if we don't have an interface prototype object?
         assert descriptor.interface.hasInterfacePrototypeObject()
-        self.noGivenProto = (
-            descriptor.interface.isIteratorInterface()
-            or descriptor.interface.isAsyncIteratorInterface()
-        )
         args = [
             Argument("JSContext*", "aCx"),
             Argument(descriptor.nativeType + "*", "aObject"),
+            Argument("JS::Handle<JSObject*>", "aGivenProto"),
+            Argument("JS::MutableHandle<JSObject*>", "aReflector"),
         ]
-        if not self.noGivenProto:
-            args.append(Argument("JS::Handle<JSObject*>", "aGivenProto"))
-        args.append(Argument("JS::MutableHandle<JSObject*>", "aReflector"))
-        CGAbstractMethod.__init__(
-            self,
-            descriptor,
-            "Wrap",
-            "bool",
-            args,
-            static=static,
-            signatureOnly=signatureOnly,
-        )
+        CGAbstractMethod.__init__(self, descriptor, "Wrap", "bool", args)
+        self.properties = properties
 
     def definition_body(self):
         failureCode = "return false;\n"
 
-        declareProto = DeclareProto(self.descriptor, noGivenProto=self.noGivenProto)
-        if self.noGivenProto:
-            assertGivenProto = ""
-        else:
-            assertGivenProto = dedent(
-                """
-                MOZ_ASSERT_IF(aGivenProto, js::IsObjectInContextCompartment(aGivenProto, aCx));
-                """
-            )
         return fill(
             """
             $*{assertions}
-            $*{assertGivenProto}
+            MOZ_ASSERT_IF(aGivenProto, js::IsObjectInContextCompartment(aGivenProto, aCx));
 
             JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
             $*{declareProto}
@@ -4792,9 +4755,8 @@ class CGWrapNonWrapperCacheMethod(CGAbstractMethod):
             return true;
             """,
             assertions=AssertInheritanceChain(self.descriptor),
-            assertGivenProto=assertGivenProto,
-            declareProto=declareProto,
-            createObject=CreateBindingJSObject(self.descriptor),
+            declareProto=DeclareProto(self.descriptor),
+            createObject=CreateBindingJSObject(self.descriptor, self.properties),
             unforgeable=CopyUnforgeablePropertiesToInstance(
                 self.descriptor, failureCode
             ),
@@ -5343,7 +5305,7 @@ class CastableObjectUnwrapper:
                 }
                 """,
                 exceptionCode=exceptionCode,
-                **self.substitution,
+                **self.substitution
             )
         else:
             self.substitution["codeOnFailure"] = codeOnFailure
@@ -5363,7 +5325,7 @@ class CastableObjectUnwrapper:
               }
             }
             """,
-            **substitution,
+            **substitution
         )
 
 
@@ -9581,6 +9543,7 @@ class CGPerSignatureCall(CGThing):
                 cgThings.append(
                     CGIterableMethodGenerator(
                         descriptor,
+                        idlNode.maplikeOrSetlikeOrIterable,
                         idlNode.identifier.name,
                         self.getArgumentNames(),
                     )
@@ -12849,7 +12812,7 @@ class CGUnionStruct(CGThing):
                     mType = e${name};
                     return mValue.m${name}.SetValue(${ctorArgs});
                     """,
-                    **vars,
+                    **vars
                 )
 
                 # bodyInHeader must be false for return values because they own
@@ -12916,7 +12879,7 @@ class CGUnionStruct(CGThing):
                 mValue.m${name}.Destroy();
                 mType = eUninitialized;
                 """,
-                **vars,
+                **vars
             )
             methods.append(
                 ClassMethod(
@@ -12934,7 +12897,7 @@ class CGUnionStruct(CGThing):
                 MOZ_RELEASE_ASSERT(Is${name}(), "Wrong type!");
                 return mValue.m${name}.Value();
                 """,
-                **vars,
+                **vars
             )
             # The non-const version of GetAs* returns our internal type
             getterReturnType = "%s&" % vars["structType"]
@@ -13281,7 +13244,7 @@ class CGUnionConversionStruct(CGThing):
                     mUnion.mType = mUnion.e${name};
                     return mUnion.mValue.m${name}.SetValue(${ctorArgs});
                     """,
-                    **vars,
+                    **vars
                 )
                 methods.append(
                     ClassMethod(
@@ -16393,52 +16356,19 @@ class CGDescriptor(CGThing):
 
         self._deps = descriptor.interface.getDeps()
 
-        iteratorCGThings = None
-        if (
-            descriptor.interface.isIterable()
-            and descriptor.interface.maplikeOrSetlikeOrIterable.isPairIterator()
-        ) or descriptor.interface.isAsyncIterable():
-            # We need the Wrap function when using the [Async]IterableIterator type, so we want to declare it before we need it. We don't really want to expose it in the header file, so we make it static too.
-            iteratorCGThings = []
-            itr_iface = (
-                descriptor.interface.maplikeOrSetlikeOrIterable.iteratorType.inner
-            )
-            iteratorDescriptor = descriptor.getDescriptor(itr_iface.identifier.name)
-            iteratorCGThings.append(
-                CGWrapNonWrapperCacheMethod(
-                    iteratorDescriptor, static=True, signatureOnly=True
-                )
-            )
-            iteratorCGThings = CGList(
-                (CGIndenter(t, declareOnly=True) for t in iteratorCGThings), "\n"
-            )
-            iteratorCGThings = CGWrapper(iteratorCGThings, pre="\n", post="\n")
-            iteratorCGThings = CGWrapper(
-                CGNamespace(
-                    toBindingNamespace(iteratorDescriptor.name), iteratorCGThings
-                ),
-                post="\n",
-            )
-
         cgThings = []
-
-        isIteratorInterface = (
-            descriptor.interface.isIteratorInterface()
-            or descriptor.interface.isAsyncIteratorInterface()
+        cgThings.append(
+            CGGeneric(declare="typedef %s NativeType;\n" % descriptor.nativeType)
         )
-        if not isIteratorInterface:
+        parent = descriptor.interface.parent
+        if parent:
             cgThings.append(
-                CGGeneric(declare="typedef %s NativeType;\n" % descriptor.nativeType)
-            )
-            parent = descriptor.interface.parent
-            if parent:
-                cgThings.append(
-                    CGGeneric(
-                        "static_assert(IsRefcounted<NativeType>::value == IsRefcounted<%s::NativeType>::value,\n"
-                        '              "Can\'t inherit from an interface with a different ownership model.");\n'
-                        % toBindingNamespace(descriptor.parentPrototypeName)
-                    )
+                CGGeneric(
+                    "static_assert(IsRefcounted<NativeType>::value == IsRefcounted<%s::NativeType>::value,\n"
+                    '              "Can\'t inherit from an interface with a different ownership model.");\n'
+                    % toBindingNamespace(descriptor.parentPrototypeName)
                 )
+            )
 
         defaultToJSONMethod = None
         needCrossOriginPropertyArrays = False
@@ -16641,19 +16571,12 @@ class CGDescriptor(CGThing):
                 cgThings.append(CGSerializer(descriptor))
                 cgThings.append(CGDeserializer(descriptor))
 
-            # CGDOMProxyJSClass/CGDOMJSClass need GetProtoObjectHandle, but we don't want to export it for the iterator interfaces, so declare it here.
-            if isIteratorInterface:
-                cgThings.append(
-                    CGGetProtoObjectHandleMethod(
-                        descriptor, static=True, signatureOnly=True
-                    )
-                )
-
             if descriptor.proxy:
                 cgThings.append(CGDOMJSProxyHandlerDefiner(handlerThing))
                 cgThings.append(CGDOMProxyJSClass(descriptor))
             else:
                 cgThings.append(CGDOMJSClass(descriptor))
+                cgThings.append(CGGetJSClassMethod(descriptor))
 
             if descriptor.interface.hasMembersInSlots():
                 cgThings.append(CGUpdateMemberSlotsMethod(descriptor))
@@ -16662,12 +16585,10 @@ class CGDescriptor(CGThing):
                 assert descriptor.wrapperCache
                 cgThings.append(CGWrapGlobalMethod(descriptor, properties))
             elif descriptor.wrapperCache:
-                cgThings.append(CGWrapWithCacheMethod(descriptor))
+                cgThings.append(CGWrapWithCacheMethod(descriptor, properties))
                 cgThings.append(CGWrapMethod(descriptor))
             else:
-                cgThings.append(
-                    CGWrapNonWrapperCacheMethod(descriptor, static=isIteratorInterface)
-                )
+                cgThings.append(CGWrapNonWrapperCacheMethod(descriptor, properties))
 
         # If we're not wrappercached, we don't know how to clear our
         # cached values, since we can't get at the JSObject.
@@ -16726,11 +16647,7 @@ class CGDescriptor(CGThing):
         # CGDOMJSClass and unscopables, if any.
         cgThings.append(
             CGCreateInterfaceObjectsMethod(
-                descriptor,
-                properties,
-                haveUnscopables,
-                haveLegacyWindowAliases,
-                static=isIteratorInterface,
+                descriptor, properties, haveUnscopables, haveLegacyWindowAliases
             )
         )
 
@@ -16740,11 +16657,8 @@ class CGDescriptor(CGThing):
             descriptor.interface.hasInterfacePrototypeObject()
             and not descriptor.hasOrdinaryObjectPrototype
         ):
-            cgThings.append(
-                CGGetProtoObjectHandleMethod(descriptor, static=isIteratorInterface)
-            )
+            cgThings.append(CGGetProtoObjectHandleMethod(descriptor))
             if descriptor.interface.hasChildInterfaces():
-                assert not isIteratorInterface
                 cgThings.append(CGGetProtoObjectMethod(descriptor))
         if descriptor.interface.hasInterfaceObject():
             cgThings.append(CGGetConstructorObjectHandleMethod(descriptor))
@@ -16756,10 +16670,9 @@ class CGDescriptor(CGThing):
 
         cgThings = CGList((CGIndenter(t, declareOnly=True) for t in cgThings), "\n")
         cgThings = CGWrapper(cgThings, pre="\n", post="\n")
-        cgThings = CGWrapper(
+        self.cgRoot = CGWrapper(
             CGNamespace(toBindingNamespace(descriptor.name), cgThings), post="\n"
         )
-        self.cgRoot = CGList([iteratorCGThings, cgThings], "\n")
 
     def declare(self):
         return self.cgRoot.declare()
@@ -18377,11 +18290,8 @@ class CGBindingRoot(CGThing):
         bindingDeclareHeaders["mozilla/dom/BindingUtils.h"] = any(
             d.isObject() for t in unionTypes for d in t.flatMemberTypes
         )
-        bindingHeaders["mozilla/dom/IterableIterator.h"] = any(
-            (
-                d.interface.isIteratorInterface()
-                and d.interface.maplikeOrSetlikeOrIterable.isPairIterator()
-            )
+        bindingDeclareHeaders["mozilla/dom/IterableIterator.h"] = any(
+            d.interface.isIteratorInterface()
             or d.interface.isAsyncIteratorInterface()
             or d.interface.isIterable()
             or d.interface.isAsyncIterable()
@@ -22280,7 +22190,7 @@ class CGIterableMethodGenerator(CGGeneric):
     using CGCallGenerator.
     """
 
-    def __init__(self, descriptor, methodName, args):
+    def __init__(self, descriptor, iterable, methodName, args):
         if methodName == "forEach":
             assert len(args) == 2
 
@@ -22319,51 +22229,43 @@ class CGIterableMethodGenerator(CGGeneric):
             return
 
         if descriptor.interface.isIterable():
-            assert descriptor.interface.maplikeOrSetlikeOrIterable.isPairIterator()
             assert len(args) == 0
 
-            wrap = f"{descriptor.interface.identifier.name}Iterator_Binding::Wrap"
-            iterClass = f"mozilla::dom::binding_detail::WrappableIterableIterator<{descriptor.nativeType}, &{wrap}>"
+            binding = descriptor.interface.identifier.name + "Iterator_Binding"
+            init = ""
         else:
-            needReturnMethod = toStringBool(
-                descriptor.interface.maplikeOrSetlikeOrIterable.getExtendedAttribute(
-                    "GenerateReturnMethod"
-                )
-                is not None
-            )
-            wrap = f"{descriptor.interface.identifier.name}AsyncIterator_Binding::Wrap"
-            iterClass = f"mozilla::dom::binding_detail::WrappableAsyncIterableIterator<{descriptor.nativeType}, {needReturnMethod}, &{wrap}>"
+            assert descriptor.interface.isAsyncIterable()
 
-        createIterator = fill(
-            """
-            typedef ${iterClass} itrType;
-            RefPtr<itrType> result(new itrType(self,
-                                               itrType::IteratorType::${itrMethod}));
-            """,
-            iterClass=iterClass,
-            itrMethod=methodName.title(),
-        )
-
-        if descriptor.interface.isAsyncIterable():
-            args.append("initError")
-            createIterator = fill(
+            binding = descriptor.interface.identifier.name + "AsyncIterator_Binding"
+            init = fill(
                 """
-                $*{createIterator}
                 {
                   ErrorResult initError;
-                  self->InitAsyncIteratorData(result->Data(), itrType::IteratorType::${itrMethod}, ${args});
+                  self->InitAsyncIterator(result.get(), ${args}initError);
                   if (initError.MaybeSetPendingException(cx, "Asynchronous iterator initialization steps for ${ifaceName} failed")) {
                     return false;
                   }
                 }
                 """,
-                createIterator=createIterator,
-                itrMethod=methodName.title(),
-                args=", ".join(args),
+                args="".join(a + ", " for a in args),
                 ifaceName=descriptor.interface.identifier.name,
             )
-
-        CGGeneric.__init__(self, createIterator)
+        CGGeneric.__init__(
+            self,
+            fill(
+                """
+                typedef ${iterClass} itrType;
+                RefPtr<itrType> result(new itrType(self,
+                                                   itrType::IteratorType::${itrMethod},
+                                                   &${binding}::Wrap));
+                $*{init}
+                """,
+                iterClass=iteratorNativeType(descriptor),
+                itrMethod=methodName.title(),
+                binding=binding,
+                init=init,
+            ),
+        )
 
 
 def getObservableArrayBackingObject(descriptor, attr, errorReturn="return false;\n"):
diff --git a/dom/bindings/Configuration.py b/dom/bindings/Configuration.py
index c1eb524b407f3a15333f2bda3aa76fe0454062ef..dd317fecf6a79d4842ceed3caa749d307150d4d3 100644
--- a/dom/bindings/Configuration.py
+++ b/dom/bindings/Configuration.py
@@ -13,13 +13,6 @@ from collections import defaultdict
 autogenerated_comment = "/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n"
 
 
-def toStringBool(arg):
-    """
-    Converts IDL/Python Boolean (True/False) to C++ Boolean (true/false)
-    """
-    return str(not not arg).lower()
-
-
 class DescriptorProvider:
     """
     A way of getting descriptors for interface names.  Subclasses must
@@ -764,8 +757,6 @@ class Descriptor(DescriptorProvider):
         if name in self.implicitJSContext:
             attrs.append("implicitJSContext")
         if member.isMethod():
-            if self.interface.isAsyncIteratorInterface() and name == "next":
-                attrs.append("implicitJSContext")
             # JSObject-returning [NewObject] methods must be fallible,
             # since they have to (fallibly) allocate the new JSObject.
             if member.getExtendedAttribute("NewObject"):
@@ -1059,16 +1050,7 @@ def iteratorNativeType(descriptor):
     assert iterableDecl.isPairIterator() or descriptor.interface.isAsyncIterable()
     if descriptor.interface.isIterable():
         return "mozilla::dom::IterableIterator<%s>" % descriptor.nativeType
-    needReturnMethod = toStringBool(
-        descriptor.interface.maplikeOrSetlikeOrIterable.getExtendedAttribute(
-            "GenerateReturnMethod"
-        )
-        is not None
-    )
-    return "mozilla::dom::binding_detail::AsyncIterableIteratorNative<%s, %s>" % (
-        descriptor.nativeType,
-        needReturnMethod,
-    )
+    return "mozilla::dom::AsyncIterableIterator<%s>" % descriptor.nativeType
 
 
 def findInnermostType(t):
diff --git a/dom/bindings/IterableIterator.cpp b/dom/bindings/IterableIterator.cpp
index e2183865f7e1f3b1029c83b5399e4889a9f571e0..438d581786d15f932c491b521face9c0647e485a 100644
--- a/dom/bindings/IterableIterator.cpp
+++ b/dom/bindings/IterableIterator.cpp
@@ -5,7 +5,6 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/IterableIterator.h"
-#include "mozilla/dom/Promise-inl.h"
 
 namespace mozilla::dom {
 
@@ -16,8 +15,8 @@ namespace mozilla::dom {
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(IterableIteratorBase)
 
-NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(IterableIteratorBase, AddRef)
-NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(IterableIteratorBase, Release)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(IterableIteratorBase)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(IterableIteratorBase)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IterableIteratorBase)
   tmp->TraverseHelper(cb);
@@ -27,9 +26,12 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IterableIteratorBase)
   tmp->UnlinkHelper();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
-namespace iterator_utils {
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IterableIteratorBase)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
 
-void DictReturn(JSContext* aCx, JS::MutableHandle<JS::Value> aResult,
+namespace iterator_utils {
+void DictReturn(JSContext* aCx, JS::MutableHandle<JSObject*> aResult,
                 bool aDone, JS::Handle<JS::Value> aValue, ErrorResult& aRv) {
   RootedDictionary<IterableKeyOrValueResult> dict(aCx);
   dict.mDone = aDone;
@@ -39,16 +41,6 @@ void DictReturn(JSContext* aCx, JS::MutableHandle<JS::Value> aResult,
     aRv.Throw(NS_ERROR_FAILURE);
     return;
   }
-  aResult.set(dictValue);
-}
-
-void DictReturn(JSContext* aCx, JS::MutableHandle<JSObject*> aResult,
-                bool aDone, JS::Handle<JS::Value> aValue, ErrorResult& aRv) {
-  JS::Rooted<JS::Value> dictValue(aCx);
-  DictReturn(aCx, &dictValue, aDone, aValue, aRv);
-  if (aRv.Failed()) {
-    return;
-  }
   aResult.set(&dictValue.toObject());
 }
 
@@ -75,249 +67,43 @@ void KeyAndValueReturn(JSContext* aCx, JS::Handle<JS::Value> aKey,
   aResult.set(&dictValue.toObject());
 }
 
-}  // namespace iterator_utils
-
-namespace binding_detail {
-
-static already_AddRefed<Promise> PromiseOrErr(
-    Result<RefPtr<Promise>, nsresult>&& aResult, ErrorResult& aError) {
-  if (aResult.isErr()) {
-    aError.Throw(aResult.unwrapErr());
-    return nullptr;
-  }
-
-  return aResult.unwrap().forget();
-}
-
-already_AddRefed<Promise> AsyncIterableNextImpl::NextSteps(
-    JSContext* aCx, AsyncIterableIteratorBase* aObject,
-    nsIGlobalObject* aGlobalObject, ErrorResult& aRv) {
-  // 2. If object’s is finished is true, then:
-  if (aObject->mIsFinished) {
-    // 1. Let result be CreateIterResultObject(undefined, true).
-    JS::Rooted<JS::Value> dict(aCx);
-    iterator_utils::DictReturn(aCx, &dict, true, JS::UndefinedHandleValue, aRv);
-    if (aRv.Failed()) {
-      return Promise::CreateRejectedWithErrorResult(aGlobalObject, aRv);
-    }
+void ResolvePromiseForFinished(JSContext* aCx, Promise* aPromise,
+                               ErrorResult& aRv) {
+  MOZ_ASSERT(aPromise);
 
-    // 2. Perform ! Call(nextPromiseCapability.[[Resolve]], undefined,
-    //    «result»).
-    // 3. Return nextPromiseCapability.[[Promise]].
-    return Promise::Resolve(aGlobalObject, aCx, dict, aRv);
-  }
-
-  // 4. Let nextPromise be the result of getting the next iteration result with
-  //    object’s target and object.
-  RefPtr<Promise> nextPromise = GetNextResult(aRv);
-
-  // 5. Let fulfillSteps be the following steps, given next:
-  auto fulfillSteps = [](JSContext* aCx, JS::Handle<JS::Value> aNext,
-                         ErrorResult& aRv,
-                         const RefPtr<AsyncIterableIteratorBase>& aObject,
-                         const nsCOMPtr<nsIGlobalObject>& aGlobalObject)
-      -> already_AddRefed<Promise> {
-    // 1. Set object’s ongoing promise to null.
-    aObject->mOngoingPromise = nullptr;
-
-    // 2. If next is end of iteration, then:
-    JS::Rooted<JS::Value> dict(aCx);
-    if (aNext.isMagic(binding_details::END_OF_ITERATION)) {
-      // 1. Set object’s is finished to true.
-      aObject->mIsFinished = true;
-      // 2. Return CreateIterResultObject(undefined, true).
-      iterator_utils::DictReturn(aCx, &dict, true, JS::UndefinedHandleValue,
-                                 aRv);
-      if (aRv.Failed()) {
-        return nullptr;
-      }
-    } else {
-      // 3. Otherwise, if interface has a pair asynchronously iterable
-      // declaration:
-      //   1. Assert: next is a value pair.
-      //   2. Return the iterator result for next and kind.
-      // 4. Otherwise:
-      //   1. Assert: interface has a value asynchronously iterable declaration.
-      //   2. Assert: next is a value of the type that appears in the
-      //   declaration.
-      //   3. Let value be next, converted to an ECMAScript value.
-      //   4. Return CreateIterResultObject(value, false).
-      iterator_utils::DictReturn(aCx, &dict, false, aNext, aRv);
-      if (aRv.Failed()) {
-        return nullptr;
-      }
-    }
-    // Note that ThenCatchWithCycleCollectedArgs expects a Promise, so
-    // we use Promise::Resolve here. The specs do convert this to a
-    // promise too at another point, but the end result should be the
-    // same.
-    return Promise::Resolve(aGlobalObject, aCx, dict, aRv);
-  };
-  // 7. Let rejectSteps be the following steps, given reason:
-  auto rejectSteps = [](JSContext* aCx, JS::Handle<JS::Value> aReason,
-                        ErrorResult& aRv,
-                        const RefPtr<AsyncIterableIteratorBase>& aObject,
-                        const nsCOMPtr<nsIGlobalObject>& aGlobalObject) {
-    // 1. Set object’s ongoing promise to null.
-    aObject->mOngoingPromise = nullptr;
-    // 2. Set object’s is finished to true.
-    aObject->mIsFinished = true;
-    // 3. Throw reason.
-    return Promise::Reject(aGlobalObject, aCx, aReason, aRv);
-  };
-  // 9. Perform PerformPromiseThen(nextPromise, onFulfilled, onRejected,
-  //    nextPromiseCapability).
-  Result<RefPtr<Promise>, nsresult> result =
-      nextPromise->ThenCatchWithCycleCollectedArgs(
-          std::move(fulfillSteps), std::move(rejectSteps), RefPtr{aObject},
-          nsCOMPtr{aGlobalObject});
-
-  // 10. Return nextPromiseCapability.[[Promise]].
-  return PromiseOrErr(std::move(result), aRv);
-}
-
-already_AddRefed<Promise> AsyncIterableNextImpl::Next(
-    JSContext* aCx, AsyncIterableIteratorBase* aObject,
-    nsISupports* aGlobalObject, ErrorResult& aRv) {
-  nsCOMPtr<nsIGlobalObject> globalObject = do_QueryInterface(aGlobalObject);
-
-  // 3.7.10.2. Asynchronous iterator prototype object
-  // …
-  // 10. If ongoingPromise is not null, then:
-  if (aObject->mOngoingPromise) {
-    // 1. Let afterOngoingPromiseCapability be
-    //    ! NewPromiseCapability(%Promise%).
-    // 2. Let onSettled be CreateBuiltinFunction(nextSteps, « »).
-
-    // aObject is the same object as 'this', so it's fine to capture 'this'
-    // without taking a strong reference, because we already take a strong
-    // reference to it through aObject.
-    auto onSettled = [this](JSContext* aCx, JS::Handle<JS::Value> aValue,
-                            ErrorResult& aRv,
-                            const RefPtr<AsyncIterableIteratorBase>& aObject,
-                            const nsCOMPtr<nsIGlobalObject>& aGlobalObject) {
-      return NextSteps(aCx, aObject, aGlobalObject, aRv);
-    };
-
-    // 3. Perform PerformPromiseThen(ongoingPromise, onSettled, onSettled,
-    //    afterOngoingPromiseCapability).
-    Result<RefPtr<Promise>, nsresult> afterOngoingPromise =
-        aObject->mOngoingPromise->ThenCatchWithCycleCollectedArgs(
-            onSettled, onSettled, RefPtr{aObject}, std::move(globalObject));
-    if (afterOngoingPromise.isErr()) {
-      aRv.Throw(afterOngoingPromise.unwrapErr());
-      return nullptr;
-    }
-
-    // 4. Set object’s ongoing promise to
-    //    afterOngoingPromiseCapability.[[Promise]].
-    aObject->mOngoingPromise = afterOngoingPromise.unwrap().forget();
-  } else {
-    // 11. Otherwise:
-    //   1. Set object’s ongoing promise to the result of running nextSteps.
-    aObject->mOngoingPromise = NextSteps(aCx, aObject, globalObject, aRv);
+  JS::Rooted<JSObject*> dict(aCx);
+  DictReturn(aCx, &dict, true, JS::UndefinedHandleValue, aRv);
+  if (aRv.Failed()) {
+    return;
   }
-
-  // 12. Return object’s ongoing promise.
-  return do_AddRef(aObject->mOngoingPromise);
+  aPromise->MaybeResolve(dict);
 }
 
-already_AddRefed<Promise> AsyncIterableReturnImpl::ReturnSteps(
-    JSContext* aCx, AsyncIterableIteratorBase* aObject,
-    nsIGlobalObject* aGlobalObject, JS::Handle<JS::Value> aValue,
-    ErrorResult& aRv) {
-  // 2. If object’s is finished is true, then:
-  if (aObject->mIsFinished) {
-    // 1. Let result be CreateIterResultObject(value, true).
-    JS::Rooted<JS::Value> dict(aCx);
-    iterator_utils::DictReturn(aCx, &dict, true, aValue, aRv);
-    if (aRv.Failed()) {
-      return Promise::CreateRejectedWithErrorResult(aGlobalObject, aRv);
-    }
+void ResolvePromiseWithKeyOrValue(JSContext* aCx, Promise* aPromise,
+                                  JS::Handle<JS::Value> aKeyOrValue,
+                                  ErrorResult& aRv) {
+  MOZ_ASSERT(aPromise);
 
-    // 2. Perform ! Call(returnPromiseCapability.[[Resolve]], undefined,
-    //    «result»).
-    // 3. Return returnPromiseCapability.[[Promise]].
-    return Promise::Resolve(aGlobalObject, aCx, dict, aRv);
+  JS::Rooted<JSObject*> dict(aCx);
+  DictReturn(aCx, &dict, false, aKeyOrValue, aRv);
+  if (aRv.Failed()) {
+    return;
   }
-
-  // 3. Set object’s is finished to true.
-  aObject->mIsFinished = true;
-
-  // 4. Return the result of running the asynchronous iterator return algorithm
-  // for interface, given object’s target, object, and value.
-  return GetReturnPromise(aCx, aValue, aRv);
+  aPromise->MaybeResolve(dict);
 }
 
-already_AddRefed<Promise> AsyncIterableReturnImpl::Return(
-    JSContext* aCx, AsyncIterableIteratorBase* aObject,
-    nsISupports* aGlobalObject, JS::Handle<JS::Value> aValue,
-    ErrorResult& aRv) {
-  nsCOMPtr<nsIGlobalObject> globalObject = do_QueryInterface(aGlobalObject);
+void ResolvePromiseWithKeyAndValue(JSContext* aCx, Promise* aPromise,
+                                   JS::Handle<JS::Value> aKey,
+                                   JS::Handle<JS::Value> aValue,
+                                   ErrorResult& aRv) {
+  MOZ_ASSERT(aPromise);
 
-  // 3.7.10.2. Asynchronous iterator prototype object
-  // …
-  RefPtr<Promise> returnStepsPromise;
-  // 11. If ongoingPromise is not null, then:
-  if (aObject->mOngoingPromise) {
-    // 1. Let afterOngoingPromiseCapability be
-    //    ! NewPromiseCapability(%Promise%).
-    // 2. Let onSettled be CreateBuiltinFunction(returnSteps, « »).
-
-    // aObject is the same object as 'this', so it's fine to capture 'this'
-    // without taking a strong reference, because we already take a strong
-    // reference to it through aObject.
-    auto onSettled = [this](JSContext* aCx, JS::Handle<JS::Value> aValue,
-                            ErrorResult& aRv,
-                            const RefPtr<AsyncIterableIteratorBase>& aObject,
-                            const nsCOMPtr<nsIGlobalObject>& aGlobalObject,
-                            JS::Handle<JS::Value> aVal) {
-      return ReturnSteps(aCx, aObject, aGlobalObject, aVal, aRv);
-    };
-
-    // 3. Perform PerformPromiseThen(ongoingPromise, onSettled, onSettled,
-    //    afterOngoingPromiseCapability).
-    Result<RefPtr<Promise>, nsresult> afterOngoingPromise =
-        aObject->mOngoingPromise->ThenCatchWithCycleCollectedArgsJS(
-            onSettled, onSettled,
-            std::make_tuple(RefPtr{aObject}, nsCOMPtr{globalObject}),
-            std::make_tuple(aValue));
-    if (afterOngoingPromise.isErr()) {
-      aRv.Throw(afterOngoingPromise.unwrapErr());
-      return nullptr;
-    }
-
-    // 4. Set returnStepsPromise to afterOngoingPromiseCapability.[[Promise]].
-    returnStepsPromise = afterOngoingPromise.unwrap().forget();
-  } else {
-    // 12. Otherwise:
-    //   1. Set returnStepsPromise to the result of running returnSteps.
-    returnStepsPromise = ReturnSteps(aCx, aObject, globalObject, aValue, aRv);
+  JS::Rooted<JSObject*> dict(aCx);
+  KeyAndValueReturn(aCx, aKey, aValue, &dict, aRv);
+  if (aRv.Failed()) {
+    return;
   }
-
-  // 13. Let fulfillSteps be the following steps:
-  auto onFullFilled = [](JSContext* aCx, JS::Handle<JS::Value>,
-                         ErrorResult& aRv,
-                         const nsCOMPtr<nsIGlobalObject>& aGlobalObject,
-                         JS::Handle<JS::Value> aVal) {
-    // 1. Return CreateIterResultObject(value, true).
-    JS::Rooted<JS::Value> dict(aCx);
-    iterator_utils::DictReturn(aCx, &dict, true, aVal, aRv);
-    return Promise::Resolve(aGlobalObject, aCx, dict, aRv);
-  };
-
-  // 14. Let onFulfilled be CreateBuiltinFunction(fulfillSteps, « »).
-  // 15. Perform PerformPromiseThen(returnStepsPromise, onFulfilled, undefined,
-  // returnPromiseCapability).
-  Result<RefPtr<Promise>, nsresult> returnPromise =
-      returnStepsPromise->ThenWithCycleCollectedArgsJS(
-          onFullFilled, std::make_tuple(std::move(globalObject)),
-          std::make_tuple(aValue));
-
-  // 16. Return returnPromiseCapability.[[Promise]].
-  return PromiseOrErr(std::move(returnPromise), aRv);
+  aPromise->MaybeResolve(dict);
 }
-
-}  // namespace binding_detail
-
+}  // namespace iterator_utils
 }  // namespace mozilla::dom
diff --git a/dom/bindings/IterableIterator.h b/dom/bindings/IterableIterator.h
index 2ca32750f3d6bbb4bf50de2ef1cbd66c616cbfeb..2534fbbecc88d382360e1895a7aa7fd0e1eb6b77 100644
--- a/dom/bindings/IterableIterator.h
+++ b/dom/bindings/IterableIterator.h
@@ -31,7 +31,6 @@
 #include "js/TypeDecls.h"
 #include "js/Value.h"
 #include "nsISupports.h"
-#include "mozilla/AlreadyAddRefed.h"
 #include "mozilla/dom/IterableIteratorBinding.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/RootedDictionary.h"
@@ -40,19 +39,7 @@
 
 namespace mozilla::dom {
 
-namespace binding_details {
-
-// JS::MagicValue(END_OF_ITERATION) is the value we use for
-// https://webidl.spec.whatwg.org/#end-of-iteration. It shouldn't be returned to
-// JS, because AsyncIterableIteratorBase::NextSteps will detect it and will
-// return the result of CreateIterResultObject(undefined, true) instead
-// (discarding the magic value).
-static const JSWhyMagic END_OF_ITERATION = JS_GENERIC_MAGIC;
-
-}  // namespace binding_details
-
 namespace iterator_utils {
-
 void DictReturn(JSContext* aCx, JS::MutableHandle<JSObject*> aResult,
                 bool aDone, JS::Handle<JS::Value> aValue, ErrorResult& aRv);
 
@@ -60,23 +47,23 @@ void KeyAndValueReturn(JSContext* aCx, JS::Handle<JS::Value> aKey,
                        JS::Handle<JS::Value> aValue,
                        JS::MutableHandle<JSObject*> aResult, ErrorResult& aRv);
 
-inline void ResolvePromiseForFinished(Promise* aPromise) {
-  aPromise->MaybeResolve(JS::MagicValue(binding_details::END_OF_ITERATION));
-}
+void ResolvePromiseForFinished(JSContext* aCx, Promise* aPromise,
+                               ErrorResult& aRv);
 
-template <typename Key, typename Value>
-void ResolvePromiseWithKeyAndValue(Promise* aPromise, const Key& aKey,
-                                   const Value& aValue) {
-  aPromise->MaybeResolve(MakeTuple(aKey, aValue));
-}
+void ResolvePromiseWithKeyOrValue(JSContext* aCx, Promise* aPromise,
+                                  JS::Handle<JS::Value> aKeyOrValue,
+                                  ErrorResult& aRv);
 
+void ResolvePromiseWithKeyAndValue(JSContext* aCx, Promise* aPromise,
+                                   JS::Handle<JS::Value> aKey,
+                                   JS::Handle<JS::Value> aValue,
+                                   ErrorResult& aRv);
 }  // namespace iterator_utils
 
-class IterableIteratorBase {
+class IterableIteratorBase : public nsISupports {
  public:
-  NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(IterableIteratorBase)
-  NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(IterableIteratorBase)
-
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_CLASS(IterableIteratorBase)
   typedef enum { Keys = 0, Values, Entries } IteratorType;
 
   IterableIteratorBase() = default;
@@ -127,11 +114,20 @@ bool CallIterableGetter(JSContext* aCx,
 }
 
 template <typename T>
-class IterableIterator : public IterableIteratorBase {
+class IterableIterator final : public IterableIteratorBase {
  public:
-  IterableIterator(T* aIterableObj, IteratorType aIteratorType)
-      : mIterableObj(aIterableObj), mIteratorType(aIteratorType), mIndex(0) {
+  typedef bool (*WrapFunc)(JSContext* aCx, IterableIterator<T>* aObject,
+                           JS::Handle<JSObject*> aGivenProto,
+                           JS::MutableHandle<JSObject*> aReflector);
+
+  explicit IterableIterator(T* aIterableObj, IteratorType aIteratorType,
+                            WrapFunc aWrapFunc)
+      : mIterableObj(aIterableObj),
+        mIteratorType(aIteratorType),
+        mWrapFunc(aWrapFunc),
+        mIndex(0) {
     MOZ_ASSERT(mIterableObj);
+    MOZ_ASSERT(mWrapFunc);
   }
 
   bool GetKeyAtIndex(JSContext* aCx, uint32_t aIndex,
@@ -189,6 +185,11 @@ class IterableIterator : public IterableIteratorBase {
     ++mIndex;
   }
 
+  bool WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto,
+                  JS::MutableHandle<JSObject*> aObj) {
+    return (*mWrapFunc)(aCx, this, aGivenProto, aObj);
+  }
+
  protected:
   virtual ~IterableIterator() = default;
 
@@ -205,215 +206,85 @@ class IterableIterator : public IterableIteratorBase {
   RefPtr<T> mIterableObj;
   // Tells whether this is a key, value, or entries iterator.
   IteratorType mIteratorType;
+  // Function pointer to binding-type-specific Wrap() call for this iterator.
+  WrapFunc mWrapFunc;
   // Current index of iteration.
   uint32_t mIndex;
 };
 
-namespace binding_detail {
-
-class AsyncIterableNextImpl;
-class AsyncIterableReturnImpl;
-
-}  // namespace binding_detail
-
-class AsyncIterableIteratorBase : public IterableIteratorBase {
+template <typename T>
+class AsyncIterableIterator final : public IterableIteratorBase,
+                                    public SupportsWeakPtr {
  public:
-  IteratorType GetIteratorType() { return mIteratorType; }
+  typedef bool (*WrapFunc)(JSContext* aCx, AsyncIterableIterator<T>* aObject,
+                           JS::Handle<JSObject*> aGivenProto,
+                           JS::MutableHandle<JSObject*> aReflector);
+
+  explicit AsyncIterableIterator(T* aIterableObj, IteratorType aIteratorType,
+                                 WrapFunc aWrapFunc)
+      : mIterableObj(aIterableObj),
+        mIteratorType(aIteratorType),
+        mWrapFunc(aWrapFunc) {
+    MOZ_ASSERT(mIterableObj);
+    MOZ_ASSERT(mWrapFunc);
+  }
 
- protected:
-  explicit AsyncIterableIteratorBase(IteratorType aIteratorType)
-      : mIteratorType(aIteratorType) {}
+  void SetData(void* aData) { mData = aData; }
 
- private:
-  friend class binding_detail::AsyncIterableNextImpl;
-  friend class binding_detail::AsyncIterableReturnImpl;
+  void* GetData() { return mData; }
 
-  // 3.7.10.1. Default asynchronous iterator objects
-  // Target is in AsyncIterableIterator
-  // Kind
-  IteratorType mIteratorType;
-  // Ongoing promise
-  RefPtr<Promise> mOngoingPromise;
-  // Is finished
-  bool mIsFinished = false;
-};
-
-template <typename T>
-class AsyncIterableIterator : public AsyncIterableIteratorBase {
- private:
-  using IteratorData = typename T::IteratorData;
+  IteratorType GetIteratorType() { return mIteratorType; }
 
- public:
-  AsyncIterableIterator(T* aIterableObj, IteratorType aIteratorType)
-      : AsyncIterableIteratorBase(aIteratorType), mIterableObj(aIterableObj) {
-    MOZ_ASSERT(mIterableObj);
+  void Next(JSContext* aCx, JS::MutableHandle<JSObject*> aResult,
+            ErrorResult& aRv) {
+    RefPtr<Promise> promise = mIterableObj->GetNextPromise(aCx, this, aRv);
+    if (!promise) {
+      aRv.Throw(NS_ERROR_FAILURE);
+      return;
+    }
+    aResult.set(promise->PromiseObj());
   }
 
-  IteratorData& Data() { return mData; }
-
- protected:
-  // We'd prefer to use ImplCycleCollectionTraverse/ImplCycleCollectionUnlink on
-  // the iterator data, but unfortunately that doesn't work because it's
-  // dependent on the template parameter. Instead we detect if the data
-  // structure has Traverse and Unlink functions and call those.
-  template <typename Data>
-  auto TraverseData(Data& aData, nsCycleCollectionTraversalCallback& aCallback,
-                    int) -> decltype(aData.Traverse(aCallback)) {
-    return aData.Traverse(aCallback);
+  bool WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto,
+                  JS::MutableHandle<JSObject*> aObj) {
+    return (*mWrapFunc)(aCx, this, aGivenProto, aObj);
   }
-  template <typename Data>
-  void TraverseData(Data& aData, nsCycleCollectionTraversalCallback& aCallback,
-                    double) {}
 
-  template <typename Data>
-  auto UnlinkData(Data& aData, int) -> decltype(aData.Unlink()) {
-    return aData.Unlink();
+ protected:
+  virtual ~AsyncIterableIterator() {
+    // As long as iterable object does not hold strong ref to its iterators,
+    // iterators will not be added to CC graph, thus make sure
+    // DestroyAsyncIterator still take place.
+    if (mIterableObj) {
+      mIterableObj->DestroyAsyncIterator(this);
+    }
   }
-  template <typename Data>
-  void UnlinkData(Data& aData, double) {}
 
   // Since we're templated on a binding, we need to possibly CC it, but can't do
   // that through macros. So it happens here.
+  // DestroyAsyncIterator is expected to assume that its AsyncIterableIterator
+  // does not need access to mData anymore. AsyncIterator does not manage mData
+  // so it should be release and null out explicitly.
   void UnlinkHelper() final {
-    AsyncIterableIterator<T>* tmp = this;
-    NS_IMPL_CYCLE_COLLECTION_UNLINK(mIterableObj);
-    UnlinkData(tmp->mData, 0);
+    mIterableObj->DestroyAsyncIterator(this);
+    mIterableObj = nullptr;
   }
 
   virtual void TraverseHelper(nsCycleCollectionTraversalCallback& cb) override {
     AsyncIterableIterator<T>* tmp = this;
     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIterableObj);
-    TraverseData(tmp->mData, cb, 0);
   }
 
-  // 3.7.10.1. Default asynchronous iterator objects
-  // Target
+  // Binding Implementation object that we're iterating over.
   RefPtr<T> mIterableObj;
-  // Kind
-  // Ongoing promise
-  // Is finished
-  // See AsyncIterableIteratorBase
-
+  // Tells whether this is a key, value, or entries iterator.
+  IteratorType mIteratorType;
+  // Function pointer to binding-type-specific Wrap() call for this iterator.
+  WrapFunc mWrapFunc;
   // Opaque data of the backing object.
-  IteratorData mData;
+  void* mData{nullptr};
 };
 
-namespace binding_detail {
-
-template <typename T>
-using IterableIteratorWrapFunc =
-    bool (*)(JSContext* aCx, IterableIterator<T>* aObject,
-             JS::MutableHandle<JSObject*> aReflector);
-
-template <typename T, IterableIteratorWrapFunc<T> WrapFunc>
-class WrappableIterableIterator final : public IterableIterator<T> {
- public:
-  using IterableIterator<T>::IterableIterator;
-
-  bool WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto,
-                  JS::MutableHandle<JSObject*> aObj) {
-    MOZ_ASSERT(!aGivenProto);
-    return (*WrapFunc)(aCx, this, aObj);
-  }
-};
-
-class AsyncIterableNextImpl {
- protected:
-  already_AddRefed<Promise> Next(JSContext* aCx,
-                                 AsyncIterableIteratorBase* aObject,
-                                 nsISupports* aGlobalObject, ErrorResult& aRv);
-  virtual already_AddRefed<Promise> GetNextResult(ErrorResult& aRv) = 0;
-
- private:
-  already_AddRefed<Promise> NextSteps(JSContext* aCx,
-                                      AsyncIterableIteratorBase* aObject,
-                                      nsIGlobalObject* aGlobalObject,
-                                      ErrorResult& aRv);
-};
-
-class AsyncIterableReturnImpl {
- protected:
-  already_AddRefed<Promise> Return(JSContext* aCx,
-                                   AsyncIterableIteratorBase* aObject,
-                                   nsISupports* aGlobalObject,
-                                   JS::Handle<JS::Value> aValue,
-                                   ErrorResult& aRv);
-  virtual already_AddRefed<Promise> GetReturnPromise(
-      JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv) = 0;
-
- private:
-  already_AddRefed<Promise> ReturnSteps(JSContext* aCx,
-                                        AsyncIterableIteratorBase* aObject,
-                                        nsIGlobalObject* aGlobalObject,
-                                        JS::Handle<JS::Value> aValue,
-                                        ErrorResult& aRv);
-};
-
-template <typename T>
-class AsyncIterableIteratorNoReturn : public AsyncIterableIterator<T>,
-                                      public AsyncIterableNextImpl {
- public:
-  using AsyncIterableIterator<T>::AsyncIterableIterator;
-
-  already_AddRefed<Promise> Next(JSContext* aCx, ErrorResult& aRv) {
-    return AsyncIterableNextImpl::Next(
-        aCx, this, this->mIterableObj->GetParentObject(), aRv);
-  }
-
- protected:
-  already_AddRefed<Promise> GetNextResult(ErrorResult& aRv) override {
-    return this->mIterableObj->GetNextIterationResult(
-        static_cast<AsyncIterableIterator<T>*>(this), aRv);
-  }
-};
-
-template <typename T>
-class AsyncIterableIteratorWithReturn : public AsyncIterableIteratorNoReturn<T>,
-                                        public AsyncIterableReturnImpl {
- public:
-  already_AddRefed<Promise> Return(JSContext* aCx, JS::Handle<JS::Value> aValue,
-                                   ErrorResult& aRv) {
-    return AsyncIterableReturnImpl::Return(
-        aCx, this, this->mIterableObj->GetParentObject(), aValue, aRv);
-  }
-
- protected:
-  using AsyncIterableIteratorNoReturn<T>::AsyncIterableIteratorNoReturn;
-
-  already_AddRefed<Promise> GetReturnPromise(JSContext* aCx,
-                                             JS::Handle<JS::Value> aValue,
-                                             ErrorResult& aRv) override {
-    return this->mIterableObj->IteratorReturn(
-        aCx, static_cast<AsyncIterableIterator<T>*>(this), aValue, aRv);
-  }
-};
-
-template <typename T, bool NeedReturnMethod>
-using AsyncIterableIteratorNative =
-    std::conditional_t<NeedReturnMethod, AsyncIterableIteratorWithReturn<T>,
-                       AsyncIterableIteratorNoReturn<T>>;
-
-template <typename T, bool NeedReturnMethod>
-using AsyncIterableIteratorWrapFunc = bool (*)(
-    JSContext* aCx, AsyncIterableIteratorNative<T, NeedReturnMethod>* aObject,
-    JS::MutableHandle<JSObject*> aReflector);
-
-template <typename T, bool NeedReturnMethod,
-          AsyncIterableIteratorWrapFunc<T, NeedReturnMethod> WrapFunc,
-          typename Base = AsyncIterableIteratorNative<T, NeedReturnMethod>>
-class WrappableAsyncIterableIterator final : public Base {
- public:
-  using Base::Base;
-
-  bool WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto,
-                  JS::MutableHandle<JSObject*> aObj) {
-    MOZ_ASSERT(!aGivenProto);
-    return (*WrapFunc)(aCx, this, aObj);
-  }
-};
-
-}  // namespace binding_detail
-
 }  // namespace mozilla::dom
 
 #endif  // mozilla_dom_IterableIterator_h
diff --git a/dom/bindings/ToJSValue.h b/dom/bindings/ToJSValue.h
index 4655993578b9d1edfb3794c4ed9ca240e0e39648..21173051739996dc89fb75824d3b89a756204937 100644
--- a/dom/bindings/ToJSValue.h
+++ b/dom/bindings/ToJSValue.h
@@ -406,34 +406,6 @@ template <typename T>
   return true;
 }
 
-// Accept tuple of other things we accept. The result will be a JS array object.
-template <typename... Elements>
-[[nodiscard]] bool ToJSValue(JSContext* aCx,
-                             const Tuple<Elements...>& aArguments,
-                             JS::MutableHandle<JS::Value> aValue) {
-  // Make sure we're called in a compartment
-  MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
-
-  JS::RootedVector<JS::Value> v(aCx);
-  if (!v.resize(sizeof...(Elements))) {
-    return false;
-  }
-  bool ok = true;
-  size_t i = 0;
-  ForEach(aArguments, [aCx, &ok, &v, &i](auto& aElem) {
-    ok = ok && ToJSValue(aCx, aElem, v[i++]);
-  });
-  if (!ok) {
-    return false;
-  }
-  JSObject* arrayObj = JS::NewArrayObject(aCx, v);
-  if (!arrayObj) {
-    return false;
-  }
-  aValue.setObject(*arrayObj);
-  return true;
-}
-
 // Accept records of other things we accept. N.B. This assumes that
 // keys are either UTF-8 or UTF-16-ish. See Bug 1706058.
 template <typename K, typename V>
diff --git a/dom/bindings/parser/WebIDL.py b/dom/bindings/parser/WebIDL.py
index ddfce6488bf10ba2f71240608bf6d0083dfbb89a..c6581ef2249c60aa5a10470028b81ff829fe9fec 100644
--- a/dom/bindings/parser/WebIDL.py
+++ b/dom/bindings/parser/WebIDL.py
@@ -8886,6 +8886,7 @@ class Parser(Tokenizer):
             if isinstance(p, IDLInterface):
                 interfaceStatements.append(p)
 
+        iterableIteratorIface = None
         for iface in interfaceStatements:
             iterable = None
             # We haven't run finish() on the interface yet, so we don't know
@@ -8901,46 +8902,13 @@ class Parser(Tokenizer):
                 def simpleExtendedAttr(str):
                     return IDLExtendedAttribute(iface.location, (str,))
 
-                if isinstance(iterable, IDLAsyncIterable):
-                    nextReturnType = IDLPromiseType(
-                        iterable.location, BuiltinTypes[IDLBuiltinType.Types.any]
-                    )
-                else:
-                    nextReturnType = BuiltinTypes[IDLBuiltinType.Types.object]
                 nextMethod = IDLMethod(
-                    iterable.location,
-                    IDLUnresolvedIdentifier(iterable.location, "next"),
-                    nextReturnType,
+                    iface.location,
+                    IDLUnresolvedIdentifier(iface.location, "next"),
+                    BuiltinTypes[IDLBuiltinType.Types.object],
                     [],
                 )
                 nextMethod.addExtendedAttributes([simpleExtendedAttr("Throws")])
-
-                methods = [nextMethod]
-
-                if iterable.getExtendedAttribute("GenerateReturnMethod"):
-                    assert isinstance(iterable, IDLAsyncIterable)
-
-                    returnMethod = IDLMethod(
-                        iterable.location,
-                        IDLUnresolvedIdentifier(iterable.location, "return"),
-                        IDLPromiseType(
-                            iterable.location, BuiltinTypes[IDLBuiltinType.Types.any]
-                        ),
-                        [
-                            IDLArgument(
-                                iterable.location,
-                                IDLUnresolvedIdentifier(
-                                    BuiltinLocation("<auto-generated-identifier>"),
-                                    "value",
-                                ),
-                                BuiltinTypes[IDLBuiltinType.Types.any],
-                                optional=True,
-                            ),
-                        ],
-                    )
-                    returnMethod.addExtendedAttributes([simpleExtendedAttr("Throws")])
-                    methods.append(returnMethod)
-
                 if iterable.isIterable():
                     itr_suffix = "Iterator"
                 else:
@@ -8957,7 +8925,7 @@ class Parser(Tokenizer):
                     self.globalScope(),
                     itr_ident,
                     None,
-                    methods,
+                    [nextMethod],
                     isKnownNonPartial=True,
                     classNameOverride=classNameOverride,
                 )
diff --git a/dom/bindings/test/TestInterfaceAsyncIterableDouble.cpp b/dom/bindings/test/TestInterfaceAsyncIterableDouble.cpp
index a0ec0d64f0914840c85f72ba61a253edd7986d36..c47bcb5aa299ba96d645d80dfe30f1a453cc206d 100644
--- a/dom/bindings/test/TestInterfaceAsyncIterableDouble.cpp
+++ b/dom/bindings/test/TestInterfaceAsyncIterableDouble.cpp
@@ -54,44 +54,73 @@ nsPIDOMWindowInner* TestInterfaceAsyncIterableDouble::GetParentObject() const {
   return mParent;
 }
 
-already_AddRefed<Promise>
-TestInterfaceAsyncIterableDouble::GetNextIterationResult(Iterator* aIterator,
-                                                         ErrorResult& aRv) {
+void TestInterfaceAsyncIterableDouble::InitAsyncIterator(Iterator* aIterator,
+                                                         ErrorResult& aError) {
+  UniquePtr<IteratorData> data(new IteratorData(0));
+  aIterator->SetData((void*)data.release());
+}
+
+void TestInterfaceAsyncIterableDouble::DestroyAsyncIterator(
+    Iterator* aIterator) {
+  auto* data = reinterpret_cast<IteratorData*>(aIterator->GetData());
+  delete data;
+}
+
+already_AddRefed<Promise> TestInterfaceAsyncIterableDouble::GetNextPromise(
+    JSContext* aCx, Iterator* aIterator, ErrorResult& aRv) {
   RefPtr<Promise> promise = Promise::Create(mParent->AsGlobal(), aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
-  NS_DispatchToMainThread(NewRunnableMethod<RefPtr<Iterator>, RefPtr<Promise>>(
-      "TestInterfaceAsyncIterableDouble::GetNextIterationResult", this,
-      &TestInterfaceAsyncIterableDouble::ResolvePromise, aIterator, promise));
+  auto* data = reinterpret_cast<IteratorData*>(aIterator->GetData());
+  data->mPromise = promise;
+
+  IterableIteratorBase::IteratorType type = aIterator->GetIteratorType();
+  NS_DispatchToMainThread(NS_NewRunnableFunction(
+      "TestInterfaceAsyncIterableDouble::GetNextPromise",
+      [data, type, self = RefPtr{this}] { self->ResolvePromise(data, type); }));
 
   return promise.forget();
 }
 
-void TestInterfaceAsyncIterableDouble::ResolvePromise(Iterator* aIterator,
-                                                      Promise* aPromise) {
-  IteratorData& data = aIterator->Data();
+void TestInterfaceAsyncIterableDouble::ResolvePromise(
+    IteratorData* aData, IterableIteratorBase::IteratorType aType) {
+  AutoJSAPI jsapi;
+  if (NS_WARN_IF(!jsapi.Init(mParent))) {
+    return;
+  }
+  JSContext* cx = jsapi.cx();
+  ErrorResult rv;
 
   // Test data: ['a', 'b'], ['c', 'd'], ['e', 'f']
-  uint32_t idx = data.mIndex;
+  uint32_t idx = aData->mIndex;
   if (idx >= mValues.Length()) {
-    iterator_utils::ResolvePromiseForFinished(aPromise);
+    iterator_utils::ResolvePromiseForFinished(cx, aData->mPromise, rv);
   } else {
-    switch (aIterator->GetIteratorType()) {
+    JS::Rooted<JS::Value> key(cx);
+    JS::Rooted<JS::Value> value(cx);
+    switch (aType) {
       case IterableIteratorBase::IteratorType::Keys:
-        aPromise->MaybeResolve(mValues[idx].first);
+        Unused << ToJSValue(cx, mValues[idx].first, &key);
+        iterator_utils::ResolvePromiseWithKeyOrValue(cx, aData->mPromise, key,
+                                                     rv);
         break;
       case IterableIteratorBase::IteratorType::Values:
-        aPromise->MaybeResolve(mValues[idx].second);
+        Unused << ToJSValue(cx, mValues[idx].second, &value);
+        iterator_utils::ResolvePromiseWithKeyOrValue(cx, aData->mPromise, value,
+                                                     rv);
         break;
       case IterableIteratorBase::IteratorType::Entries:
-        iterator_utils::ResolvePromiseWithKeyAndValue(
-            aPromise, mValues[idx].first, mValues[idx].second);
+        Unused << ToJSValue(cx, mValues[idx].first, &key);
+        Unused << ToJSValue(cx, mValues[idx].second, &value);
+        iterator_utils::ResolvePromiseWithKeyAndValue(cx, aData->mPromise, key,
+                                                      value, rv);
         break;
     }
 
-    data.mIndex++;
+    aData->mIndex++;
+    aData->mPromise = nullptr;
   }
 }
 
diff --git a/dom/bindings/test/TestInterfaceAsyncIterableDouble.h b/dom/bindings/test/TestInterfaceAsyncIterableDouble.h
index 4b458617dc677017a03f14a8d146777a183dcf3d..cbf9e19ad621a94bab52d170e7d8bba975d67be1 100644
--- a/dom/bindings/test/TestInterfaceAsyncIterableDouble.h
+++ b/dom/bindings/test/TestInterfaceAsyncIterableDouble.h
@@ -38,21 +38,27 @@ class TestInterfaceAsyncIterableDouble final : public nsISupports,
   static already_AddRefed<TestInterfaceAsyncIterableDouble> Constructor(
       const GlobalObject& aGlobal, ErrorResult& rv);
 
-  struct IteratorData {
-    uint32_t mIndex = 0;
-  };
-
   using Iterator = AsyncIterableIterator<TestInterfaceAsyncIterableDouble>;
-
-  void InitAsyncIteratorData(IteratorData& aData, Iterator::IteratorType aType,
-                             ErrorResult& aError) {}
-
-  already_AddRefed<Promise> GetNextIterationResult(Iterator* aIterator,
-                                                   ErrorResult& aRv);
+  void InitAsyncIterator(Iterator* aIterator, ErrorResult& aError);
+  void DestroyAsyncIterator(Iterator* aIterator);
+  already_AddRefed<Promise> GetNextPromise(JSContext* aCx, Iterator* aIterator,
+                                           ErrorResult& aRv);
 
  private:
+  struct IteratorData {
+    explicit IteratorData(int32_t aIndex) : mIndex(aIndex) {}
+    ~IteratorData() {
+      if (mPromise) {
+        mPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+        mPromise = nullptr;
+      }
+    }
+    RefPtr<Promise> mPromise;
+    uint32_t mIndex;
+  };
   virtual ~TestInterfaceAsyncIterableDouble() = default;
-  void ResolvePromise(Iterator* aIterator, Promise* aPromise);
+  void ResolvePromise(IteratorData* aData,
+                      IterableIteratorBase::IteratorType aType);
 
   nsCOMPtr<nsPIDOMWindowInner> mParent;
   nsTArray<std::pair<nsString, nsString>> mValues;
diff --git a/dom/bindings/test/TestInterfaceAsyncIterableDoubleUnion.cpp b/dom/bindings/test/TestInterfaceAsyncIterableDoubleUnion.cpp
index 300574d7d70f0c21c6384f3b733b31decf7be1b2..c001aa7da56dd38130e7684d206526cadb429ab2 100644
--- a/dom/bindings/test/TestInterfaceAsyncIterableDoubleUnion.cpp
+++ b/dom/bindings/test/TestInterfaceAsyncIterableDoubleUnion.cpp
@@ -60,46 +60,74 @@ nsPIDOMWindowInner* TestInterfaceAsyncIterableDoubleUnion::GetParentObject()
   return mParent;
 }
 
-already_AddRefed<Promise>
-TestInterfaceAsyncIterableDoubleUnion::GetNextIterationResult(
-    Iterator* aIterator, ErrorResult& aRv) {
+void TestInterfaceAsyncIterableDoubleUnion::InitAsyncIterator(
+    Iterator* aIterator, ErrorResult& aError) {
+  UniquePtr<IteratorData> data(new IteratorData(0));
+  aIterator->SetData((void*)data.release());
+}
+
+void TestInterfaceAsyncIterableDoubleUnion::DestroyAsyncIterator(
+    Iterator* aIterator) {
+  auto* data = reinterpret_cast<IteratorData*>(aIterator->GetData());
+  delete data;
+}
+
+already_AddRefed<Promise> TestInterfaceAsyncIterableDoubleUnion::GetNextPromise(
+    JSContext* aCx, Iterator* aIterator, ErrorResult& aRv) {
   RefPtr<Promise> promise = Promise::Create(mParent->AsGlobal(), aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
-  NS_DispatchToMainThread(NewRunnableMethod<RefPtr<Iterator>, RefPtr<Promise>>(
-      "TestInterfaceAsyncIterableDoubleUnion::GetNextIterationResult", this,
-      &TestInterfaceAsyncIterableDoubleUnion::ResolvePromise, aIterator,
-      promise));
+  auto* data = reinterpret_cast<IteratorData*>(aIterator->GetData());
+  data->mPromise = promise;
+
+  IterableIteratorBase::IteratorType type = aIterator->GetIteratorType();
+  NS_DispatchToMainThread(NS_NewRunnableFunction(
+      "TestInterfaceAsyncIterableDoubleUnion::GetNextPromise",
+      [data, type, self = RefPtr{this}] { self->ResolvePromise(data, type); }));
 
   return promise.forget();
 }
 
-void TestInterfaceAsyncIterableDoubleUnion::ResolvePromise(Iterator* aIterator,
-                                                           Promise* aPromise) {
-  IteratorData& data = aIterator->Data();
+void TestInterfaceAsyncIterableDoubleUnion::ResolvePromise(
+    IteratorData* aData, IterableIteratorBase::IteratorType aType) {
+  AutoJSAPI jsapi;
+  if (NS_WARN_IF(!jsapi.Init(mParent))) {
+    return;
+  }
+  JSContext* cx = jsapi.cx();
+  ErrorResult rv;
 
   // Test data:
   // [long, 1], [string, "a"]
-  uint32_t idx = data.mIndex;
+  uint32_t idx = aData->mIndex;
   if (idx >= mValues.Length()) {
-    iterator_utils::ResolvePromiseForFinished(aPromise);
+    iterator_utils::ResolvePromiseForFinished(cx, aData->mPromise, rv);
   } else {
-    switch (aIterator->GetIteratorType()) {
+    JS::Rooted<JS::Value> key(cx);
+    JS::Rooted<JS::Value> value(cx);
+    switch (aType) {
       case IterableIteratorBase::IteratorType::Keys:
-        aPromise->MaybeResolve(mValues[idx].first);
+        Unused << ToJSValue(cx, mValues[idx].first, &key);
+        iterator_utils::ResolvePromiseWithKeyOrValue(cx, aData->mPromise, key,
+                                                     rv);
         break;
       case IterableIteratorBase::IteratorType::Values:
-        aPromise->MaybeResolve(mValues[idx].second);
+        Unused << ToJSValue(cx, mValues[idx].second, &value);
+        iterator_utils::ResolvePromiseWithKeyOrValue(cx, aData->mPromise, value,
+                                                     rv);
         break;
       case IterableIteratorBase::IteratorType::Entries:
-        iterator_utils::ResolvePromiseWithKeyAndValue(
-            aPromise, mValues[idx].first, mValues[idx].second);
+        Unused << ToJSValue(cx, mValues[idx].first, &key);
+        Unused << ToJSValue(cx, mValues[idx].second, &value);
+        iterator_utils::ResolvePromiseWithKeyAndValue(cx, aData->mPromise, key,
+                                                      value, rv);
         break;
     }
 
-    data.mIndex++;
+    aData->mIndex++;
+    aData->mPromise = nullptr;
   }
 }
 
diff --git a/dom/bindings/test/TestInterfaceAsyncIterableDoubleUnion.h b/dom/bindings/test/TestInterfaceAsyncIterableDoubleUnion.h
index 902c99b1a91e649df4dc13d05d07691b4d2d1716..d3e34f3ece0a40013639ef8ed59b1633ae4ac855 100644
--- a/dom/bindings/test/TestInterfaceAsyncIterableDoubleUnion.h
+++ b/dom/bindings/test/TestInterfaceAsyncIterableDoubleUnion.h
@@ -38,21 +38,28 @@ class TestInterfaceAsyncIterableDoubleUnion final : public nsISupports,
   static already_AddRefed<TestInterfaceAsyncIterableDoubleUnion> Constructor(
       const GlobalObject& aGlobal, ErrorResult& rv);
 
-  struct IteratorData {
-    uint32_t mIndex = 0;
-  };
-
   using Iterator = AsyncIterableIterator<TestInterfaceAsyncIterableDoubleUnion>;
-
-  void InitAsyncIteratorData(IteratorData& aData, Iterator::IteratorType aType,
-                             ErrorResult& aError) {}
-
-  already_AddRefed<Promise> GetNextIterationResult(Iterator* aIterator,
-                                                   ErrorResult& aRv);
+  void InitAsyncIterator(Iterator* aIterator, ErrorResult& aError);
+  void DestroyAsyncIterator(Iterator* aIterator);
+  already_AddRefed<Promise> GetNextPromise(JSContext* aCx, Iterator* aIterator,
+                                           ErrorResult& aRv);
 
  private:
+  struct IteratorData {
+    explicit IteratorData(int32_t aIndex) : mIndex(aIndex) {}
+    ~IteratorData() {
+      if (mPromise) {
+        mPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+        mPromise = nullptr;
+      }
+    }
+    RefPtr<Promise> mPromise;
+    uint32_t mIndex;
+  };
+
   virtual ~TestInterfaceAsyncIterableDoubleUnion() = default;
-  void ResolvePromise(Iterator* aIterator, Promise* aPromise);
+  void ResolvePromise(IteratorData* aData,
+                      IterableIteratorBase::IteratorType aType);
 
   nsCOMPtr<nsPIDOMWindowInner> mParent;
   nsTArray<std::pair<nsString, OwningStringOrLong>> mValues;
diff --git a/dom/bindings/test/TestInterfaceAsyncIterableSingle.cpp b/dom/bindings/test/TestInterfaceAsyncIterableSingle.cpp
index 5c77ef868250f32f2ae631f0e2dc892eae3f0860..2baa9286da4465b2c39dfe271814e5e37b6cf187 100644
--- a/dom/bindings/test/TestInterfaceAsyncIterableSingle.cpp
+++ b/dom/bindings/test/TestInterfaceAsyncIterableSingle.cpp
@@ -9,8 +9,6 @@
 #include "nsPIDOMWindow.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/IterableIterator.h"
-#include "mozilla/dom/Promise-inl.h"
-#include "nsThreadUtils.h"
 
 namespace mozilla::dom {
 
@@ -54,75 +52,68 @@ nsPIDOMWindowInner* TestInterfaceAsyncIterableSingle::GetParentObject() const {
   return mParent;
 }
 
-void TestInterfaceAsyncIterableSingle::InitAsyncIteratorData(
-    IteratorData& aData, Iterator::IteratorType aType, ErrorResult& aError) {
+void TestInterfaceAsyncIterableSingle::InitAsyncIterator(Iterator* aIterator,
+                                                         ErrorResult& aError) {
   if (mFailToInit) {
     aError.ThrowTypeError("Caller asked us to fail");
     return;
   }
 
-  // Nothing else to do.
-  MOZ_ASSERT(aData.mIndex == 0);
-  MOZ_ASSERT(aData.mMultiplier == 1);
+  UniquePtr<IteratorData> data(new IteratorData(0, 1));
+  aIterator->SetData((void*)data.release());
 }
 
-already_AddRefed<Promise>
-TestInterfaceAsyncIterableSingle::GetNextIterationResult(Iterator* aIterator,
-                                                         ErrorResult& aRv) {
-  return GetNextIterationResult(aIterator, aIterator->Data(), aRv);
+void TestInterfaceAsyncIterableSingle::DestroyAsyncIterator(
+    Iterator* aIterator) {
+  auto* data = reinterpret_cast<IteratorData*>(aIterator->GetData());
+  delete data;
 }
 
-already_AddRefed<Promise>
-TestInterfaceAsyncIterableSingle::GetNextIterationResult(
-    IterableIteratorBase* aIterator, IteratorData& aData, ErrorResult& aRv) {
+already_AddRefed<Promise> TestInterfaceAsyncIterableSingle::GetNextPromise(
+    JSContext* aCx, Iterator* aIterator, ErrorResult& aRv) {
+  return GetNextPromise(aCx, aIterator,
+                        reinterpret_cast<IteratorData*>(aIterator->GetData()),
+                        aRv);
+}
+
+already_AddRefed<Promise> TestInterfaceAsyncIterableSingle::GetNextPromise(
+    JSContext* aCx, IterableIteratorBase* aIterator, IteratorData* aData,
+    ErrorResult& aRv) {
   RefPtr<Promise> promise = Promise::Create(mParent->AsGlobal(), aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
+  aData->mPromise = promise;
 
-  nsCOMPtr<nsIRunnable> callResolvePromise =
-      NewRunnableMethod<RefPtr<IterableIteratorBase>, IteratorData&,
-                        RefPtr<Promise>>(
-          "TestInterfaceAsyncIterableSingle::GetNextIterationResult", this,
-          &TestInterfaceAsyncIterableSingle::ResolvePromise, aIterator, aData,
-          promise);
-  if (aData.mBlockingPromisesIndex < aData.mBlockingPromises.Length()) {
-    aData.mBlockingPromises[aData.mBlockingPromisesIndex]
-        ->AddCallbacksWithCycleCollectedArgs(
-            [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv,
-               nsIRunnable* aCallResolvePromise) {
-              NS_DispatchToMainThread(aCallResolvePromise);
-            },
-            [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv,
-               nsIRunnable* aCallResolvePromise) {},
-            std::move(callResolvePromise));
-    ++aData.mBlockingPromisesIndex;
-  } else {
-    NS_DispatchToMainThread(callResolvePromise);
-  }
+  NS_DispatchToMainThread(NS_NewRunnableFunction(
+      "TestInterfaceAsyncIterableSingle::GetNextPromise",
+      [iterator = RefPtr{aIterator}, aData, self = RefPtr{this}] {
+        self->ResolvePromise(iterator, aData);
+      }));
 
   return promise.forget();
 }
 
 void TestInterfaceAsyncIterableSingle::ResolvePromise(
-    IterableIteratorBase* aIterator, IteratorData& aData, Promise* aPromise) {
-  if (aData.mIndex >= 10) {
-    iterator_utils::ResolvePromiseForFinished(aPromise);
+    IterableIteratorBase* aIterator, IteratorData* aData) {
+  AutoJSAPI jsapi;
+  if (NS_WARN_IF(!jsapi.Init(mParent))) {
+    return;
+  }
+  JSContext* cx = jsapi.cx();
+  ErrorResult rv;
+  if (aData->mIndex >= 10) {
+    iterator_utils::ResolvePromiseForFinished(cx, aData->mPromise, rv);
   } else {
-    aPromise->MaybeResolve(int32_t(aData.mIndex * 9 % 7 * aData.mMultiplier));
+    JS::Rooted<JS::Value> value(cx);
+    Unused << ToJSValue(
+        cx, (int32_t)(aData->mIndex * 9 % 7 * aData->mMultiplier), &value);
+    iterator_utils::ResolvePromiseWithKeyOrValue(cx, aData->mPromise, value,
+                                                 rv);
 
-    aData.mIndex++;
+    aData->mIndex++;
   }
-}
-
-void TestInterfaceAsyncIterableSingle::IteratorData::Traverse(
-    nsCycleCollectionTraversalCallback& cb) {
-  TestInterfaceAsyncIterableSingle::IteratorData* tmp = this;
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBlockingPromises);
-}
-void TestInterfaceAsyncIterableSingle::IteratorData::Unlink() {
-  TestInterfaceAsyncIterableSingle::IteratorData* tmp = this;
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mBlockingPromises);
+  aData->mPromise = nullptr;
 }
 
 }  // namespace mozilla::dom
diff --git a/dom/bindings/test/TestInterfaceAsyncIterableSingle.h b/dom/bindings/test/TestInterfaceAsyncIterableSingle.h
index a8da2336d6cadd77166389aa3d096adbb215aa03..9b8dbed67e20e58bc3375562f723170723ae667c 100644
--- a/dom/bindings/test/TestInterfaceAsyncIterableSingle.h
+++ b/dom/bindings/test/TestInterfaceAsyncIterableSingle.h
@@ -41,31 +41,35 @@ class TestInterfaceAsyncIterableSingle : public nsISupports,
       const TestInterfaceAsyncIterableSingleOptions& aOptions, ErrorResult& rv);
 
   using Iterator = AsyncIterableIterator<TestInterfaceAsyncIterableSingle>;
-  already_AddRefed<Promise> GetNextIterationResult(Iterator* aIterator,
-                                                   ErrorResult& aRv);
+  void InitAsyncIterator(Iterator* aIterator, ErrorResult& aError);
+  void DestroyAsyncIterator(Iterator* aIterator);
+  already_AddRefed<Promise> GetNextPromise(JSContext* aCx, Iterator* aIterator,
+                                           ErrorResult& aRv);
 
+ protected:
   struct IteratorData {
-    void Traverse(nsCycleCollectionTraversalCallback& cb);
-    void Unlink();
-
-    uint32_t mIndex = 0;
-    uint32_t mMultiplier = 1;
-    Sequence<OwningNonNull<Promise>> mBlockingPromises;
-    size_t mBlockingPromisesIndex = 0;
+    IteratorData(int32_t aIndex, uint32_t aMultiplier)
+        : mIndex(aIndex), mMultiplier(aMultiplier) {}
+    ~IteratorData() {
+      if (mPromise) {
+        mPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+        mPromise = nullptr;
+      }
+    }
+    RefPtr<Promise> mPromise;
+    uint32_t mIndex;
+    uint32_t mMultiplier;
   };
 
-  void InitAsyncIteratorData(IteratorData& aData, Iterator::IteratorType aType,
-                             ErrorResult& aError);
-
- protected:
-  already_AddRefed<Promise> GetNextIterationResult(
-      IterableIteratorBase* aIterator, IteratorData& aData, ErrorResult& aRv);
+  already_AddRefed<Promise> GetNextPromise(JSContext* aCx,
+                                           IterableIteratorBase* aIterator,
+                                           IteratorData* aData,
+                                           ErrorResult& aRv);
 
   virtual ~TestInterfaceAsyncIterableSingle() = default;
 
  private:
-  void ResolvePromise(IterableIteratorBase* aIterator, IteratorData& aData,
-                      Promise* aPromise);
+  void ResolvePromise(IterableIteratorBase* aIterator, IteratorData* aData);
 
   nsCOMPtr<nsPIDOMWindowInner> mParent;
   bool mFailToInit;
diff --git a/dom/bindings/test/TestInterfaceAsyncIterableSingleWithArgs.cpp b/dom/bindings/test/TestInterfaceAsyncIterableSingleWithArgs.cpp
index 02e459fccf4cb46528730ae2c9c9d46fa021900a..aa8f61b094fe673cfc85816bdef383b1c5a18a60 100644
--- a/dom/bindings/test/TestInterfaceAsyncIterableSingleWithArgs.cpp
+++ b/dom/bindings/test/TestInterfaceAsyncIterableSingleWithArgs.cpp
@@ -5,39 +5,13 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/TestInterfaceAsyncIterableSingleWithArgs.h"
-#include "js/Value.h"
 #include "mozilla/dom/TestInterfaceJSMaplikeSetlikeIterableBinding.h"
 #include "nsPIDOMWindow.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/IterableIterator.h"
-#include "mozilla/dom/Promise-inl.h"
 
 namespace mozilla::dom {
 
-NS_IMPL_CYCLE_COLLECTION_CLASS(TestInterfaceAsyncIterableSingleWithArgs)
-
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(
-    TestInterfaceAsyncIterableSingleWithArgs, TestInterfaceAsyncIterableSingle)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(
-    TestInterfaceAsyncIterableSingleWithArgs, TestInterfaceAsyncIterableSingle)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
-
-NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(
-    TestInterfaceAsyncIterableSingleWithArgs, TestInterfaceAsyncIterableSingle)
-  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mReturnLastCalledWith)
-NS_IMPL_CYCLE_COLLECTION_TRACE_END
-
-NS_IMPL_ADDREF_INHERITED(TestInterfaceAsyncIterableSingleWithArgs,
-                         TestInterfaceAsyncIterableSingle)
-NS_IMPL_RELEASE_INHERITED(TestInterfaceAsyncIterableSingleWithArgs,
-                          TestInterfaceAsyncIterableSingle)
-
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
-    TestInterfaceAsyncIterableSingleWithArgs)
-NS_INTERFACE_MAP_END_INHERITING(TestInterfaceAsyncIterableSingle)
-
 // static
 already_AddRefed<TestInterfaceAsyncIterableSingleWithArgs>
 TestInterfaceAsyncIterableSingleWithArgs::Constructor(
@@ -60,34 +34,26 @@ JSObject* TestInterfaceAsyncIterableSingleWithArgs::WrapObject(
                                                                 aGivenProto);
 }
 
-void TestInterfaceAsyncIterableSingleWithArgs::InitAsyncIteratorData(
-    IteratorData& aData, Iterator::IteratorType aType,
-    const TestInterfaceAsyncIteratorOptions& aOptions, ErrorResult& aError) {
-  aData.mMultiplier = aOptions.mMultiplier;
-  aData.mBlockingPromises = aOptions.mBlockingPromises;
+void TestInterfaceAsyncIterableSingleWithArgs::InitAsyncIterator(
+    Iterator* aIterator, const TestInterfaceAsyncIteratorOptions& aOptions,
+    ErrorResult& aError) {
+  UniquePtr<IteratorData> data(new IteratorData(0, aOptions.mMultiplier));
+  aIterator->SetData((void*)data.release());
 }
 
-already_AddRefed<Promise>
-TestInterfaceAsyncIterableSingleWithArgs::GetNextIterationResult(
-    Iterator* aIterator, ErrorResult& aRv) {
-  return TestInterfaceAsyncIterableSingle::GetNextIterationResult(
-      aIterator, aIterator->Data(), aRv);
+void TestInterfaceAsyncIterableSingleWithArgs::DestroyAsyncIterator(
+    Iterator* aIterator) {
+  auto* data = reinterpret_cast<IteratorData*>(aIterator->GetData());
+  delete data;
 }
 
 already_AddRefed<Promise>
-TestInterfaceAsyncIterableSingleWithArgs::IteratorReturn(
-    JSContext* aCx, Iterator* aIterator, JS::Handle<JS::Value> aValue,
-    ErrorResult& aRv) {
-  ++mReturnCallCount;
-
-  RefPtr<Promise> promise = Promise::Create(GetParentObject()->AsGlobal(), aRv);
-  if (NS_WARN_IF(aRv.Failed())) {
-    return nullptr;
-  }
-
-  mReturnLastCalledWith = aValue;
-  promise->MaybeResolve(JS::UndefinedHandleValue);
-  return promise.forget();
+TestInterfaceAsyncIterableSingleWithArgs::GetNextPromise(JSContext* aCx,
+                                                         Iterator* aIterator,
+                                                         ErrorResult& aRv) {
+  return TestInterfaceAsyncIterableSingle::GetNextPromise(
+      aCx, aIterator, reinterpret_cast<IteratorData*>(aIterator->GetData()),
+      aRv);
 }
 
 }  // namespace mozilla::dom
diff --git a/dom/bindings/test/TestInterfaceAsyncIterableSingleWithArgs.h b/dom/bindings/test/TestInterfaceAsyncIterableSingleWithArgs.h
index a632f8b6eee7bd19a626f23aa8f88dcb91fa8f84..30d7b8455b6cad08fe0538aeb6b576eee3f66bb2 100644
--- a/dom/bindings/test/TestInterfaceAsyncIterableSingleWithArgs.h
+++ b/dom/bindings/test/TestInterfaceAsyncIterableSingleWithArgs.h
@@ -18,11 +18,6 @@ struct TestInterfaceAsyncIteratorOptions;
 class TestInterfaceAsyncIterableSingleWithArgs final
     : public TestInterfaceAsyncIterableSingle {
  public:
-  NS_DECL_ISUPPORTS_INHERITED
-  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(
-      TestInterfaceAsyncIterableSingleWithArgs,
-      TestInterfaceAsyncIterableSingle)
-
   using TestInterfaceAsyncIterableSingle::TestInterfaceAsyncIterableSingle;
   virtual JSObject* WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aGivenProto) override;
@@ -31,28 +26,13 @@ class TestInterfaceAsyncIterableSingleWithArgs final
 
   using Iterator =
       AsyncIterableIterator<TestInterfaceAsyncIterableSingleWithArgs>;
+  void InitAsyncIterator(Iterator* aIterator,
+                         const TestInterfaceAsyncIteratorOptions& aOptions,
+                         ErrorResult& aError);
+  void DestroyAsyncIterator(Iterator* aIterator);
 
-  void InitAsyncIteratorData(IteratorData& aData, Iterator::IteratorType aType,
-                             const TestInterfaceAsyncIteratorOptions& aOptions,
-                             ErrorResult& aError);
-
-  already_AddRefed<Promise> GetNextIterationResult(Iterator* aIterator,
-                                                   ErrorResult& aRv);
-  already_AddRefed<Promise> IteratorReturn(JSContext* aCx, Iterator* aIterator,
-                                           JS::Handle<JS::Value> aValue,
+  already_AddRefed<Promise> GetNextPromise(JSContext* aCx, Iterator* aIterator,
                                            ErrorResult& aRv);
-
-  uint32_t ReturnCallCount() { return mReturnCallCount; }
-  void GetReturnLastCalledWith(JSContext* aCx,
-                               JS::MutableHandle<JS::Value> aReturnCalledWith) {
-    aReturnCalledWith.set(mReturnLastCalledWith);
-  }
-
- private:
-  ~TestInterfaceAsyncIterableSingleWithArgs() = default;
-
-  JS::Heap<JS::Value> mReturnLastCalledWith;
-  uint32_t mReturnCallCount = 0;
 };
 
 }  // namespace mozilla::dom
diff --git a/dom/bindings/test/test_async_iterable.html b/dom/bindings/test/test_async_iterable.html
index 8dabf157d8e1c69deb9826c8b5fde3f87ec398b4..03af2097a5e72e19777630ab6182598ca04e6115 100644
--- a/dom/bindings/test/test_async_iterable.html
+++ b/dom/bindings/test/test_async_iterable.html
@@ -14,24 +14,17 @@ add_task(async function init() {
   await SpecialPowers.pushPrefEnv({set: [["dom.expose_test_interfaces", true]]});
 });
 
-const singleValues = Array(10).fill(0).map((_, i) => i * 9 % 7);
-
-async function check_single_result_values(values, multiplier = 1) {
-  dump(JSON.stringify(values));
-  is(values.length, 10, `AsyncIterableSingle: should return 10 elements`);
-  for (let i = 0; i < 10; i++) {
-    let expected = singleValues[i] * multiplier;
-    is(values[i], expected,
-      `AsyncIterableSingle: should be ${expected}, get ${values[i]}`);
-  }
-}
-
 async function check_single_result(itr, multiplier = 1) {
   let values = [];
   for await (let v of itr) {
     values.push(v);
   }
-  check_single_result_values(values, multiplier);
+  is(values.length, 10, `AsyncIterableSingle: should returns 10 elements`);
+  for (let i = 0; i < 10; i++) {
+    let expected = i * 9 % 7 * multiplier;
+    is(values[i], expected,
+      `AsyncIterableSingle: should be ${expected}, get ${values[i]}`);
+  }
 }
 
 async function test_data_single() {
@@ -65,104 +58,6 @@ async function test_data_single() {
 
   await check_single_result(itr, 1);
   await check_single_result(itr.values({ multiplier: 2 }), 2);
-
-  // eslint-disable-next-line no-undef
-  itr = new TestInterfaceAsyncIterableSingle();
-  let itrValues = itr.values();
-  let values = [];
-  for (let i = 0; i < 10; ++i) {
-    values.push(itrValues.next());
-  }
-  check_single_result_values(await Promise.all(values).then(v => v.map(w => w.value)));
-
-  // Test that there is only one ongoing promise at a time.
-  // Async iterables return a promise that is then resolved with the iterator
-  // value. We create an array of unresolved promises here, one promise for
-  // every result that we expect from the iterator. We pass that array of
-  // promises to the .value() method of the
-  // TestInterfaceAsyncIterableSingleWithArgs, and it will chain the resolving
-  // of each resulting iterator value on the corresponding promise from this
-  // array. We then resolve the promises in the array one by one in reverse
-  // order. This tries to make sure that the iterator always resolves the
-  // promises in the order of iteration.
-  let unblockers = [];
-  let blockingPromises = [];
-  for (let i = 0; i < 10; ++i) {
-    let unblocker;
-    let promise = new Promise((resolve, reject) => {
-      unblocker = resolve;
-    });
-    unblockers.push(unblocker);
-    blockingPromises.push(promise);
-  }
-
-  // eslint-disable-next-line no-undef
-  itr = new TestInterfaceAsyncIterableSingleWithArgs();
-  itrValues = itr.values({ blockingPromises });
-  values = [];
-  for (let i = 0; i < 10; ++i) {
-    values.push(itrValues.next());
-  }
-  unblockers.reverse();
-  for (let unblocker of unblockers) {
-    unblocker();
-  }
-
-  check_single_result_values(await Promise.all(values).then(v => v.map(w => w.value)));
-
-  // eslint-disable-next-line no-undef
-  itr = new TestInterfaceAsyncIterableSingleWithArgs();
-
-  let callCount = itr.returnCallCount;
-
-  let i = 0;
-  for await (let v of itr) {
-    if (++i > 1) {
-      break;
-    }
-    values.push(v);
-  }
-
-  is(itr.returnCallCount, callCount + 1,
-     `AsyncIterableSingle: breaking out of for-await-of loop should call "return"`);
-  is(itr.returnLastCalledWith, undefined,
-     `AsyncIterableSingleWithArgs: the asynchronous iterator return algorithm should be called with the argument that was passed in.`);
-
-  // eslint-disable-next-line no-undef
-  itr = new TestInterfaceAsyncIterableSingleWithArgs();
-
-  async function * yieldFromIterator () {
-    yield * itr
-  }
-
-  let yieldingIterator = yieldFromIterator();
-
-  let result = await yieldingIterator.next();
-  is(result.value, singleValues[0],
-     `AsyncIterableSingle: should be ${singleValues[0]}, get ${result.value}`);
-  result = await yieldingIterator.next();
-  is(result.value, singleValues[1],
-     `AsyncIterableSingle: should be ${singleValues[1]}, get ${result.value}`);
-
-  result = await yieldingIterator.return("abcd");
-  is(typeof result, "object",
-     `AsyncIterableSingleWithArgs: "return("abcd")" should return { done: true, value: "abcd" }`);
-  is(result.done, true,
-     `AsyncIterableSingleWithArgs: "return("abcd")" should return { done: true, value: "abcd" }`);
-  is(result.value, "abcd",
-     `AsyncIterableSingleWithArgs: "return("abcd")" should return { done: true, value: "abcd" }`);
-  is(itr.returnLastCalledWith, "abcd",
-     `AsyncIterableSingleWithArgs: the asynchronous iterator return algorithm should be called with the argument that was passed in.`);
-
-  result = await yieldingIterator.return("efgh");
-  is(typeof result, "object",
-     `AsyncIterableSingleWithArgs: "return("efgh")" should return { done: true, value: "efgh" }`);
-  is(result.done, true,
-     `AsyncIterableSingleWithArgs: "return("efgh")" should return { done: true, value: "efgh" }`);
-  is(result.value, "efgh",
-     `AsyncIterableSingleWithArgs: "return("efgh")" should return { done: true, value: "efgh" }`);
-  is(itr.returnLastCalledWith, "abcd",
-     `AsyncIterableSingleWithArgs: the asynchronous iterator return algorithm shouldn't be called if the iterator's 'is finished' flag is true already.`);
 }
 
 async function test_data_double() {
diff --git a/dom/broadcastchannel/BroadcastChannel.cpp b/dom/broadcastchannel/BroadcastChannel.cpp
index ee8581f769741ed65d2548e3cc27a783508c8cc7..c3607e90a8a2784f4c6ae119d9d446af89d16a9f 100644
--- a/dom/broadcastchannel/BroadcastChannel.cpp
+++ b/dom/broadcastchannel/BroadcastChannel.cpp
@@ -14,7 +14,6 @@
 #include "mozilla/dom/StructuredCloneHolder.h"
 #include "mozilla/dom/ipc/StructuredCloneData.h"
 #include "mozilla/dom/RefMessageBodyService.h"
-#include "mozilla/dom/RootedDictionary.h"
 #include "mozilla/dom/SharedMessageBody.h"
 #include "mozilla/dom/WorkerScope.h"
 #include "mozilla/dom/WorkerRef.h"
diff --git a/dom/cache/Cache.cpp b/dom/cache/Cache.cpp
index d9b94f6b14bc7320c6ef68ec034a3c4547e4dc3e..5da7a5cb42a9bc795134179d82aa10d613524174 100644
--- a/dom/cache/Cache.cpp
+++ b/dom/cache/Cache.cpp
@@ -13,7 +13,6 @@
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/PromiseNativeHandler.h"
 #include "mozilla/dom/Response.h"
-#include "mozilla/dom/RootedDictionary.h"
 #include "mozilla/dom/WorkerPrivate.h"
 #include "mozilla/dom/CacheBinding.h"
 #include "mozilla/dom/cache/AutoUtils.h"
diff --git a/dom/cache/CacheStorage.h b/dom/cache/CacheStorage.h
index 306c7cccd42d24430f23f99ad88d7cd9d6cff240..78cbafe37e27eb30b7e8643eb3d8177823bb7ea1 100644
--- a/dom/cache/CacheStorage.h
+++ b/dom/cache/CacheStorage.h
@@ -20,8 +20,6 @@ class nsIGlobalObject;
 namespace mozilla {
 
 class ErrorResult;
-enum UseCounter : int16_t;
-enum class UseCounterWorker : int16_t;
 
 namespace ipc {
 class PrincipalInfo;
diff --git a/dom/cache/TypeUtils.cpp b/dom/cache/TypeUtils.cpp
index 07fe4223f4b6874be1367f726e254f39fde94c74..029446ad18e72b9f993a04bb5c67c4e7991f7189 100644
--- a/dom/cache/TypeUtils.cpp
+++ b/dom/cache/TypeUtils.cpp
@@ -15,7 +15,6 @@
 #include "mozilla/dom/InternalRequest.h"
 #include "mozilla/dom/Request.h"
 #include "mozilla/dom/Response.h"
-#include "mozilla/dom/RootedDictionary.h"
 #include "mozilla/dom/cache/CacheTypes.h"
 #include "mozilla/dom/cache/ReadStream.h"
 #include "mozilla/ipc/BackgroundChild.h"
diff --git a/dom/fs/api/FileSystemDirectoryHandle.cpp b/dom/fs/api/FileSystemDirectoryHandle.cpp
index a2d4bfaad86e36b8bcac27c0b73e5d907d6b679c..81fb530f5f20885f0e3919348f8211e55cd0b8c0 100644
--- a/dom/fs/api/FileSystemDirectoryHandle.cpp
+++ b/dom/fs/api/FileSystemDirectoryHandle.cpp
@@ -12,6 +12,7 @@
 #include "js/TypeDecls.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/FileSystemDirectoryHandleBinding.h"
+#include "mozilla/dom/FileSystemDirectoryIterator.h"
 #include "mozilla/dom/FileSystemHandleBinding.h"
 #include "mozilla/dom/FileSystemManager.h"
 #include "mozilla/dom/PFileSystemManager.h"
@@ -50,15 +51,27 @@ FileSystemHandleKind FileSystemDirectoryHandle::Kind() const {
   return FileSystemHandleKind::Directory;
 }
 
-void FileSystemDirectoryHandle::InitAsyncIteratorData(
-    IteratorData& aData, iterator_t::IteratorType aType, ErrorResult& aError) {
-  aData.mImpl =
-      fs::FileSystemDirectoryIteratorFactory::Create(mMetadata, aType);
+void FileSystemDirectoryHandle::InitAsyncIterator(
+    FileSystemDirectoryHandle::iterator_t* aIterator, ErrorResult& aError) {
+  aIterator->SetData(
+      static_cast<void*>(fs::FileSystemDirectoryIteratorFactory::Create(
+                             mMetadata, aIterator->GetIteratorType())
+                             .release()));
 }
 
-already_AddRefed<Promise> FileSystemDirectoryHandle::GetNextIterationResult(
-    FileSystemDirectoryHandle::iterator_t* aIterator, ErrorResult& aError) {
-  return aIterator->Data().mImpl->Next(mGlobal, mManager, aError);
+void FileSystemDirectoryHandle::DestroyAsyncIterator(
+    FileSystemDirectoryHandle::iterator_t* aIterator) {
+  auto* it =
+      static_cast<FileSystemDirectoryIterator::Impl*>(aIterator->GetData());
+  delete it;
+  aIterator->SetData(nullptr);
+}
+
+already_AddRefed<Promise> FileSystemDirectoryHandle::GetNextPromise(
+    JSContext* /* aCx */, FileSystemDirectoryHandle::iterator_t* aIterator,
+    ErrorResult& aError) {
+  return static_cast<FileSystemDirectoryIterator::Impl*>(aIterator->GetData())
+      ->Next(mGlobal, mManager, aError);
 }
 
 already_AddRefed<Promise> FileSystemDirectoryHandle::GetFileHandle(
diff --git a/dom/fs/api/FileSystemDirectoryHandle.h b/dom/fs/api/FileSystemDirectoryHandle.h
index 36ac7455c55be231559ff0ec4a8fa10d5af0d081..87b0695fd3ce1809e86a0713ee502461bd2e1120 100644
--- a/dom/fs/api/FileSystemDirectoryHandle.h
+++ b/dom/fs/api/FileSystemDirectoryHandle.h
@@ -17,6 +17,7 @@ class ErrorResult;
 
 namespace dom {
 
+class FileSystemDirectoryIterator;
 struct FileSystemGetFileOptions;
 struct FileSystemGetDirectoryOptions;
 struct FileSystemRemoveOptions;
@@ -45,16 +46,13 @@ class FileSystemDirectoryHandle final : public FileSystemHandle {
   // WebIDL Interface
   FileSystemHandleKind Kind() const override;
 
-  struct IteratorData {
-    UniquePtr<FileSystemDirectoryIterator::Impl> mImpl;
-  };
+  void InitAsyncIterator(iterator_t* aIterator, ErrorResult& aError);
 
-  void InitAsyncIteratorData(IteratorData& aData,
-                             iterator_t::IteratorType aType,
-                             ErrorResult& aError);
+  void DestroyAsyncIterator(iterator_t* aIterator);
 
-  [[nodiscard]] already_AddRefed<Promise> GetNextIterationResult(
-      iterator_t* aIterator, ErrorResult& aError);
+  [[nodiscard]] already_AddRefed<Promise> GetNextPromise(JSContext* aCx,
+                                                         iterator_t* aIterator,
+                                                         ErrorResult& aError);
 
   already_AddRefed<Promise> GetFileHandle(
       const nsAString& aName, const FileSystemGetFileOptions& aOptions,
diff --git a/dom/fs/child/FileSystemDirectoryIteratorFactory.cpp b/dom/fs/child/FileSystemDirectoryIteratorFactory.cpp
index ce2c9ac062f82e7d2c0ec52f5322e5dfd7cbaf3c..573b47c3f836aba8e0454eff077b1cf6167ca91b 100644
--- a/dom/fs/child/FileSystemDirectoryIteratorFactory.cpp
+++ b/dom/fs/child/FileSystemDirectoryIteratorFactory.cpp
@@ -22,23 +22,52 @@ namespace mozilla::dom::fs {
 
 namespace {
 
+inline JSContext* GetContext(const RefPtr<Promise>& aPromise) {
+  AutoJSAPI jsapi;
+  if (NS_WARN_IF(!jsapi.Init(aPromise->GetGlobalObject()))) {
+    return nullptr;
+  }
+  return jsapi.cx();
+}
+
 template <IterableIteratorBase::IteratorType Type>
 struct ValueResolver;
 
 template <>
 struct ValueResolver<IterableIteratorBase::Keys> {
-  void operator()(nsIGlobalObject* aGlobal, RefPtr<FileSystemManager>& aManager,
-                  const FileSystemEntryMetadata& aValue,
-                  const RefPtr<Promise>& aPromise) {
-    aPromise->MaybeResolve(aValue.entryName());
+  nsresult operator()(nsIGlobalObject* aGlobal,
+                      RefPtr<FileSystemManager>& aManager,
+                      const FileSystemEntryMetadata& aValue,
+                      const RefPtr<Promise>& aPromise, ErrorResult& aError) {
+    JSContext* cx = GetContext(aPromise);
+    if (!cx) {
+      return NS_ERROR_UNEXPECTED;
+    }
+
+    JS::Rooted<JS::Value> key(cx);
+
+    if (!ToJSValue(cx, aValue.entryName(), &key)) {
+      return NS_ERROR_INVALID_ARG;
+    }
+
+    iterator_utils::ResolvePromiseWithKeyOrValue(cx, aPromise.get(), key,
+                                                 aError);
+
+    return NS_OK;
   }
 };
 
 template <>
 struct ValueResolver<IterableIteratorBase::Values> {
-  void operator()(nsIGlobalObject* aGlobal, RefPtr<FileSystemManager>& aManager,
-                  const FileSystemEntryMetadata& aValue,
-                  const RefPtr<Promise>& aPromise) {
+  nsresult operator()(nsIGlobalObject* aGlobal,
+                      RefPtr<FileSystemManager>& aManager,
+                      const FileSystemEntryMetadata& aValue,
+                      const RefPtr<Promise>& aPromise, ErrorResult& aError) {
+    JSContext* cx = GetContext(aPromise);
+    if (!cx) {
+      return NS_ERROR_UNEXPECTED;
+    }
+
     RefPtr<FileSystemHandle> handle;
 
     if (aValue.directory()) {
@@ -47,15 +76,34 @@ struct ValueResolver<IterableIteratorBase::Values> {
       handle = new FileSystemFileHandle(aGlobal, aManager, aValue);
     }
 
-    aPromise->MaybeResolve(std::move(handle));
+    JS::Rooted<JS::Value> value(cx);
+    if (!ToJSValue(cx, handle, &value)) {
+      return NS_ERROR_INVALID_ARG;
+    }
+
+    iterator_utils::ResolvePromiseWithKeyOrValue(cx, aPromise.get(), value,
+                                                 aError);
+
+    return NS_OK;
   }
 };
 
 template <>
 struct ValueResolver<IterableIteratorBase::Entries> {
-  void operator()(nsIGlobalObject* aGlobal, RefPtr<FileSystemManager>& aManager,
-                  const FileSystemEntryMetadata& aValue,
-                  const RefPtr<Promise>& aPromise) {
+  nsresult operator()(nsIGlobalObject* aGlobal,
+                      RefPtr<FileSystemManager>& aManager,
+                      const FileSystemEntryMetadata& aValue,
+                      const RefPtr<Promise>& aPromise, ErrorResult& aError) {
+    JSContext* cx = GetContext(aPromise);
+    if (!cx) {
+      return NS_ERROR_UNEXPECTED;
+    }
+
+    JS::Rooted<JS::Value> key(cx);
+    if (!ToJSValue(cx, aValue.entryName(), &key)) {
+      return NS_ERROR_INVALID_ARG;
+    }
+
     RefPtr<FileSystemHandle> handle;
 
     if (aValue.directory()) {
@@ -64,8 +112,15 @@ struct ValueResolver<IterableIteratorBase::Entries> {
       handle = new FileSystemFileHandle(aGlobal, aManager, aValue);
     }
 
-    iterator_utils::ResolvePromiseWithKeyAndValue(aPromise, aValue.entryName(),
-                                                  handle);
+    JS::Rooted<JS::Value> value(cx);
+    if (!ToJSValue(cx, handle, &value)) {
+      return NS_ERROR_INVALID_ARG;
+    }
+
+    iterator_utils::ResolvePromiseWithKeyAndValue(cx, aPromise.get(), key,
+                                                  value, aError);
+
+    return NS_OK;
   }
 };
 
@@ -88,16 +143,27 @@ class DoubleBufferQueueImpl
   // XXX This doesn't have to be public
   void ResolveValue(nsIGlobalObject* aGlobal,
                     RefPtr<FileSystemManager>& aManager,
-                    const Maybe<DataType>& aValue, RefPtr<Promise> aPromise) {
+                    const Maybe<DataType>& aValue, RefPtr<Promise> aPromise,
+                    ErrorResult& aError) {
     MOZ_ASSERT(aPromise);
     MOZ_ASSERT(aPromise.get());
 
+    AutoJSAPI jsapi;
+    if (NS_WARN_IF(!jsapi.Init(aPromise->GetGlobalObject()))) {
+      aPromise->MaybeReject(NS_ERROR_DOM_UNKNOWN_ERR);
+      aError = NS_ERROR_DOM_UNKNOWN_ERR;
+      return;
+    }
+
+    JSContext* aCx = jsapi.cx();
+    MOZ_ASSERT(aCx);
+
     if (!aValue) {
-      iterator_utils::ResolvePromiseForFinished(aPromise);
+      iterator_utils::ResolvePromiseForFinished(aCx, aPromise.get(), aError);
       return;
     }
 
-    ValueResolver{}(aGlobal, aManager, *aValue, aPromise);
+    ValueResolver{}(aGlobal, aManager, *aValue, aPromise, aError);
   }
 
   already_AddRefed<Promise> Next(nsIGlobalObject* aGlobal,
@@ -108,7 +174,7 @@ class DoubleBufferQueueImpl
       return nullptr;
     }
 
-    next(aGlobal, aManager, promise);
+    next(aGlobal, aManager, promise, aError);
 
     return promise.forget();
   }
@@ -117,7 +183,7 @@ class DoubleBufferQueueImpl
 
  protected:
   void next(nsIGlobalObject* aGlobal, RefPtr<FileSystemManager>& aManager,
-            RefPtr<Promise> aResult) {
+            RefPtr<Promise> aResult, ErrorResult& aError) {
     MOZ_ASSERT(aResult);
 
     Maybe<DataType> rawValue;
@@ -129,7 +195,7 @@ class DoubleBufferQueueImpl
       ErrorResult rv;
       RefPtr<Promise> promise = Promise::Create(aGlobal, rv);
       if (rv.Failed()) {
-        aResult->MaybeReject(std::move(rv));
+        aResult->MaybeReject(NS_ERROR_DOM_UNKNOWN_ERR);
         return;
       }
 
@@ -159,7 +225,7 @@ class DoubleBufferQueueImpl
             }
 
             IgnoredErrorResult rv;
-            ResolveValue(global, manager, value, aResult);
+            ResolveValue(global, manager, value, aResult, rv);
           },
           [](nsresult aRv) {});
       promise->AppendNativeHandler(listener);
@@ -173,7 +239,7 @@ class DoubleBufferQueueImpl
 
     nextInternal(rawValue);
 
-    ResolveValue(aGlobal, aManager, rawValue, aResult);
+    ResolveValue(aGlobal, aManager, rawValue, aResult, aError);
   }
 
   bool nextInternal(Maybe<DataType>& aNext) {
diff --git a/dom/fs/test/gtest/api/TestFileSystemDirectoryHandle.cpp b/dom/fs/test/gtest/api/TestFileSystemDirectoryHandle.cpp
index 647f289bbd24da97ae50761ab87494f2d9c2ea67..6da71747c9a0a49d1f9fffafa89168bb574416b8 100644
--- a/dom/fs/test/gtest/api/TestFileSystemDirectoryHandle.cpp
+++ b/dom/fs/test/gtest/api/TestFileSystemDirectoryHandle.cpp
@@ -9,6 +9,7 @@
 #include "mozilla/UniquePtr.h"
 #include "mozilla/dom/FileSystemDirectoryHandle.h"
 #include "mozilla/dom/FileSystemDirectoryHandleBinding.h"
+#include "mozilla/dom/FileSystemDirectoryIterator.h"
 #include "mozilla/dom/FileSystemHandle.h"
 #include "mozilla/dom/FileSystemHandleBinding.h"
 #include "mozilla/dom/FileSystemManager.h"
@@ -29,6 +30,12 @@ class TestFileSystemDirectoryHandle : public ::testing::Test {
     mManager = MakeAndAddRef<FileSystemManager>(mGlobal, nullptr);
   }
 
+  FileSystemDirectoryHandle::iterator_t::WrapFunc GetWrapFunc() const {
+    return [](JSContext*, AsyncIterableIterator<FileSystemDirectoryHandle>*,
+              JS::Handle<JSObject*>,
+              JS::MutableHandle<JSObject*>) -> bool { return true; };
+  }
+
   nsIGlobalObject* mGlobal = GetGlobal();
   const IterableIteratorBase::IteratorType mIteratorType =
       IterableIteratorBase::IteratorType::Keys;
@@ -45,7 +52,7 @@ TEST_F(TestFileSystemDirectoryHandle, constructDirectoryHandleRefPointer) {
   ASSERT_TRUE(dirHandle);
 }
 
-TEST_F(TestFileSystemDirectoryHandle, initIterator) {
+TEST_F(TestFileSystemDirectoryHandle, initAndDestroyIterator) {
   RefPtr<FileSystemDirectoryHandle> dirHandle =
       MakeAndAddRef<FileSystemDirectoryHandle>(mGlobal, mManager, mMetadata,
                                                mRequestHandler.release());
@@ -53,19 +60,28 @@ TEST_F(TestFileSystemDirectoryHandle, initIterator) {
   ASSERT_TRUE(dirHandle);
 
   RefPtr<FileSystemDirectoryHandle::iterator_t> iterator =
-      new FileSystemDirectoryHandle::iterator_t(dirHandle.get(), mIteratorType);
+      new FileSystemDirectoryHandle::iterator_t(dirHandle.get(), mIteratorType,
+                                                GetWrapFunc());
   IgnoredErrorResult rv;
-  dirHandle->InitAsyncIteratorData(iterator->Data(), mIteratorType, rv);
-  ASSERT_TRUE(iterator->Data().mImpl);
+  dirHandle->InitAsyncIterator(iterator, rv);
+  ASSERT_TRUE(iterator->GetData());
+
+  dirHandle->DestroyAsyncIterator(iterator);
+  ASSERT_EQ(nullptr, iterator->GetData());
 }
 
 class MockFileSystemDirectoryIteratorImpl final
     : public FileSystemDirectoryIterator::Impl {
  public:
+  NS_INLINE_DECL_REFCOUNTING(MockFileSystemDirectoryIteratorImpl)
+
   MOCK_METHOD(already_AddRefed<Promise>, Next,
               (nsIGlobalObject * aGlobal, RefPtr<FileSystemManager>& aManager,
                ErrorResult& aError),
               (override));
+
+ protected:
+  ~MockFileSystemDirectoryIteratorImpl() = default;
 };
 
 TEST_F(TestFileSystemDirectoryHandle, isNextPromiseReturned) {
@@ -75,20 +91,22 @@ TEST_F(TestFileSystemDirectoryHandle, isNextPromiseReturned) {
 
   ASSERT_TRUE(dirHandle);
 
-  auto mockIter = MakeUnique<MockFileSystemDirectoryIteratorImpl>();
+  auto* mockIter = new MockFileSystemDirectoryIteratorImpl();
   IgnoredErrorResult error;
   EXPECT_CALL(*mockIter, Next(_, _, _))
       .WillOnce(::testing::Return(Promise::Create(mGlobal, error)));
 
   RefPtr<FileSystemDirectoryHandle::iterator_t> iterator =
-      MakeAndAddRef<FileSystemDirectoryHandle::iterator_t>(dirHandle.get(),
-                                                           mIteratorType);
-  iterator->Data().mImpl = std::move(mockIter);
+      MakeAndAddRef<FileSystemDirectoryHandle::iterator_t>(
+          dirHandle.get(), mIteratorType, GetWrapFunc());
+  iterator->SetData(static_cast<void*>(mockIter));
 
   IgnoredErrorResult rv;
   RefPtr<Promise> promise =
-      dirHandle->GetNextIterationResult(iterator.get(), rv);
+      dirHandle->GetNextPromise(nullptr, iterator.get(), rv);
   ASSERT_TRUE(promise);
+
+  dirHandle->DestroyAsyncIterator(iterator.get());
 }
 
 TEST_F(TestFileSystemDirectoryHandle, isHandleKindDirectory) {
diff --git a/dom/ipc/SharedMap.cpp b/dom/ipc/SharedMap.cpp
index 70e12784e925faa0bd97550df85af7342f97cb31..6bd83d822d35816b7806462598c3da8816f9d860 100644
--- a/dom/ipc/SharedMap.cpp
+++ b/dom/ipc/SharedMap.cpp
@@ -15,7 +15,6 @@
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/ContentProcessMessageManager.h"
 #include "mozilla/dom/IPCBlobUtils.h"
-#include "mozilla/dom/RootedDictionary.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/IOBuffers.h"
 #include "mozilla/ScriptPreloader.h"
diff --git a/dom/notification/Notification.cpp b/dom/notification/Notification.cpp
index 24100656591b13e37dc5383fb1ed7768d0f0bbfc..0717d4fb6f5ddeacdf3474064946b67cf812fc37 100644
--- a/dom/notification/Notification.cpp
+++ b/dom/notification/Notification.cpp
@@ -27,7 +27,6 @@
 #include "mozilla/dom/PermissionMessageUtils.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/PromiseWorkerProxy.h"
-#include "mozilla/dom/RootedDictionary.h"
 #include "mozilla/dom/ServiceWorkerGlobalScopeBinding.h"
 #include "mozilla/dom/ServiceWorkerManager.h"
 #include "mozilla/dom/ServiceWorkerUtils.h"
diff --git a/dom/promise/Promise-inl.h b/dom/promise/Promise-inl.h
index f472acbc166c7abdcb2f2f64555aeed61f50b5ac..b238db446c9f3a4da274c1d96db92dbc6b38681e 100644
--- a/dom/promise/Promise-inl.h
+++ b/dom/promise/Promise-inl.h
@@ -227,15 +227,13 @@ class NativeThenHandler<ResolveCallback, RejectCallback, std::tuple<Args...>,
 
 }  // anonymous namespace
 
-template <typename ResolveCallback, typename RejectCallback, typename... Args,
-          typename... JSArgs>
-Result<RefPtr<Promise>, nsresult>
-Promise::ThenCatchWithCycleCollectedArgsJSImpl(
+template <typename ResolveCallback, typename RejectCallback, typename... Args>
+Promise::ThenResult<ResolveCallback, Args...>
+Promise::ThenCatchWithCycleCollectedArgsImpl(
     Maybe<ResolveCallback>&& aOnResolve, Maybe<RejectCallback>&& aOnReject,
-    std::tuple<Args...>&& aArgs, std::tuple<JSArgs...>&& aJSArgs) {
-  using HandlerType =
-      NativeThenHandler<ResolveCallback, RejectCallback, std::tuple<Args...>,
-                        std::tuple<JSArgs...>>;
+    Args&&... aArgs) {
+  using HandlerType = NativeThenHandler<ResolveCallback, RejectCallback,
+                                        std::tuple<Args...>, std::tuple<>>;
 
   ErrorResult rv;
   RefPtr<Promise> promise = Promise::Create(GetParentObject(), rv);
@@ -243,11 +241,10 @@ Promise::ThenCatchWithCycleCollectedArgsJSImpl(
     return Err(rv.StealNSResult());
   }
 
-  auto* handler = new (fallible)
-      HandlerType(promise, std::forward<Maybe<ResolveCallback>>(aOnResolve),
-                  std::forward<Maybe<RejectCallback>>(aOnReject),
-                  std::forward<std::tuple<Args...>>(aArgs),
-                  std::forward<std::tuple<JSArgs...>>(aJSArgs));
+  auto* handler = new (fallible) HandlerType(
+      promise, std::forward<Maybe<ResolveCallback>>(aOnResolve),
+      std::forward<Maybe<RejectCallback>>(aOnReject),
+      std::make_tuple(std::forward<Args>(aArgs)...), std::make_tuple());
 
   if (!handler) {
     return Err(NS_ERROR_OUT_OF_MEMORY);
@@ -257,17 +254,6 @@ Promise::ThenCatchWithCycleCollectedArgsJSImpl(
   return std::move(promise);
 }
 
-template <typename ResolveCallback, typename RejectCallback, typename... Args>
-Promise::ThenResult<ResolveCallback, Args...>
-Promise::ThenCatchWithCycleCollectedArgsImpl(
-    Maybe<ResolveCallback>&& aOnResolve, Maybe<RejectCallback>&& aOnReject,
-    Args&&... aArgs) {
-  return ThenCatchWithCycleCollectedArgsJSImpl(
-      std::forward<Maybe<ResolveCallback>>(aOnResolve),
-      std::forward<Maybe<RejectCallback>>(aOnReject),
-      std::make_tuple(std::forward<Args>(aArgs)...), std::make_tuple());
-}
-
 template <typename ResolveCallback, typename RejectCallback, typename... Args>
 Promise::ThenResult<ResolveCallback, Args...>
 Promise::ThenCatchWithCycleCollectedArgs(ResolveCallback&& aOnResolve,
@@ -293,22 +279,28 @@ Promise::ThenResult<Callback, Args...> Promise::CatchWithCycleCollectedArgs(
                                              std::forward<Args>(aArgs)...);
 }
 
-template <typename ResolveCallback, typename RejectCallback, typename ArgsTuple,
-          typename JSArgsTuple>
-Result<RefPtr<Promise>, nsresult> Promise::ThenCatchWithCycleCollectedArgsJS(
-    ResolveCallback&& aOnResolve, RejectCallback&& aOnReject, ArgsTuple&& aArgs,
-    JSArgsTuple&& aJSArgs) {
-  return ThenCatchWithCycleCollectedArgsJSImpl(
-      Some(aOnResolve), Some(aOnReject), std::forward<ArgsTuple>(aArgs),
-      std::forward<JSArgsTuple>(aJSArgs));
-}
-
 template <typename Callback, typename ArgsTuple, typename JSArgsTuple>
 Result<RefPtr<Promise>, nsresult> Promise::ThenWithCycleCollectedArgsJS(
     Callback&& aOnResolve, ArgsTuple&& aArgs, JSArgsTuple&& aJSArgs) {
-  return ThenCatchWithCycleCollectedArgsJSImpl(
-      Some(aOnResolve), Maybe<Callback>(Nothing()),
+  using HandlerType =
+      NativeThenHandler<Callback, Callback, ArgsTuple, JSArgsTuple>;
+
+  ErrorResult rv;
+  RefPtr<Promise> promise = Promise::Create(GetParentObject(), rv);
+  if (rv.Failed()) {
+    return Err(rv.StealNSResult());
+  }
+
+  auto* handler = new (fallible) HandlerType(
+      promise, Some(aOnResolve), Maybe<Callback>(Nothing()),
       std::forward<ArgsTuple>(aArgs), std::forward<JSArgsTuple>(aJSArgs));
+
+  if (!handler) {
+    return Err(NS_ERROR_OUT_OF_MEMORY);
+  }
+
+  AppendNativeHandler(handler);
+  return std::move(promise);
 }
 
 template <typename ResolveCallback, typename RejectCallback, typename... Args>
diff --git a/dom/promise/Promise.h b/dom/promise/Promise.h
index 2c5d3e0f05379056a41410368dd3aa37a8fd5758..578a959c2ae605ff0b6205948f5633154e61cbf5 100644
--- a/dom/promise/Promise.h
+++ b/dom/promise/Promise.h
@@ -275,14 +275,9 @@ class Promise : public SupportsWeakPtr {
   ThenResult<Callback, Args...> CatchWithCycleCollectedArgs(
       Callback&& aOnReject, Args&&... aArgs);
 
-  // Same as Then[Catch]CycleCollectedArgs but the arguments are gathered into
-  // an `std::tuple` and there is an additional `std::tuple` for JS arguments
-  // after that.
-  template <typename ResolveCallback, typename RejectCallback,
-            typename ArgsTuple, typename JSArgsTuple>
-  Result<RefPtr<Promise>, nsresult> ThenCatchWithCycleCollectedArgsJS(
-      ResolveCallback&& aOnResolve, RejectCallback&& aOnReject,
-      ArgsTuple&& aArgs, JSArgsTuple&& aJSArgs);
+  // Same as ThenCycleCollectedArgs but the arguments are gathered into an
+  // `std::tuple` and there is an additional `std::tuple` for JS arguments after
+  // that.
   template <typename Callback, typename ArgsTuple, typename JSArgsTuple>
   Result<RefPtr<Promise>, nsresult> ThenWithCycleCollectedArgsJS(
       Callback&& aOnResolve, ArgsTuple&& aArgs, JSArgsTuple&& aJSArgs);
@@ -337,11 +332,6 @@ class Promise : public SupportsWeakPtr {
       nsIGlobalObject* aGlobal, ErrorResult& aRejectionError);
 
  protected:
-  template <typename ResolveCallback, typename RejectCallback, typename... Args,
-            typename... JSArgs>
-  Result<RefPtr<Promise>, nsresult> ThenCatchWithCycleCollectedArgsJSImpl(
-      Maybe<ResolveCallback>&& aOnResolve, Maybe<RejectCallback>&& aOnReject,
-      std::tuple<Args...>&& aArgs, std::tuple<JSArgs...>&& aJSArgs);
   template <typename ResolveCallback, typename RejectCallback, typename... Args>
   ThenResult<ResolveCallback, Args...> ThenCatchWithCycleCollectedArgsImpl(
       Maybe<ResolveCallback>&& aOnResolve, Maybe<RejectCallback>&& aOnReject,
diff --git a/dom/promise/gtest/ThenWithCycleCollectedArgsJS.cpp b/dom/promise/gtest/ThenWithCycleCollectedArgsJS.cpp
index 31b9fde3cac2c83d9df547a98b9e4d63c102ca31..b97de65d093ad3dea36d7fa293734469088015e0 100644
--- a/dom/promise/gtest/ThenWithCycleCollectedArgsJS.cpp
+++ b/dom/promise/gtest/ThenWithCycleCollectedArgsJS.cpp
@@ -78,81 +78,3 @@ TEST(ThenWithCycleCollectedArgsJS, Mixed)
       std::make_tuple(global, promise),
       std::make_tuple(JS::UndefinedHandleValue, JS::HandleObject(obj)));
 }
-
-TEST(ThenCatchWithCycleCollectedArgsJS, Empty)
-{
-  nsCOMPtr<nsIGlobalObject> global =
-      xpc::NativeGlobal(xpc::PrivilegedJunkScope());
-
-  RefPtr<Promise> promise = Promise::Create(global, IgnoreErrors());
-  auto result = promise->ThenCatchWithCycleCollectedArgsJS(
-      [](JSContext*, JS::Handle<JS::Value>, ErrorResult&) { return nullptr; },
-      [](JSContext*, JS::Handle<JS::Value>, ErrorResult&) { return nullptr; },
-      std::make_tuple(), std::make_tuple());
-}
-
-TEST(ThenCatchWithCycleCollectedArgsJS, nsCOMPtr)
-{
-  nsCOMPtr<nsIGlobalObject> global =
-      xpc::NativeGlobal(xpc::PrivilegedJunkScope());
-
-  RefPtr<Promise> promise = Promise::Create(global, IgnoreErrors());
-  auto result = promise->ThenCatchWithCycleCollectedArgsJS(
-      [](JSContext*, JS::Handle<JS::Value>, ErrorResult&, nsIGlobalObject*) {
-        return nullptr;
-      },
-      [](JSContext*, JS::Handle<JS::Value>, ErrorResult&, nsIGlobalObject*) {
-        return nullptr;
-      },
-      std::make_tuple(global), std::make_tuple());
-}
-
-TEST(ThenCatchWithCycleCollectedArgsJS, RefPtr)
-{
-  nsCOMPtr<nsIGlobalObject> global =
-      xpc::NativeGlobal(xpc::PrivilegedJunkScope());
-
-  RefPtr<Promise> promise = Promise::Create(global, IgnoreErrors());
-  auto result = promise->ThenCatchWithCycleCollectedArgsJS(
-      [](JSContext*, JS::Handle<JS::Value>, ErrorResult&, Promise*) {
-        return nullptr;
-      },
-      [](JSContext*, JS::Handle<JS::Value>, ErrorResult&, Promise*) {
-        return nullptr;
-      },
-      std::make_tuple(promise), std::make_tuple());
-}
-
-TEST(ThenCatchWithCycleCollectedArgsJS, RefPtrAndJSHandle)
-{
-  nsCOMPtr<nsIGlobalObject> global =
-      xpc::NativeGlobal(xpc::PrivilegedJunkScope());
-
-  RefPtr<Promise> promise = Promise::Create(global, IgnoreErrors());
-  auto result = promise->ThenCatchWithCycleCollectedArgsJS(
-      [](JSContext*, JS::Handle<JS::Value> v, ErrorResult&, Promise*,
-         JS::Handle<JS::Value>) { return nullptr; },
-      [](JSContext*, JS::Handle<JS::Value> v, ErrorResult&, Promise*,
-         JS::Handle<JS::Value>) { return nullptr; },
-      std::make_tuple(promise), std::make_tuple(JS::UndefinedHandleValue));
-}
-
-TEST(ThenCatchWithCycleCollectedArgsJS, Mixed)
-{
-  AutoJSAPI jsapi;
-  MOZ_ALWAYS_TRUE(jsapi.Init(xpc::PrivilegedJunkScope()));
-  JSContext* cx = jsapi.cx();
-  nsCOMPtr<nsIGlobalObject> global = xpc::CurrentNativeGlobal(cx);
-  JS::Rooted<JSObject*> obj(cx, JS_NewPlainObject(cx));
-
-  RefPtr<Promise> promise = Promise::Create(global, IgnoreErrors());
-  auto result = promise->ThenCatchWithCycleCollectedArgsJS(
-      [](JSContext*, JS::Handle<JS::Value>, ErrorResult&, nsIGlobalObject*,
-         Promise*, JS::Handle<JS::Value>,
-         JS::Handle<JSObject*>) { return nullptr; },
-      [](JSContext*, JS::Handle<JS::Value>, ErrorResult&, nsIGlobalObject*,
-         Promise*, JS::Handle<JS::Value>,
-         JS::Handle<JSObject*>) { return nullptr; },
-      std::make_tuple(global, promise),
-      std::make_tuple(JS::UndefinedHandleValue, JS::HandleObject(obj)));
-}
diff --git a/dom/serviceworkers/ServiceWorkerContainer.cpp b/dom/serviceworkers/ServiceWorkerContainer.cpp
index 2ce6b60d5b3fb3887c33c360633491300048439c..eeb5862f7680c8d17b859faf39f7723c5692be31 100644
--- a/dom/serviceworkers/ServiceWorkerContainer.cpp
+++ b/dom/serviceworkers/ServiceWorkerContainer.cpp
@@ -32,7 +32,6 @@
 #include "mozilla/dom/MessageEventBinding.h"
 #include "mozilla/dom/Navigator.h"
 #include "mozilla/dom/Promise.h"
-#include "mozilla/dom/RootedDictionary.h"
 #include "mozilla/dom/ServiceWorker.h"
 #include "mozilla/dom/ServiceWorkerContainerBinding.h"
 #include "mozilla/dom/ServiceWorkerManager.h"
diff --git a/dom/webidl/TestInterfaceJSMaplikeSetlikeIterable.webidl b/dom/webidl/TestInterfaceJSMaplikeSetlikeIterable.webidl
index 2363a8930b29aedcee79ca327e18706a13c493e6..3dadf84301b0c7c2694c247e6c897de474ec0d2f 100644
--- a/dom/webidl/TestInterfaceJSMaplikeSetlikeIterable.webidl
+++ b/dom/webidl/TestInterfaceJSMaplikeSetlikeIterable.webidl
@@ -111,7 +111,6 @@ interface TestInterfaceAsyncIterableSingle {
 
 dictionary TestInterfaceAsyncIteratorOptions {
   unsigned long multiplier = 1;
-  sequence<Promise<any>> blockingPromises = [];
 };
 
 [Pref="dom.expose_test_interfaces",
@@ -120,12 +119,7 @@ interface TestInterfaceAsyncIterableSingleWithArgs {
   [Throws]
   constructor();
 
-  [GenerateReturnMethod]
   async iterable<long>(optional TestInterfaceAsyncIteratorOptions options = {});
-
-  readonly attribute long returnCallCount;
-
-  readonly attribute any returnLastCalledWith;
 };
 
 [Pref="dom.expose_test_interfaces",
diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp
index e15afcdebdecb9ceed97cb539a511b34670b19c1..7ae860d4361ea41f3486c23a13e379c6513fbfc1 100644
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -49,7 +49,6 @@
 #include "mozilla/dom/PromiseDebugging.h"
 #include "mozilla/dom/RemoteWorkerChild.h"
 #include "mozilla/dom/RemoteWorkerService.h"
-#include "mozilla/dom/RootedDictionary.h"
 #include "mozilla/dom/TimeoutHandler.h"
 #include "mozilla/dom/WorkerBinding.h"
 #include "mozilla/dom/WorkerScope.h"
diff --git a/dom/worklet/Worklet.cpp b/dom/worklet/Worklet.cpp
index a73be7c4f4155881afd36a0f9693c2ba967ba636..36c4c66423d7c87ec765470a592e3afb56b67d05 100644
--- a/dom/worklet/Worklet.cpp
+++ b/dom/worklet/Worklet.cpp
@@ -16,7 +16,6 @@
 #include "mozilla/dom/PromiseNativeHandler.h"
 #include "mozilla/dom/Request.h"
 #include "mozilla/dom/Response.h"
-#include "mozilla/dom/RootedDictionary.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/ScriptLoader.h"
 #include "mozilla/dom/WorkletImpl.h"
diff --git a/mfbt/Tuple.h b/mfbt/Tuple.h
index 7fec059ef9679924a21441f8f5fb921f5ff5488b..85ff3f314e15d1b1f016b009bd417d8900386e34 100644
--- a/mfbt/Tuple.h
+++ b/mfbt/Tuple.h
@@ -116,7 +116,7 @@ struct TupleImpl<Index> {
   bool operator==(const TupleImpl<Index>& aOther) const { return true; }
 
   template <typename F>
-  void ForEach(const F& aFunc) const {}
+  void ForEach(const F& aFunc) {}
 };
 
 /*
@@ -449,7 +449,7 @@ inline void ForEach(Tuple<>& aTuple, const F& aFunc) {}
 
 template <typename F, typename... Elements>
 void ForEach(const Tuple<Elements...>& aTuple, const F& aFunc) {
-  aTuple.ForEach(aFunc);
+  aTuple.ForEach(aTuple, aFunc);
 }
 
 template <typename F, typename... Elements>
diff --git a/mfbt/tests/TestTuple.cpp b/mfbt/tests/TestTuple.cpp
index 4e20101972b4a749d377cb8946525ced93d9a66f..13f61048f1e0eed6a36def8aae3b06583d24214d 100644
--- a/mfbt/tests/TestTuple.cpp
+++ b/mfbt/tests/TestTuple.cpp
@@ -292,59 +292,6 @@ static bool TestTieIgnore() {
   return true;
 }
 
-template <typename... Elements, typename F>
-static void CheckForEachCall(const Tuple<Elements...>& aTuple,
-                             F&& CallForEach) {
-  constexpr std::size_t tupleSize = sizeof...(Elements);
-
-  Tuple<Elements...> checkResult;
-  std::size_t i = 0;
-  auto createResult = [&](auto& aElem) {
-    static_assert(tupleSize == 3,
-                  "Need to deal with more/less cases in the switch below");
-
-    CHECK(i < tupleSize);
-    switch (i) {
-      case 0:
-        Get<0>(checkResult) = aElem;
-        break;
-      case 1:
-        Get<1>(checkResult) = aElem;
-        break;
-      case 2:
-        Get<2>(checkResult) = aElem;
-        break;
-    }
-    ++i;
-  };
-
-  CallForEach(aTuple, createResult);
-
-  CHECK(checkResult == aTuple);
-}
-
-static bool TestForEach() {
-  Tuple<int, float, char> tuple = MakeTuple(42, 0.5f, 'c');
-
-  CheckForEachCall(
-      tuple, [](auto& aTuple, auto&& aLambda) { aTuple.ForEach(aLambda); });
-
-  CheckForEachCall(
-      tuple, [](auto& aTuple, auto&& aLambda) { ForEach(aTuple, aLambda); });
-
-  CheckForEachCall(tuple, [](auto& aTuple, auto&& aLambda) {
-    const decltype(aTuple)& constTuple = aTuple;
-    constTuple.ForEach(aLambda);
-  });
-
-  CheckForEachCall(tuple, [](auto& aTuple, auto&& aLambda) {
-    const decltype(aTuple)& constTuple = aTuple;
-    ForEach(constTuple, aLambda);
-  });
-
-  return true;
-}
-
 int main() {
   TestConstruction();
   TestConstructionFromMozPair();
@@ -357,6 +304,5 @@ int main() {
   TestTie();
   TestTieIgnore();
   TestTieMozPair();
-  TestForEach();
   return 0;
 }
diff --git a/toolkit/components/extensions/webrequest/StreamFilter.cpp b/toolkit/components/extensions/webrequest/StreamFilter.cpp
index aab284dda202849980dcf4f121157f3bcebed3da..c6d8361573de61af51a71bfb755ea23e5598f4c1 100644
--- a/toolkit/components/extensions/webrequest/StreamFilter.cpp
+++ b/toolkit/components/extensions/webrequest/StreamFilter.cpp
@@ -16,7 +16,6 @@
 #include "mozilla/extensions/StreamFilterParent.h"
 #include "mozilla/dom/AutoEntryScript.h"
 #include "mozilla/dom/ContentChild.h"
-#include "mozilla/dom/RootedDictionary.h"
 #include "mozilla/ipc/Endpoint.h"
 #include "nsContentUtils.h"
 #include "nsCycleCollectionParticipant.h"