From 3e507adf9910fe0f5af34aa8abd60ec10c75495c Mon Sep 17 00:00:00 2001 From: Peter Van der Beken Date: Thu, 22 Sep 2022 18:28:15 +0000 Subject: [PATCH] Bug 1788969 - Align async iterable code more closely with the spec. r=edgar Implement the common steps for the next method from https://webidl.spec.whatwg.org/#es-asynchronous-iterator-prototype-object in a base class, that all async iterable iterator objects inherit from. Natives that implement an async iterable only need to implement the "getting the next iteration result" part in their GetNextPromise method. This means they don't have to create the object according to "CreateIterResultObject" themselves, but can just create promise and often resolve it with a native value directly. We've switched to a special JS::Value to signal "end of iteration", but that's hidden inside the iterator_utils::ResolvePromiseForFinished helper. The WebIDL parser now uses the right return type for the generated "next" method, which means that any exceptions in the binding code itself will actually be correctly converted to a rejected promise instead of being rethrown. This also uses a class for the generated iterable iterator that's not exposed outside the binding code. No other code should create and/or wrap these anyway. Differential Revision: https://phabricator.services.mozilla.com/D156323 --- dom/bindings/Codegen.py | 18 +- dom/bindings/Configuration.py | 7 +- dom/bindings/IterableIterator.cpp | 203 ++++++++++++++---- dom/bindings/IterableIterator.h | 163 ++++++++++---- dom/bindings/ToJSValue.h | 28 +++ dom/bindings/parser/WebIDL.py | 8 +- .../test/TestInterfaceAsyncIterableDouble.cpp | 41 +--- .../test/TestInterfaceAsyncIterableDouble.h | 4 +- .../TestInterfaceAsyncIterableDoubleUnion.cpp | 43 ++-- .../TestInterfaceAsyncIterableDoubleUnion.h | 12 +- .../test/TestInterfaceAsyncIterableSingle.cpp | 58 ++--- .../test/TestInterfaceAsyncIterableSingle.h | 23 +- ...stInterfaceAsyncIterableSingleWithArgs.cpp | 10 +- ...TestInterfaceAsyncIterableSingleWithArgs.h | 2 +- dom/bindings/test/test_async_iterable.html | 61 +++++- dom/fs/api/FileSystemDirectoryHandle.cpp | 3 +- dom/fs/api/FileSystemDirectoryHandle.h | 3 +- .../FileSystemDirectoryIteratorFactory.cpp | 106 ++------- .../api/TestFileSystemDirectoryHandle.cpp | 16 +- ...stInterfaceJSMaplikeSetlikeIterable.webidl | 1 + 20 files changed, 489 insertions(+), 321 deletions(-) diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index a2c3ebcc1406..9d12396cc2d6 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -35,7 +35,6 @@ from Configuration import ( getAllTypes, Descriptor, MemberIsLegacyUnforgeable, - iteratorNativeType, ) AUTOGENERATED_WARNING_COMMENT = ( @@ -5305,7 +5304,7 @@ class CastableObjectUnwrapper: } """, exceptionCode=exceptionCode, - **self.substitution + **self.substitution, ) else: self.substitution["codeOnFailure"] = codeOnFailure @@ -5325,7 +5324,7 @@ class CastableObjectUnwrapper: } } """, - **substitution + **substitution, ) @@ -12812,7 +12811,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 @@ -12879,7 +12878,7 @@ class CGUnionStruct(CGThing): mValue.m${name}.Destroy(); mType = eUninitialized; """, - **vars + **vars, ) methods.append( ClassMethod( @@ -12897,7 +12896,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"] @@ -13244,7 +13243,7 @@ class CGUnionConversionStruct(CGThing): mUnion.mType = mUnion.e${name}; return mUnion.mValue.m${name}.SetValue(${ctorArgs}); """, - **vars + **vars, ) methods.append( ClassMethod( @@ -22229,14 +22228,17 @@ class CGIterableMethodGenerator(CGGeneric): return if descriptor.interface.isIterable(): + assert descriptor.interface.maplikeOrSetlikeOrIterable.isPairIterator() assert len(args) == 0 binding = descriptor.interface.identifier.name + "Iterator_Binding" + iterClass = f"mozilla::dom::binding_detail::WrappableIterableIterator<{descriptor.nativeType}>" init = "" else: assert descriptor.interface.isAsyncIterable() binding = descriptor.interface.identifier.name + "AsyncIterator_Binding" + iterClass = f"mozilla::dom::binding_detail::WrappableAsyncIterableIterator<{descriptor.nativeType}>" init = fill( """ { @@ -22260,7 +22262,7 @@ class CGIterableMethodGenerator(CGGeneric): &${binding}::Wrap)); $*{init} """, - iterClass=iteratorNativeType(descriptor), + iterClass=iterClass, itrMethod=methodName.title(), binding=binding, init=init, diff --git a/dom/bindings/Configuration.py b/dom/bindings/Configuration.py index dd317fecf6a7..67d9e18c1cec 100644 --- a/dom/bindings/Configuration.py +++ b/dom/bindings/Configuration.py @@ -757,6 +757,8 @@ 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"): @@ -1050,7 +1052,10 @@ def iteratorNativeType(descriptor): assert iterableDecl.isPairIterator() or descriptor.interface.isAsyncIterable() if descriptor.interface.isIterable(): return "mozilla::dom::IterableIterator<%s>" % descriptor.nativeType - return "mozilla::dom::AsyncIterableIterator<%s>" % descriptor.nativeType + return ( + "mozilla::dom::binding_detail::AsyncIterableIteratorNoReturn<%s>" + % descriptor.nativeType + ) def findInnermostType(t): diff --git a/dom/bindings/IterableIterator.cpp b/dom/bindings/IterableIterator.cpp index 88680ac1ecb9..ceb1851c8676 100644 --- a/dom/bindings/IterableIterator.cpp +++ b/dom/bindings/IterableIterator.cpp @@ -5,6 +5,7 @@ * 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 { @@ -31,7 +32,8 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IterableIteratorBase) NS_INTERFACE_MAP_END namespace iterator_utils { -void DictReturn(JSContext* aCx, JS::MutableHandle aResult, + +void DictReturn(JSContext* aCx, JS::MutableHandle aResult, bool aDone, JS::Handle aValue, ErrorResult& aRv) { RootedDictionary dict(aCx); dict.mDone = aDone; @@ -41,6 +43,16 @@ void DictReturn(JSContext* aCx, JS::MutableHandle aResult, aRv.Throw(NS_ERROR_FAILURE); return; } + aResult.set(dictValue); +} + +void DictReturn(JSContext* aCx, JS::MutableHandle aResult, + bool aDone, JS::Handle aValue, ErrorResult& aRv) { + JS::Rooted dictValue(aCx); + DictReturn(aCx, &dictValue, aDone, aValue, aRv); + if (aRv.Failed()) { + return; + } aResult.set(&dictValue.toObject()); } @@ -67,46 +79,153 @@ void KeyAndValueReturn(JSContext* aCx, JS::Handle aKey, aResult.set(&dictValue.toObject()); } -void ResolvePromiseForFinished(JSContext* aCx, Promise* aPromise) { - MOZ_ASSERT(aPromise); - - ErrorResult error; - JS::Rooted dict(aCx); - DictReturn(aCx, &dict, true, JS::UndefinedHandleValue, error); - if (error.Failed()) { - aPromise->MaybeReject(std::move(error)); - return; - } - aPromise->MaybeResolve(dict); -} - -void ResolvePromiseWithKeyOrValue(JSContext* aCx, Promise* aPromise, - JS::Handle aKeyOrValue) { - MOZ_ASSERT(aPromise); - - ErrorResult error; - JS::Rooted dict(aCx); - DictReturn(aCx, &dict, false, aKeyOrValue, error); - if (error.Failed()) { - aPromise->MaybeReject(std::move(error)); - return; - } - aPromise->MaybeResolve(dict); -} - -void ResolvePromiseWithKeyAndValue(JSContext* aCx, Promise* aPromise, - JS::Handle aKey, - JS::Handle aValue) { - MOZ_ASSERT(aPromise); - - ErrorResult error; - JS::Rooted dict(aCx); - KeyAndValueReturn(aCx, aKey, aValue, &dict, error); - if (error.Failed()) { - aPromise->MaybeReject(std::move(error)); - return; - } - aPromise->MaybeResolve(dict); -} } // namespace iterator_utils + +namespace binding_detail { + +static already_AddRefed PromiseOrErr( + Result, nsresult>&& aResult, ErrorResult& aError) { + if (aResult.isErr()) { + aError.Throw(aResult.unwrapErr()); + return nullptr; + } + + return aResult.unwrap().forget(); +} + +already_AddRefed 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 dict(aCx); + iterator_utils::DictReturn(aCx, &dict, true, JS::UndefinedHandleValue, aRv); + if (aRv.Failed()) { + return Promise::CreateRejectedWithErrorResult(aGlobalObject, aRv); + } + + // 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 nextPromise = GetNextPromise(aRv); + + // 5. Let fulfillSteps be the following steps, given next: + auto fulfillSteps = [](JSContext* aCx, JS::Handle aNext, + ErrorResult& aRv, + const RefPtr& aObject, + const nsCOMPtr& aGlobalObject) + -> already_AddRefed { + // 1. Set object’s ongoing promise to null. + aObject->mOngoingPromise = nullptr; + + // 2. If next is end of iteration, then: + JS::Rooted 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 aReason, + ErrorResult& aRv, + const RefPtr& aObject, + const nsCOMPtr& 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, 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 AsyncIterableNextImpl::Next( + JSContext* aCx, AsyncIterableIteratorBase* aObject, + nsISupports* aGlobalObject, ErrorResult& aRv) { + nsCOMPtr 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 aValue, + ErrorResult& aRv, + const RefPtr& aObject, + const nsCOMPtr& aGlobalObject) { + return NextSteps(aCx, aObject, aGlobalObject, aRv); + }; + + // 3. Perform PerformPromiseThen(ongoingPromise, onSettled, onSettled, + // afterOngoingPromiseCapability). + Result, 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); + } + + // 12. Return object’s ongoing promise. + return do_AddRef(aObject->mOngoingPromise); +} + +} // namespace binding_detail + } // namespace mozilla::dom diff --git a/dom/bindings/IterableIterator.h b/dom/bindings/IterableIterator.h index 6a5080deb3af..376d9d948fc6 100644 --- a/dom/bindings/IterableIterator.h +++ b/dom/bindings/IterableIterator.h @@ -31,6 +31,7 @@ #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" @@ -39,7 +40,19 @@ 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 aResult, bool aDone, JS::Handle aValue, ErrorResult& aRv); @@ -47,14 +60,16 @@ void KeyAndValueReturn(JSContext* aCx, JS::Handle aKey, JS::Handle aValue, JS::MutableHandle aResult, ErrorResult& aRv); -void ResolvePromiseForFinished(JSContext* aCx, Promise* aPromise); +inline void ResolvePromiseForFinished(Promise* aPromise) { + aPromise->MaybeResolve(JS::MagicValue(binding_details::END_OF_ITERATION)); +} -void ResolvePromiseWithKeyOrValue(JSContext* aCx, Promise* aPromise, - JS::Handle aKeyOrValue); +template +void ResolvePromiseWithKeyAndValue(Promise* aPromise, const Key& aKey, + const Value& aValue) { + aPromise->MaybeResolve(MakeTuple(aKey, aValue)); +} -void ResolvePromiseWithKeyAndValue(JSContext* aCx, Promise* aPromise, - JS::Handle aKey, - JS::Handle aValue); } // namespace iterator_utils class IterableIteratorBase : public nsISupports { @@ -209,44 +224,46 @@ class IterableIterator final : public IterableIteratorBase { uint32_t mIndex; }; -template -class AsyncIterableIterator final : public IterableIteratorBase, - public SupportsWeakPtr { - public: - typedef bool (*WrapFunc)(JSContext* aCx, AsyncIterableIterator* aObject, - JS::Handle aGivenProto, - JS::MutableHandle aReflector); +namespace binding_detail { - explicit AsyncIterableIterator(T* aIterableObj, IteratorType aIteratorType, - WrapFunc aWrapFunc) - : mIterableObj(aIterableObj), - mIteratorType(aIteratorType), - mWrapFunc(aWrapFunc) { +class AsyncIterableNextImpl; + +} // namespace binding_detail + +class AsyncIterableIteratorBase : public IterableIteratorBase { + public: + IteratorType GetIteratorType() { return mIteratorType; } + + protected: + explicit AsyncIterableIteratorBase(IteratorType aIteratorType) + : mIteratorType(aIteratorType) {} + + private: + friend class binding_detail::AsyncIterableNextImpl; + + // 3.7.10.1. Default asynchronous iterator objects + // Target is in AsyncIterableIterator + // Kind + IteratorType mIteratorType; + // Ongoing promise + RefPtr mOngoingPromise; + // Is finished + bool mIsFinished = false; +}; + +template +class AsyncIterableIterator : public AsyncIterableIteratorBase, + public SupportsWeakPtr { + public: + AsyncIterableIterator(T* aIterableObj, IteratorType aIteratorType) + : AsyncIterableIteratorBase(aIteratorType), mIterableObj(aIterableObj) { MOZ_ASSERT(mIterableObj); - MOZ_ASSERT(mWrapFunc); } void SetData(void* aData) { mData = aData; } void* GetData() { return mData; } - IteratorType GetIteratorType() { return mIteratorType; } - - void Next(JSContext* aCx, JS::MutableHandle aResult, - ErrorResult& aRv) { - RefPtr promise = mIterableObj->GetNextPromise(aCx, this, aRv); - if (!promise) { - aRv.Throw(NS_ERROR_FAILURE); - return; - } - aResult.set(promise->PromiseObj()); - } - - bool WrapObject(JSContext* aCx, JS::Handle aGivenProto, - JS::MutableHandle aObj) { - return (*mWrapFunc)(aCx, this, aGivenProto, aObj); - } - protected: virtual ~AsyncIterableIterator() { // As long as iterable object does not hold strong ref to its iterators, @@ -272,16 +289,80 @@ class AsyncIterableIterator final : public IterableIteratorBase, NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIterableObj); } - // Binding Implementation object that we're iterating over. + // 3.7.10.1. Default asynchronous iterator objects + // Target RefPtr 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; + // Kind + // Ongoing promise + // Is finished + // See AsyncIterableIteratorBase + // Opaque data of the backing object. void* mData{nullptr}; }; +namespace binding_detail { + +template +using WrappableIterableIterator = IterableIterator; + +class AsyncIterableNextImpl { + protected: + already_AddRefed Next(JSContext* aCx, + AsyncIterableIteratorBase* aObject, + nsISupports* aGlobalObject, ErrorResult& aRv); + virtual already_AddRefed GetNextPromise(ErrorResult& aRv) = 0; + + private: + already_AddRefed NextSteps(JSContext* aCx, + AsyncIterableIteratorBase* aObject, + nsIGlobalObject* aGlobalObject, + ErrorResult& aRv); +}; + +template +class AsyncIterableIteratorNoReturn : public AsyncIterableIterator, + public AsyncIterableNextImpl { + public: + using WrapFunc = bool (*)(JSContext* aCx, + AsyncIterableIteratorNoReturn* aObject, + JS::Handle aGivenProto, + JS::MutableHandle aReflector); + using AsyncIterableIteratorBase::IteratorType; + + AsyncIterableIteratorNoReturn(T* aIterableObj, IteratorType aIteratorType, + WrapFunc aWrapFunc) + : AsyncIterableIterator(aIterableObj, aIteratorType), + mWrapFunc(aWrapFunc) { + MOZ_ASSERT(mWrapFunc); + } + + bool WrapObject(JSContext* aCx, JS::Handle aGivenProto, + JS::MutableHandle aObj) { + return (*mWrapFunc)(aCx, this, aGivenProto, aObj); + } + + already_AddRefed Next(JSContext* aCx, ErrorResult& aRv) { + return AsyncIterableNextImpl::Next( + aCx, this, this->mIterableObj->GetParentObject(), aRv); + } + + protected: + already_AddRefed GetNextPromise(ErrorResult& aRv) override { + return this->mIterableObj->GetNextPromise( + static_cast*>(this), aRv); + } + + private: + // Function pointer to binding-type-specific Wrap() call for this iterator. + WrapFunc mWrapFunc; +}; + +template +using WrappableAsyncIterableIterator = AsyncIterableIteratorNoReturn; + +} // namespace binding_detail + } // namespace mozilla::dom #endif // mozilla_dom_IterableIterator_h diff --git a/dom/bindings/ToJSValue.h b/dom/bindings/ToJSValue.h index 211730517399..4655993578b9 100644 --- a/dom/bindings/ToJSValue.h +++ b/dom/bindings/ToJSValue.h @@ -406,6 +406,34 @@ template return true; } +// Accept tuple of other things we accept. The result will be a JS array object. +template +[[nodiscard]] bool ToJSValue(JSContext* aCx, + const Tuple& aArguments, + JS::MutableHandle aValue) { + // Make sure we're called in a compartment + MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx)); + + JS::RootedVector 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 diff --git a/dom/bindings/parser/WebIDL.py b/dom/bindings/parser/WebIDL.py index c6581ef2249c..4b46c43f02d3 100644 --- a/dom/bindings/parser/WebIDL.py +++ b/dom/bindings/parser/WebIDL.py @@ -8902,10 +8902,16 @@ 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( iface.location, IDLUnresolvedIdentifier(iface.location, "next"), - BuiltinTypes[IDLBuiltinType.Types.object], + nextReturnType, [], ) nextMethod.addExtendedAttributes([simpleExtendedAttr("Throws")]) diff --git a/dom/bindings/test/TestInterfaceAsyncIterableDouble.cpp b/dom/bindings/test/TestInterfaceAsyncIterableDouble.cpp index dd243ebf318e..495513a1b232 100644 --- a/dom/bindings/test/TestInterfaceAsyncIterableDouble.cpp +++ b/dom/bindings/test/TestInterfaceAsyncIterableDouble.cpp @@ -67,61 +67,42 @@ void TestInterfaceAsyncIterableDouble::DestroyAsyncIterator( } already_AddRefed TestInterfaceAsyncIterableDouble::GetNextPromise( - JSContext* aCx, Iterator* aIterator, ErrorResult& aRv) { + Iterator* aIterator, ErrorResult& aRv) { RefPtr promise = Promise::Create(mParent->AsGlobal(), aRv); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } - auto* data = reinterpret_cast(aIterator->GetData()); - data->mPromise = promise; - - NS_DispatchToMainThread(NS_NewRunnableFunction( - "TestInterfaceAsyncIterableDouble::GetNextPromise", - [self = RefPtr{this}, iterator = RefPtr{aIterator}] { - self->ResolvePromise(iterator); - })); + NS_DispatchToMainThread(NewRunnableMethod, RefPtr>( + "TestInterfaceAsyncIterableDouble::GetNextPromise", this, + &TestInterfaceAsyncIterableDouble::ResolvePromise, aIterator, promise)); return promise.forget(); } -void TestInterfaceAsyncIterableDouble::ResolvePromise(Iterator* aIterator) { +void TestInterfaceAsyncIterableDouble::ResolvePromise(Iterator* aIterator, + Promise* aPromise) { IteratorData* data = reinterpret_cast(aIterator->GetData()); - AutoJSAPI jsapi; - if (NS_WARN_IF(!jsapi.Init(mParent))) { - data->mPromise->MaybeRejectWithInvalidStateError( - "Couldn't get the global."); - return; - } - JSContext* cx = jsapi.cx(); - // Test data: ['a', 'b'], ['c', 'd'], ['e', 'f'] uint32_t idx = data->mIndex; if (idx >= mValues.Length()) { - iterator_utils::ResolvePromiseForFinished(cx, data->mPromise); + iterator_utils::ResolvePromiseForFinished(aPromise); } else { - JS::Rooted key(cx); - JS::Rooted value(cx); switch (aIterator->GetIteratorType()) { case IterableIteratorBase::IteratorType::Keys: - Unused << ToJSValue(cx, mValues[idx].first, &key); - iterator_utils::ResolvePromiseWithKeyOrValue(cx, data->mPromise, key); + aPromise->MaybeResolve(mValues[idx].first); break; case IterableIteratorBase::IteratorType::Values: - Unused << ToJSValue(cx, mValues[idx].second, &value); - iterator_utils::ResolvePromiseWithKeyOrValue(cx, data->mPromise, value); + aPromise->MaybeResolve(mValues[idx].second); break; case IterableIteratorBase::IteratorType::Entries: - Unused << ToJSValue(cx, mValues[idx].first, &key); - Unused << ToJSValue(cx, mValues[idx].second, &value); - iterator_utils::ResolvePromiseWithKeyAndValue(cx, data->mPromise, key, - value); + iterator_utils::ResolvePromiseWithKeyAndValue( + aPromise, mValues[idx].first, mValues[idx].second); break; } data->mIndex++; - data->mPromise = nullptr; } } diff --git a/dom/bindings/test/TestInterfaceAsyncIterableDouble.h b/dom/bindings/test/TestInterfaceAsyncIterableDouble.h index f9c0e07851ed..737e5fe09930 100644 --- a/dom/bindings/test/TestInterfaceAsyncIterableDouble.h +++ b/dom/bindings/test/TestInterfaceAsyncIterableDouble.h @@ -41,7 +41,7 @@ class TestInterfaceAsyncIterableDouble final : public nsISupports, using Iterator = AsyncIterableIterator; void InitAsyncIterator(Iterator* aIterator, ErrorResult& aError); void DestroyAsyncIterator(Iterator* aIterator); - already_AddRefed GetNextPromise(JSContext* aCx, Iterator* aIterator, + already_AddRefed GetNextPromise(Iterator* aIterator, ErrorResult& aRv); private: @@ -57,7 +57,7 @@ class TestInterfaceAsyncIterableDouble final : public nsISupports, uint32_t mIndex; }; virtual ~TestInterfaceAsyncIterableDouble() = default; - void ResolvePromise(Iterator* aIterator); + void ResolvePromise(Iterator* aIterator, Promise* aPromise); nsCOMPtr mParent; nsTArray> mValues; diff --git a/dom/bindings/test/TestInterfaceAsyncIterableDoubleUnion.cpp b/dom/bindings/test/TestInterfaceAsyncIterableDoubleUnion.cpp index 568e316c4247..0e2e761ba3fa 100644 --- a/dom/bindings/test/TestInterfaceAsyncIterableDoubleUnion.cpp +++ b/dom/bindings/test/TestInterfaceAsyncIterableDoubleUnion.cpp @@ -73,63 +73,44 @@ void TestInterfaceAsyncIterableDoubleUnion::DestroyAsyncIterator( } already_AddRefed TestInterfaceAsyncIterableDoubleUnion::GetNextPromise( - JSContext* aCx, Iterator* aIterator, ErrorResult& aRv) { + Iterator* aIterator, ErrorResult& aRv) { RefPtr promise = Promise::Create(mParent->AsGlobal(), aRv); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } - auto* data = reinterpret_cast(aIterator->GetData()); - data->mPromise = promise; - - NS_DispatchToMainThread(NS_NewRunnableFunction( - "TestInterfaceAsyncIterableDoubleUnion::GetNextPromise", - [self = RefPtr{this}, iterator = RefPtr{aIterator}] { - self->ResolvePromise(iterator); - })); + NS_DispatchToMainThread(NewRunnableMethod, RefPtr>( + "TestInterfaceAsyncIterableDoubleUnion::GetNextPromise", this, + &TestInterfaceAsyncIterableDoubleUnion::ResolvePromise, aIterator, + promise)); return promise.forget(); } -void TestInterfaceAsyncIterableDoubleUnion::ResolvePromise( - Iterator* aIterator) { +void TestInterfaceAsyncIterableDoubleUnion::ResolvePromise(Iterator* aIterator, + Promise* aPromise) { IteratorData* data = reinterpret_cast(aIterator->GetData()); - AutoJSAPI jsapi; - if (NS_WARN_IF(!jsapi.Init(mParent))) { - data->mPromise->MaybeRejectWithInvalidStateError( - "Couldn't get the global."); - return; - } - JSContext* cx = jsapi.cx(); - // Test data: // [long, 1], [string, "a"] uint32_t idx = data->mIndex; if (idx >= mValues.Length()) { - iterator_utils::ResolvePromiseForFinished(cx, data->mPromise); + iterator_utils::ResolvePromiseForFinished(aPromise); } else { - JS::Rooted key(cx); - JS::Rooted value(cx); switch (aIterator->GetIteratorType()) { case IterableIteratorBase::IteratorType::Keys: - Unused << ToJSValue(cx, mValues[idx].first, &key); - iterator_utils::ResolvePromiseWithKeyOrValue(cx, data->mPromise, key); + aPromise->MaybeResolve(mValues[idx].first); break; case IterableIteratorBase::IteratorType::Values: - Unused << ToJSValue(cx, mValues[idx].second, &value); - iterator_utils::ResolvePromiseWithKeyOrValue(cx, data->mPromise, value); + aPromise->MaybeResolve(mValues[idx].second); break; case IterableIteratorBase::IteratorType::Entries: - Unused << ToJSValue(cx, mValues[idx].first, &key); - Unused << ToJSValue(cx, mValues[idx].second, &value); - iterator_utils::ResolvePromiseWithKeyAndValue(cx, data->mPromise, key, - value); + iterator_utils::ResolvePromiseWithKeyAndValue( + aPromise, mValues[idx].first, mValues[idx].second); break; } data->mIndex++; - data->mPromise = nullptr; } } diff --git a/dom/bindings/test/TestInterfaceAsyncIterableDoubleUnion.h b/dom/bindings/test/TestInterfaceAsyncIterableDoubleUnion.h index 4b9cd44027ec..5bc4732be4ea 100644 --- a/dom/bindings/test/TestInterfaceAsyncIterableDoubleUnion.h +++ b/dom/bindings/test/TestInterfaceAsyncIterableDoubleUnion.h @@ -41,24 +41,18 @@ class TestInterfaceAsyncIterableDoubleUnion final : public nsISupports, using Iterator = AsyncIterableIterator; void InitAsyncIterator(Iterator* aIterator, ErrorResult& aError); void DestroyAsyncIterator(Iterator* aIterator); - already_AddRefed GetNextPromise(JSContext* aCx, Iterator* aIterator, + already_AddRefed GetNextPromise(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 mPromise; + uint32_t mIndex; }; virtual ~TestInterfaceAsyncIterableDoubleUnion() = default; - void ResolvePromise(Iterator* aIterator); + void ResolvePromise(Iterator* aIterator, Promise* aPromise); nsCOMPtr mParent; nsTArray> mValues; diff --git a/dom/bindings/test/TestInterfaceAsyncIterableSingle.cpp b/dom/bindings/test/TestInterfaceAsyncIterableSingle.cpp index ad9ffbe9190f..80d707d7daba 100644 --- a/dom/bindings/test/TestInterfaceAsyncIterableSingle.cpp +++ b/dom/bindings/test/TestInterfaceAsyncIterableSingle.cpp @@ -9,6 +9,8 @@ #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 { @@ -59,7 +61,7 @@ void TestInterfaceAsyncIterableSingle::InitAsyncIterator(Iterator* aIterator, return; } - UniquePtr data(new IteratorData(0, 1)); + UniquePtr data(new IteratorData()); aIterator->SetData((void*)data.release()); } @@ -70,51 +72,51 @@ void TestInterfaceAsyncIterableSingle::DestroyAsyncIterator( } already_AddRefed TestInterfaceAsyncIterableSingle::GetNextPromise( - JSContext* aCx, Iterator* aIterator, ErrorResult& aRv) { - return GetNextPromise(aCx, aIterator, - reinterpret_cast(aIterator->GetData()), - aRv); + Iterator* aIterator, ErrorResult& aRv) { + return GetNextPromise( + aIterator, reinterpret_cast(aIterator->GetData()), aRv); } already_AddRefed TestInterfaceAsyncIterableSingle::GetNextPromise( - JSContext* aCx, IterableIteratorBase* aIterator, IteratorData* aData, - ErrorResult& aRv) { + IterableIteratorBase* aIterator, IteratorData* aData, ErrorResult& aRv) { RefPtr promise = Promise::Create(mParent->AsGlobal(), aRv); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } - aData->mPromise = promise; - NS_DispatchToMainThread(NS_NewRunnableFunction( - "TestInterfaceAsyncIterableSingle::GetNextPromise", - [iterator = RefPtr{aIterator}, aData, self = RefPtr{this}] { - self->ResolvePromise(iterator, aData); - })); + nsCOMPtr callResolvePromise = + NewRunnableMethod, IteratorData*, + RefPtr>( + "TestInterfaceAsyncIterableSingle::GetNextPromise", this, + &TestInterfaceAsyncIterableSingle::ResolvePromise, aIterator, aData, + promise); + if (aData->mBlockingPromisesIndex < aData->mBlockingPromises.Length()) { + aData->mBlockingPromises[aData->mBlockingPromisesIndex] + ->AddCallbacksWithCycleCollectedArgs( + [](JSContext* aCx, JS::Handle aValue, ErrorResult& aRv, + nsIRunnable* aCallResolvePromise) { + NS_DispatchToMainThread(aCallResolvePromise); + }, + [](JSContext* aCx, JS::Handle aValue, ErrorResult& aRv, + nsIRunnable* aCallResolvePromise) {}, + std::move(callResolvePromise)); + ++aData->mBlockingPromisesIndex; + } else { + NS_DispatchToMainThread(callResolvePromise); + } return promise.forget(); } void TestInterfaceAsyncIterableSingle::ResolvePromise( - IterableIteratorBase* aIterator, IteratorData* aData) { - AutoJSAPI jsapi; - if (NS_WARN_IF(!jsapi.Init(mParent))) { - aData->mPromise->MaybeRejectWithInvalidStateError( - "Couldn't get the global."); - return; - } - JSContext* cx = jsapi.cx(); - + IterableIteratorBase* aIterator, IteratorData* aData, Promise* aPromise) { if (aData->mIndex >= 10) { - iterator_utils::ResolvePromiseForFinished(cx, aData->mPromise); + iterator_utils::ResolvePromiseForFinished(aPromise); } else { - JS::Rooted value(cx); - Unused << ToJSValue( - cx, (int32_t)(aData->mIndex * 9 % 7 * aData->mMultiplier), &value); - iterator_utils::ResolvePromiseWithKeyOrValue(cx, aData->mPromise, value); + aPromise->MaybeResolve(int32_t(aData->mIndex * 9 % 7 * aData->mMultiplier)); aData->mIndex++; } - aData->mPromise = nullptr; } } // namespace mozilla::dom diff --git a/dom/bindings/test/TestInterfaceAsyncIterableSingle.h b/dom/bindings/test/TestInterfaceAsyncIterableSingle.h index 9b8dbed67e20..7b861062d81d 100644 --- a/dom/bindings/test/TestInterfaceAsyncIterableSingle.h +++ b/dom/bindings/test/TestInterfaceAsyncIterableSingle.h @@ -43,33 +43,26 @@ class TestInterfaceAsyncIterableSingle : public nsISupports, using Iterator = AsyncIterableIterator; void InitAsyncIterator(Iterator* aIterator, ErrorResult& aError); void DestroyAsyncIterator(Iterator* aIterator); - already_AddRefed GetNextPromise(JSContext* aCx, Iterator* aIterator, + already_AddRefed GetNextPromise(Iterator* aIterator, ErrorResult& aRv); protected: struct IteratorData { - IteratorData(int32_t aIndex, uint32_t aMultiplier) - : mIndex(aIndex), mMultiplier(aMultiplier) {} - ~IteratorData() { - if (mPromise) { - mPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR); - mPromise = nullptr; - } - } - RefPtr mPromise; - uint32_t mIndex; - uint32_t mMultiplier; + uint32_t mIndex = 0; + uint32_t mMultiplier = 1; + Sequence> mBlockingPromises; + size_t mBlockingPromisesIndex = 0; }; - already_AddRefed GetNextPromise(JSContext* aCx, - IterableIteratorBase* aIterator, + already_AddRefed GetNextPromise(IterableIteratorBase* aIterator, IteratorData* aData, ErrorResult& aRv); virtual ~TestInterfaceAsyncIterableSingle() = default; private: - void ResolvePromise(IterableIteratorBase* aIterator, IteratorData* aData); + void ResolvePromise(IterableIteratorBase* aIterator, IteratorData* aData, + Promise* aPromise); nsCOMPtr mParent; bool mFailToInit; diff --git a/dom/bindings/test/TestInterfaceAsyncIterableSingleWithArgs.cpp b/dom/bindings/test/TestInterfaceAsyncIterableSingleWithArgs.cpp index aa8f61b094fe..6371df4627c2 100644 --- a/dom/bindings/test/TestInterfaceAsyncIterableSingleWithArgs.cpp +++ b/dom/bindings/test/TestInterfaceAsyncIterableSingleWithArgs.cpp @@ -9,6 +9,7 @@ #include "nsPIDOMWindow.h" #include "mozilla/dom/BindingUtils.h" #include "mozilla/dom/IterableIterator.h" +#include "mozilla/dom/Promise-inl.h" namespace mozilla::dom { @@ -37,7 +38,8 @@ JSObject* TestInterfaceAsyncIterableSingleWithArgs::WrapObject( void TestInterfaceAsyncIterableSingleWithArgs::InitAsyncIterator( Iterator* aIterator, const TestInterfaceAsyncIteratorOptions& aOptions, ErrorResult& aError) { - UniquePtr data(new IteratorData(0, aOptions.mMultiplier)); + UniquePtr data( + new IteratorData{0, aOptions.mMultiplier, aOptions.mBlockingPromises}); aIterator->SetData((void*)data.release()); } @@ -48,12 +50,10 @@ void TestInterfaceAsyncIterableSingleWithArgs::DestroyAsyncIterator( } already_AddRefed -TestInterfaceAsyncIterableSingleWithArgs::GetNextPromise(JSContext* aCx, - Iterator* aIterator, +TestInterfaceAsyncIterableSingleWithArgs::GetNextPromise(Iterator* aIterator, ErrorResult& aRv) { return TestInterfaceAsyncIterableSingle::GetNextPromise( - aCx, aIterator, reinterpret_cast(aIterator->GetData()), - aRv); + aIterator, reinterpret_cast(aIterator->GetData()), aRv); } } // namespace mozilla::dom diff --git a/dom/bindings/test/TestInterfaceAsyncIterableSingleWithArgs.h b/dom/bindings/test/TestInterfaceAsyncIterableSingleWithArgs.h index 30d7b8455b6c..ef4cf3104f75 100644 --- a/dom/bindings/test/TestInterfaceAsyncIterableSingleWithArgs.h +++ b/dom/bindings/test/TestInterfaceAsyncIterableSingleWithArgs.h @@ -31,7 +31,7 @@ class TestInterfaceAsyncIterableSingleWithArgs final ErrorResult& aError); void DestroyAsyncIterator(Iterator* aIterator); - already_AddRefed GetNextPromise(JSContext* aCx, Iterator* aIterator, + already_AddRefed GetNextPromise(Iterator* aIterator, ErrorResult& aRv); }; diff --git a/dom/bindings/test/test_async_iterable.html b/dom/bindings/test/test_async_iterable.html index 03af2097a5e7..9393ab43dd0a 100644 --- a/dom/bindings/test/test_async_iterable.html +++ b/dom/bindings/test/test_async_iterable.html @@ -14,12 +14,9 @@ add_task(async function init() { await SpecialPowers.pushPrefEnv({set: [["dom.expose_test_interfaces", true]]}); }); -async function check_single_result(itr, multiplier = 1) { - let values = []; - for await (let v of itr) { - values.push(v); - } - is(values.length, 10, `AsyncIterableSingle: should returns 10 elements`); +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 = i * 9 % 7 * multiplier; is(values[i], expected, @@ -27,6 +24,14 @@ async function check_single_result(itr, multiplier = 1) { } } +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); +} + async function test_data_single() { info(`AsyncIterableSingle: Testing simple iterable creation and functionality`); @@ -58,6 +63,50 @@ 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))); } async function test_data_double() { diff --git a/dom/fs/api/FileSystemDirectoryHandle.cpp b/dom/fs/api/FileSystemDirectoryHandle.cpp index 81fb530f5f20..4521f3ee0828 100644 --- a/dom/fs/api/FileSystemDirectoryHandle.cpp +++ b/dom/fs/api/FileSystemDirectoryHandle.cpp @@ -68,8 +68,7 @@ void FileSystemDirectoryHandle::DestroyAsyncIterator( } already_AddRefed FileSystemDirectoryHandle::GetNextPromise( - JSContext* /* aCx */, FileSystemDirectoryHandle::iterator_t* aIterator, - ErrorResult& aError) { + FileSystemDirectoryHandle::iterator_t* aIterator, ErrorResult& aError) { return static_cast(aIterator->GetData()) ->Next(mGlobal, mManager, aError); } diff --git a/dom/fs/api/FileSystemDirectoryHandle.h b/dom/fs/api/FileSystemDirectoryHandle.h index 87b0695fd3ce..34ef2f0eb50e 100644 --- a/dom/fs/api/FileSystemDirectoryHandle.h +++ b/dom/fs/api/FileSystemDirectoryHandle.h @@ -50,8 +50,7 @@ class FileSystemDirectoryHandle final : public FileSystemHandle { void DestroyAsyncIterator(iterator_t* aIterator); - [[nodiscard]] already_AddRefed GetNextPromise(JSContext* aCx, - iterator_t* aIterator, + [[nodiscard]] already_AddRefed GetNextPromise(iterator_t* aIterator, ErrorResult& aError); already_AddRefed GetFileHandle( diff --git a/dom/fs/child/FileSystemDirectoryIteratorFactory.cpp b/dom/fs/child/FileSystemDirectoryIteratorFactory.cpp index 29fab55d49a2..ce2c9ac062f8 100644 --- a/dom/fs/child/FileSystemDirectoryIteratorFactory.cpp +++ b/dom/fs/child/FileSystemDirectoryIteratorFactory.cpp @@ -22,51 +22,23 @@ namespace mozilla::dom::fs { namespace { -inline JSContext* GetContext(const RefPtr& aPromise) { - AutoJSAPI jsapi; - if (NS_WARN_IF(!jsapi.Init(aPromise->GetGlobalObject()))) { - return nullptr; - } - return jsapi.cx(); -} - template struct ValueResolver; template <> struct ValueResolver { - nsresult operator()(nsIGlobalObject* aGlobal, - RefPtr& aManager, - const FileSystemEntryMetadata& aValue, - const RefPtr& aPromise, ErrorResult& aError) { - JSContext* cx = GetContext(aPromise); - if (!cx) { - return NS_ERROR_UNEXPECTED; - } - - JS::Rooted key(cx); - - if (!ToJSValue(cx, aValue.entryName(), &key)) { - return NS_ERROR_INVALID_ARG; - } - - iterator_utils::ResolvePromiseWithKeyOrValue(cx, aPromise.get(), key); - - return NS_OK; + void operator()(nsIGlobalObject* aGlobal, RefPtr& aManager, + const FileSystemEntryMetadata& aValue, + const RefPtr& aPromise) { + aPromise->MaybeResolve(aValue.entryName()); } }; template <> struct ValueResolver { - nsresult operator()(nsIGlobalObject* aGlobal, - RefPtr& aManager, - const FileSystemEntryMetadata& aValue, - const RefPtr& aPromise, ErrorResult& aError) { - JSContext* cx = GetContext(aPromise); - if (!cx) { - return NS_ERROR_UNEXPECTED; - } - + void operator()(nsIGlobalObject* aGlobal, RefPtr& aManager, + const FileSystemEntryMetadata& aValue, + const RefPtr& aPromise) { RefPtr handle; if (aValue.directory()) { @@ -75,33 +47,15 @@ struct ValueResolver { handle = new FileSystemFileHandle(aGlobal, aManager, aValue); } - JS::Rooted value(cx); - if (!ToJSValue(cx, handle, &value)) { - return NS_ERROR_INVALID_ARG; - } - - iterator_utils::ResolvePromiseWithKeyOrValue(cx, aPromise.get(), value); - - return NS_OK; + aPromise->MaybeResolve(std::move(handle)); } }; template <> struct ValueResolver { - nsresult operator()(nsIGlobalObject* aGlobal, - RefPtr& aManager, - const FileSystemEntryMetadata& aValue, - const RefPtr& aPromise, ErrorResult& aError) { - JSContext* cx = GetContext(aPromise); - if (!cx) { - return NS_ERROR_UNEXPECTED; - } - - JS::Rooted key(cx); - if (!ToJSValue(cx, aValue.entryName(), &key)) { - return NS_ERROR_INVALID_ARG; - } - + void operator()(nsIGlobalObject* aGlobal, RefPtr& aManager, + const FileSystemEntryMetadata& aValue, + const RefPtr& aPromise) { RefPtr handle; if (aValue.directory()) { @@ -110,15 +64,8 @@ struct ValueResolver { handle = new FileSystemFileHandle(aGlobal, aManager, aValue); } - JS::Rooted value(cx); - if (!ToJSValue(cx, handle, &value)) { - return NS_ERROR_INVALID_ARG; - } - - iterator_utils::ResolvePromiseWithKeyAndValue(cx, aPromise.get(), key, - value); - - return NS_OK; + iterator_utils::ResolvePromiseWithKeyAndValue(aPromise, aValue.entryName(), + handle); } }; @@ -141,27 +88,16 @@ class DoubleBufferQueueImpl // XXX This doesn't have to be public void ResolveValue(nsIGlobalObject* aGlobal, RefPtr& aManager, - const Maybe& aValue, RefPtr aPromise, - ErrorResult& aError) { + const Maybe& aValue, RefPtr aPromise) { 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(aCx, aPromise.get()); + iterator_utils::ResolvePromiseForFinished(aPromise); return; } - ValueResolver{}(aGlobal, aManager, *aValue, aPromise, aError); + ValueResolver{}(aGlobal, aManager, *aValue, aPromise); } already_AddRefed Next(nsIGlobalObject* aGlobal, @@ -172,7 +108,7 @@ class DoubleBufferQueueImpl return nullptr; } - next(aGlobal, aManager, promise, aError); + next(aGlobal, aManager, promise); return promise.forget(); } @@ -181,7 +117,7 @@ class DoubleBufferQueueImpl protected: void next(nsIGlobalObject* aGlobal, RefPtr& aManager, - RefPtr aResult, ErrorResult& aError) { + RefPtr aResult) { MOZ_ASSERT(aResult); Maybe rawValue; @@ -193,7 +129,7 @@ class DoubleBufferQueueImpl ErrorResult rv; RefPtr promise = Promise::Create(aGlobal, rv); if (rv.Failed()) { - aResult->MaybeReject(NS_ERROR_DOM_UNKNOWN_ERR); + aResult->MaybeReject(std::move(rv)); return; } @@ -223,7 +159,7 @@ class DoubleBufferQueueImpl } IgnoredErrorResult rv; - ResolveValue(global, manager, value, aResult, rv); + ResolveValue(global, manager, value, aResult); }, [](nsresult aRv) {}); promise->AppendNativeHandler(listener); @@ -237,7 +173,7 @@ class DoubleBufferQueueImpl nextInternal(rawValue); - ResolveValue(aGlobal, aManager, rawValue, aResult, aError); + ResolveValue(aGlobal, aManager, rawValue, aResult); } bool nextInternal(Maybe& aNext) { diff --git a/dom/fs/test/gtest/api/TestFileSystemDirectoryHandle.cpp b/dom/fs/test/gtest/api/TestFileSystemDirectoryHandle.cpp index 6da71747c9a0..72cefe9b847b 100644 --- a/dom/fs/test/gtest/api/TestFileSystemDirectoryHandle.cpp +++ b/dom/fs/test/gtest/api/TestFileSystemDirectoryHandle.cpp @@ -30,12 +30,6 @@ class TestFileSystemDirectoryHandle : public ::testing::Test { mManager = MakeAndAddRef(mGlobal, nullptr); } - FileSystemDirectoryHandle::iterator_t::WrapFunc GetWrapFunc() const { - return [](JSContext*, AsyncIterableIterator*, - JS::Handle, - JS::MutableHandle) -> bool { return true; }; - } - nsIGlobalObject* mGlobal = GetGlobal(); const IterableIteratorBase::IteratorType mIteratorType = IterableIteratorBase::IteratorType::Keys; @@ -60,8 +54,7 @@ TEST_F(TestFileSystemDirectoryHandle, initAndDestroyIterator) { ASSERT_TRUE(dirHandle); RefPtr iterator = - new FileSystemDirectoryHandle::iterator_t(dirHandle.get(), mIteratorType, - GetWrapFunc()); + new FileSystemDirectoryHandle::iterator_t(dirHandle.get(), mIteratorType); IgnoredErrorResult rv; dirHandle->InitAsyncIterator(iterator, rv); ASSERT_TRUE(iterator->GetData()); @@ -97,13 +90,12 @@ TEST_F(TestFileSystemDirectoryHandle, isNextPromiseReturned) { .WillOnce(::testing::Return(Promise::Create(mGlobal, error))); RefPtr iterator = - MakeAndAddRef( - dirHandle.get(), mIteratorType, GetWrapFunc()); + MakeAndAddRef(dirHandle.get(), + mIteratorType); iterator->SetData(static_cast(mockIter)); IgnoredErrorResult rv; - RefPtr promise = - dirHandle->GetNextPromise(nullptr, iterator.get(), rv); + RefPtr promise = dirHandle->GetNextPromise(iterator.get(), rv); ASSERT_TRUE(promise); dirHandle->DestroyAsyncIterator(iterator.get()); diff --git a/dom/webidl/TestInterfaceJSMaplikeSetlikeIterable.webidl b/dom/webidl/TestInterfaceJSMaplikeSetlikeIterable.webidl index 3dadf84301b0..e9c88dd08da4 100644 --- a/dom/webidl/TestInterfaceJSMaplikeSetlikeIterable.webidl +++ b/dom/webidl/TestInterfaceJSMaplikeSetlikeIterable.webidl @@ -111,6 +111,7 @@ interface TestInterfaceAsyncIterableSingle { dictionary TestInterfaceAsyncIteratorOptions { unsigned long multiplier = 1; + sequence> blockingPromises = []; }; [Pref="dom.expose_test_interfaces",