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
This commit is contained in:
Peter Van der Beken 2022-09-22 18:28:15 +00:00
parent bd1c6c4d03
commit 3e507adf99
20 changed files with 489 additions and 321 deletions

View File

@ -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,

View File

@ -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):

View File

@ -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<JSObject*> aResult,
void DictReturn(JSContext* aCx, JS::MutableHandle<JS::Value> aResult,
bool aDone, JS::Handle<JS::Value> aValue, ErrorResult& aRv) {
RootedDictionary<IterableKeyOrValueResult> dict(aCx);
dict.mDone = aDone;
@ -41,6 +43,16 @@ void DictReturn(JSContext* aCx, JS::MutableHandle<JSObject*> 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());
}
@ -67,46 +79,153 @@ void KeyAndValueReturn(JSContext* aCx, JS::Handle<JS::Value> aKey,
aResult.set(&dictValue.toObject());
}
void ResolvePromiseForFinished(JSContext* aCx, Promise* aPromise) {
MOZ_ASSERT(aPromise);
ErrorResult error;
JS::Rooted<JSObject*> 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<JS::Value> aKeyOrValue) {
MOZ_ASSERT(aPromise);
ErrorResult error;
JS::Rooted<JSObject*> 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<JS::Value> aKey,
JS::Handle<JS::Value> aValue) {
MOZ_ASSERT(aPromise);
ErrorResult error;
JS::Rooted<JSObject*> 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<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 objects 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);
}
// 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
// objects target and object.
RefPtr<Promise> nextPromise = GetNextPromise(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 objects 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 objects 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 objects ongoing promise to null.
aObject->mOngoingPromise = nullptr;
// 2. Set objects 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 objects ongoing promise to
// afterOngoingPromiseCapability.[[Promise]].
aObject->mOngoingPromise = afterOngoingPromise.unwrap().forget();
} else {
// 11. Otherwise:
// 1. Set objects ongoing promise to the result of running nextSteps.
aObject->mOngoingPromise = NextSteps(aCx, aObject, globalObject, aRv);
}
// 12. Return objects ongoing promise.
return do_AddRef(aObject->mOngoingPromise);
}
} // namespace binding_detail
} // namespace mozilla::dom

View File

@ -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<JSObject*> aResult,
bool aDone, JS::Handle<JS::Value> aValue, ErrorResult& aRv);
@ -47,14 +60,16 @@ void KeyAndValueReturn(JSContext* aCx, JS::Handle<JS::Value> aKey,
JS::Handle<JS::Value> aValue,
JS::MutableHandle<JSObject*> 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<JS::Value> aKeyOrValue);
template <typename Key, typename Value>
void ResolvePromiseWithKeyAndValue(Promise* aPromise, const Key& aKey,
const Value& aValue) {
aPromise->MaybeResolve(MakeTuple(aKey, aValue));
}
void ResolvePromiseWithKeyAndValue(JSContext* aCx, Promise* aPromise,
JS::Handle<JS::Value> aKey,
JS::Handle<JS::Value> aValue);
} // namespace iterator_utils
class IterableIteratorBase : public nsISupports {
@ -209,44 +224,46 @@ class IterableIterator final : public IterableIteratorBase {
uint32_t mIndex;
};
template <typename T>
class AsyncIterableIterator final : public IterableIteratorBase,
public SupportsWeakPtr {
public:
typedef bool (*WrapFunc)(JSContext* aCx, AsyncIterableIterator<T>* aObject,
JS::Handle<JSObject*> aGivenProto,
JS::MutableHandle<JSObject*> 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<Promise> mOngoingPromise;
// Is finished
bool mIsFinished = false;
};
template <typename T>
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<JSObject*> aResult,
ErrorResult& aRv) {
RefPtr<Promise> promise = mIterableObj->GetNextPromise(aCx, this, aRv);
if (!promise) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
aResult.set(promise->PromiseObj());
}
bool WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto,
JS::MutableHandle<JSObject*> 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<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;
// Kind
// Ongoing promise
// Is finished
// See AsyncIterableIteratorBase
// Opaque data of the backing object.
void* mData{nullptr};
};
namespace binding_detail {
template <typename T>
using WrappableIterableIterator = IterableIterator<T>;
class AsyncIterableNextImpl {
protected:
already_AddRefed<Promise> Next(JSContext* aCx,
AsyncIterableIteratorBase* aObject,
nsISupports* aGlobalObject, ErrorResult& aRv);
virtual already_AddRefed<Promise> GetNextPromise(ErrorResult& aRv) = 0;
private:
already_AddRefed<Promise> NextSteps(JSContext* aCx,
AsyncIterableIteratorBase* aObject,
nsIGlobalObject* aGlobalObject,
ErrorResult& aRv);
};
template <typename T>
class AsyncIterableIteratorNoReturn : public AsyncIterableIterator<T>,
public AsyncIterableNextImpl {
public:
using WrapFunc = bool (*)(JSContext* aCx,
AsyncIterableIteratorNoReturn<T>* aObject,
JS::Handle<JSObject*> aGivenProto,
JS::MutableHandle<JSObject*> aReflector);
using AsyncIterableIteratorBase::IteratorType;
AsyncIterableIteratorNoReturn(T* aIterableObj, IteratorType aIteratorType,
WrapFunc aWrapFunc)
: AsyncIterableIterator<T>(aIterableObj, aIteratorType),
mWrapFunc(aWrapFunc) {
MOZ_ASSERT(mWrapFunc);
}
bool WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto,
JS::MutableHandle<JSObject*> aObj) {
return (*mWrapFunc)(aCx, this, aGivenProto, aObj);
}
already_AddRefed<Promise> Next(JSContext* aCx, ErrorResult& aRv) {
return AsyncIterableNextImpl::Next(
aCx, this, this->mIterableObj->GetParentObject(), aRv);
}
protected:
already_AddRefed<Promise> GetNextPromise(ErrorResult& aRv) override {
return this->mIterableObj->GetNextPromise(
static_cast<AsyncIterableIterator<T>*>(this), aRv);
}
private:
// Function pointer to binding-type-specific Wrap() call for this iterator.
WrapFunc mWrapFunc;
};
template <typename T>
using WrappableAsyncIterableIterator = AsyncIterableIteratorNoReturn<T>;
} // namespace binding_detail
} // namespace mozilla::dom
#endif // mozilla_dom_IterableIterator_h

View File

@ -406,6 +406,34 @@ 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>

View File

@ -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")])

View File

@ -67,61 +67,42 @@ void TestInterfaceAsyncIterableDouble::DestroyAsyncIterator(
}
already_AddRefed<Promise> TestInterfaceAsyncIterableDouble::GetNextPromise(
JSContext* aCx, Iterator* aIterator, ErrorResult& aRv) {
Iterator* aIterator, ErrorResult& aRv) {
RefPtr<Promise> promise = Promise::Create(mParent->AsGlobal(), aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
auto* data = reinterpret_cast<IteratorData*>(aIterator->GetData());
data->mPromise = promise;
NS_DispatchToMainThread(NS_NewRunnableFunction(
"TestInterfaceAsyncIterableDouble::GetNextPromise",
[self = RefPtr{this}, iterator = RefPtr{aIterator}] {
self->ResolvePromise(iterator);
}));
NS_DispatchToMainThread(NewRunnableMethod<RefPtr<Iterator>, RefPtr<Promise>>(
"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<IteratorData*>(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<JS::Value> key(cx);
JS::Rooted<JS::Value> 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;
}
}

View File

@ -41,7 +41,7 @@ class TestInterfaceAsyncIterableDouble final : public nsISupports,
using Iterator = AsyncIterableIterator<TestInterfaceAsyncIterableDouble>;
void InitAsyncIterator(Iterator* aIterator, ErrorResult& aError);
void DestroyAsyncIterator(Iterator* aIterator);
already_AddRefed<Promise> GetNextPromise(JSContext* aCx, Iterator* aIterator,
already_AddRefed<Promise> 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<nsPIDOMWindowInner> mParent;
nsTArray<std::pair<nsString, nsString>> mValues;

View File

@ -73,63 +73,44 @@ void TestInterfaceAsyncIterableDoubleUnion::DestroyAsyncIterator(
}
already_AddRefed<Promise> TestInterfaceAsyncIterableDoubleUnion::GetNextPromise(
JSContext* aCx, Iterator* aIterator, ErrorResult& aRv) {
Iterator* aIterator, ErrorResult& aRv) {
RefPtr<Promise> promise = Promise::Create(mParent->AsGlobal(), aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
auto* data = reinterpret_cast<IteratorData*>(aIterator->GetData());
data->mPromise = promise;
NS_DispatchToMainThread(NS_NewRunnableFunction(
"TestInterfaceAsyncIterableDoubleUnion::GetNextPromise",
[self = RefPtr{this}, iterator = RefPtr{aIterator}] {
self->ResolvePromise(iterator);
}));
NS_DispatchToMainThread(NewRunnableMethod<RefPtr<Iterator>, RefPtr<Promise>>(
"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<IteratorData*>(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<JS::Value> key(cx);
JS::Rooted<JS::Value> 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;
}
}

View File

@ -41,24 +41,18 @@ class TestInterfaceAsyncIterableDoubleUnion final : public nsISupports,
using Iterator = AsyncIterableIterator<TestInterfaceAsyncIterableDoubleUnion>;
void InitAsyncIterator(Iterator* aIterator, ErrorResult& aError);
void DestroyAsyncIterator(Iterator* aIterator);
already_AddRefed<Promise> GetNextPromise(JSContext* aCx, Iterator* aIterator,
already_AddRefed<Promise> 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<Promise> mPromise;
uint32_t mIndex;
};
virtual ~TestInterfaceAsyncIterableDoubleUnion() = default;
void ResolvePromise(Iterator* aIterator);
void ResolvePromise(Iterator* aIterator, Promise* aPromise);
nsCOMPtr<nsPIDOMWindowInner> mParent;
nsTArray<std::pair<nsString, OwningStringOrLong>> mValues;

View File

@ -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<IteratorData> data(new IteratorData(0, 1));
UniquePtr<IteratorData> data(new IteratorData());
aIterator->SetData((void*)data.release());
}
@ -70,51 +72,51 @@ void TestInterfaceAsyncIterableSingle::DestroyAsyncIterator(
}
already_AddRefed<Promise> TestInterfaceAsyncIterableSingle::GetNextPromise(
JSContext* aCx, Iterator* aIterator, ErrorResult& aRv) {
return GetNextPromise(aCx, aIterator,
reinterpret_cast<IteratorData*>(aIterator->GetData()),
aRv);
Iterator* aIterator, ErrorResult& aRv) {
return GetNextPromise(
aIterator, reinterpret_cast<IteratorData*>(aIterator->GetData()), aRv);
}
already_AddRefed<Promise> TestInterfaceAsyncIterableSingle::GetNextPromise(
JSContext* aCx, IterableIteratorBase* aIterator, IteratorData* aData,
ErrorResult& aRv) {
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;
NS_DispatchToMainThread(NS_NewRunnableFunction(
"TestInterfaceAsyncIterableSingle::GetNextPromise",
[iterator = RefPtr{aIterator}, aData, self = RefPtr{this}] {
self->ResolvePromise(iterator, aData);
}));
nsCOMPtr<nsIRunnable> callResolvePromise =
NewRunnableMethod<RefPtr<IterableIteratorBase>, IteratorData*,
RefPtr<Promise>>(
"TestInterfaceAsyncIterableSingle::GetNextPromise", 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);
}
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<JS::Value> 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

View File

@ -43,33 +43,26 @@ class TestInterfaceAsyncIterableSingle : public nsISupports,
using Iterator = AsyncIterableIterator<TestInterfaceAsyncIterableSingle>;
void InitAsyncIterator(Iterator* aIterator, ErrorResult& aError);
void DestroyAsyncIterator(Iterator* aIterator);
already_AddRefed<Promise> GetNextPromise(JSContext* aCx, Iterator* aIterator,
already_AddRefed<Promise> 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<Promise> mPromise;
uint32_t mIndex;
uint32_t mMultiplier;
uint32_t mIndex = 0;
uint32_t mMultiplier = 1;
Sequence<OwningNonNull<Promise>> mBlockingPromises;
size_t mBlockingPromisesIndex = 0;
};
already_AddRefed<Promise> GetNextPromise(JSContext* aCx,
IterableIteratorBase* aIterator,
already_AddRefed<Promise> 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<nsPIDOMWindowInner> mParent;
bool mFailToInit;

View File

@ -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<IteratorData> data(new IteratorData(0, aOptions.mMultiplier));
UniquePtr<IteratorData> data(
new IteratorData{0, aOptions.mMultiplier, aOptions.mBlockingPromises});
aIterator->SetData((void*)data.release());
}
@ -48,12 +50,10 @@ void TestInterfaceAsyncIterableSingleWithArgs::DestroyAsyncIterator(
}
already_AddRefed<Promise>
TestInterfaceAsyncIterableSingleWithArgs::GetNextPromise(JSContext* aCx,
Iterator* aIterator,
TestInterfaceAsyncIterableSingleWithArgs::GetNextPromise(Iterator* aIterator,
ErrorResult& aRv) {
return TestInterfaceAsyncIterableSingle::GetNextPromise(
aCx, aIterator, reinterpret_cast<IteratorData*>(aIterator->GetData()),
aRv);
aIterator, reinterpret_cast<IteratorData*>(aIterator->GetData()), aRv);
}
} // namespace mozilla::dom

View File

@ -31,7 +31,7 @@ class TestInterfaceAsyncIterableSingleWithArgs final
ErrorResult& aError);
void DestroyAsyncIterator(Iterator* aIterator);
already_AddRefed<Promise> GetNextPromise(JSContext* aCx, Iterator* aIterator,
already_AddRefed<Promise> GetNextPromise(Iterator* aIterator,
ErrorResult& aRv);
};

View File

@ -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() {

View File

@ -68,8 +68,7 @@ void FileSystemDirectoryHandle::DestroyAsyncIterator(
}
already_AddRefed<Promise> FileSystemDirectoryHandle::GetNextPromise(
JSContext* /* aCx */, FileSystemDirectoryHandle::iterator_t* aIterator,
ErrorResult& aError) {
FileSystemDirectoryHandle::iterator_t* aIterator, ErrorResult& aError) {
return static_cast<FileSystemDirectoryIterator::Impl*>(aIterator->GetData())
->Next(mGlobal, mManager, aError);
}

View File

@ -50,8 +50,7 @@ class FileSystemDirectoryHandle final : public FileSystemHandle {
void DestroyAsyncIterator(iterator_t* aIterator);
[[nodiscard]] already_AddRefed<Promise> GetNextPromise(JSContext* aCx,
iterator_t* aIterator,
[[nodiscard]] already_AddRefed<Promise> GetNextPromise(iterator_t* aIterator,
ErrorResult& aError);
already_AddRefed<Promise> GetFileHandle(

View File

@ -22,51 +22,23 @@ 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> {
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);
return NS_OK;
void operator()(nsIGlobalObject* aGlobal, RefPtr<FileSystemManager>& aManager,
const FileSystemEntryMetadata& aValue,
const RefPtr<Promise>& aPromise) {
aPromise->MaybeResolve(aValue.entryName());
}
};
template <>
struct ValueResolver<IterableIteratorBase::Values> {
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;
}
void operator()(nsIGlobalObject* aGlobal, RefPtr<FileSystemManager>& aManager,
const FileSystemEntryMetadata& aValue,
const RefPtr<Promise>& aPromise) {
RefPtr<FileSystemHandle> handle;
if (aValue.directory()) {
@ -75,33 +47,15 @@ struct ValueResolver<IterableIteratorBase::Values> {
handle = new FileSystemFileHandle(aGlobal, aManager, aValue);
}
JS::Rooted<JS::Value> 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<IterableIteratorBase::Entries> {
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;
}
void operator()(nsIGlobalObject* aGlobal, RefPtr<FileSystemManager>& aManager,
const FileSystemEntryMetadata& aValue,
const RefPtr<Promise>& aPromise) {
RefPtr<FileSystemHandle> handle;
if (aValue.directory()) {
@ -110,15 +64,8 @@ struct ValueResolver<IterableIteratorBase::Entries> {
handle = new FileSystemFileHandle(aGlobal, aManager, aValue);
}
JS::Rooted<JS::Value> 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<FileSystemManager>& aManager,
const Maybe<DataType>& aValue, RefPtr<Promise> aPromise,
ErrorResult& aError) {
const Maybe<DataType>& aValue, RefPtr<Promise> 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<Promise> 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<FileSystemManager>& aManager,
RefPtr<Promise> aResult, ErrorResult& aError) {
RefPtr<Promise> aResult) {
MOZ_ASSERT(aResult);
Maybe<DataType> rawValue;
@ -193,7 +129,7 @@ class DoubleBufferQueueImpl
ErrorResult rv;
RefPtr<Promise> 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<DataType>& aNext) {

View File

@ -30,12 +30,6 @@ 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;
@ -60,8 +54,7 @@ TEST_F(TestFileSystemDirectoryHandle, initAndDestroyIterator) {
ASSERT_TRUE(dirHandle);
RefPtr<FileSystemDirectoryHandle::iterator_t> 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<FileSystemDirectoryHandle::iterator_t> iterator =
MakeAndAddRef<FileSystemDirectoryHandle::iterator_t>(
dirHandle.get(), mIteratorType, GetWrapFunc());
MakeAndAddRef<FileSystemDirectoryHandle::iterator_t>(dirHandle.get(),
mIteratorType);
iterator->SetData(static_cast<void*>(mockIter));
IgnoredErrorResult rv;
RefPtr<Promise> promise =
dirHandle->GetNextPromise(nullptr, iterator.get(), rv);
RefPtr<Promise> promise = dirHandle->GetNextPromise(iterator.get(), rv);
ASSERT_TRUE(promise);
dirHandle->DestroyAsyncIterator(iterator.get());

View File

@ -111,6 +111,7 @@ interface TestInterfaceAsyncIterableSingle {
dictionary TestInterfaceAsyncIteratorOptions {
unsigned long multiplier = 1;
sequence<Promise<any>> blockingPromises = [];
};
[Pref="dom.expose_test_interfaces",