зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1782400 - Implement "return" method for WebIDL async iterable iterator. r=edgar
Differential Revision: https://phabricator.services.mozilla.com/D156350
This commit is contained in:
Родитель
9ffbe8f65e
Коммит
2fecef1b02
|
@ -4501,9 +4501,17 @@ def CopyUnforgeablePropertiesToInstance(descriptor, failureCode):
|
|||
|
||||
|
||||
def AssertInheritanceChain(descriptor):
|
||||
asserts = ""
|
||||
# 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,
|
||||
)
|
||||
iface = descriptor.interface
|
||||
while iface:
|
||||
while iface.parent:
|
||||
iface = iface.parent
|
||||
desc = descriptor.getDescriptor(iface.identifier.name)
|
||||
asserts += (
|
||||
"MOZ_ASSERT(static_cast<%s*>(aObject) == \n"
|
||||
|
@ -4511,7 +4519,6 @@ 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
|
||||
|
||||
|
@ -9574,7 +9581,6 @@ class CGPerSignatureCall(CGThing):
|
|||
cgThings.append(
|
||||
CGIterableMethodGenerator(
|
||||
descriptor,
|
||||
idlNode.maplikeOrSetlikeOrIterable,
|
||||
idlNode.identifier.name,
|
||||
self.getArgumentNames(),
|
||||
)
|
||||
|
@ -22274,7 +22280,7 @@ class CGIterableMethodGenerator(CGGeneric):
|
|||
using CGCallGenerator.
|
||||
"""
|
||||
|
||||
def __init__(self, descriptor, iterable, methodName, args):
|
||||
def __init__(self, descriptor, methodName, args):
|
||||
if methodName == "forEach":
|
||||
assert len(args) == 2
|
||||
|
||||
|
@ -22319,8 +22325,14 @@ class CGIterableMethodGenerator(CGGeneric):
|
|||
wrap = f"{descriptor.interface.identifier.name}Iterator_Binding::Wrap"
|
||||
iterClass = f"mozilla::dom::binding_detail::WrappableIterableIterator<{descriptor.nativeType}, &{wrap}>"
|
||||
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}, &{wrap}>"
|
||||
iterClass = f"mozilla::dom::binding_detail::WrappableAsyncIterableIterator<{descriptor.nativeType}, {needReturnMethod}, &{wrap}>"
|
||||
|
||||
createIterator = fill(
|
||||
"""
|
||||
|
|
|
@ -13,6 +13,13 @@ 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
|
||||
|
@ -1052,9 +1059,15 @@ def iteratorNativeType(descriptor):
|
|||
assert iterableDecl.isPairIterator() or descriptor.interface.isAsyncIterable()
|
||||
if descriptor.interface.isIterable():
|
||||
return "mozilla::dom::IterableIterator<%s>" % descriptor.nativeType
|
||||
return (
|
||||
"mozilla::dom::binding_detail::AsyncIterableIteratorNoReturn<%s>"
|
||||
% descriptor.nativeType
|
||||
needReturnMethod = toStringBool(
|
||||
descriptor.interface.maplikeOrSetlikeOrIterable.getExtendedAttribute(
|
||||
"GenerateReturnMethod"
|
||||
)
|
||||
is not None
|
||||
)
|
||||
return "mozilla::dom::binding_detail::AsyncIterableIteratorNative<%s, %s>" % (
|
||||
descriptor.nativeType,
|
||||
needReturnMethod,
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -109,7 +109,7 @@ already_AddRefed<Promise> AsyncIterableNextImpl::NextSteps(
|
|||
|
||||
// 4. Let nextPromise be the result of getting the next iteration result with
|
||||
// object’s target and object.
|
||||
RefPtr<Promise> nextPromise = GetNextPromise(aRv);
|
||||
RefPtr<Promise> nextPromise = GetNextResult(aRv);
|
||||
|
||||
// 5. Let fulfillSteps be the following steps, given next:
|
||||
auto fulfillSteps = [](JSContext* aCx, JS::Handle<JS::Value> aNext,
|
||||
|
@ -222,6 +222,102 @@ already_AddRefed<Promise> AsyncIterableNextImpl::Next(
|
|||
return do_AddRef(aObject->mOngoingPromise);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// 2. Perform ! Call(returnPromiseCapability.[[Resolve]], undefined,
|
||||
// «result»).
|
||||
// 3. Return returnPromiseCapability.[[Promise]].
|
||||
return Promise::Resolve(aGlobalObject, aCx, dict, aRv);
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise> AsyncIterableReturnImpl::Return(
|
||||
JSContext* aCx, AsyncIterableIteratorBase* aObject,
|
||||
nsISupports* aGlobalObject, JS::Handle<JS::Value> aValue,
|
||||
ErrorResult& aRv) {
|
||||
nsCOMPtr<nsIGlobalObject> globalObject = do_QueryInterface(aGlobalObject);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
} // namespace binding_detail
|
||||
|
||||
} // namespace mozilla::dom
|
||||
|
|
|
@ -212,6 +212,7 @@ class IterableIterator : public IterableIteratorBase {
|
|||
namespace binding_detail {
|
||||
|
||||
class AsyncIterableNextImpl;
|
||||
class AsyncIterableReturnImpl;
|
||||
|
||||
} // namespace binding_detail
|
||||
|
||||
|
@ -225,6 +226,7 @@ class AsyncIterableIteratorBase : public IterableIteratorBase {
|
|||
|
||||
private:
|
||||
friend class binding_detail::AsyncIterableNextImpl;
|
||||
friend class binding_detail::AsyncIterableReturnImpl;
|
||||
|
||||
// 3.7.10.1. Default asynchronous iterator objects
|
||||
// Target is in AsyncIterableIterator
|
||||
|
@ -320,7 +322,7 @@ class AsyncIterableNextImpl {
|
|||
already_AddRefed<Promise> Next(JSContext* aCx,
|
||||
AsyncIterableIteratorBase* aObject,
|
||||
nsISupports* aGlobalObject, ErrorResult& aRv);
|
||||
virtual already_AddRefed<Promise> GetNextPromise(ErrorResult& aRv) = 0;
|
||||
virtual already_AddRefed<Promise> GetNextResult(ErrorResult& aRv) = 0;
|
||||
|
||||
private:
|
||||
already_AddRefed<Promise> NextSteps(JSContext* aCx,
|
||||
|
@ -329,6 +331,24 @@ class AsyncIterableNextImpl {
|
|||
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 {
|
||||
|
@ -341,23 +361,50 @@ class AsyncIterableIteratorNoReturn : public AsyncIterableIterator<T>,
|
|||
}
|
||||
|
||||
protected:
|
||||
already_AddRefed<Promise> GetNextPromise(ErrorResult& aRv) override {
|
||||
return this->mIterableObj->GetNextPromise(
|
||||
already_AddRefed<Promise> GetNextResult(ErrorResult& aRv) override {
|
||||
return this->mIterableObj->GetNextIterationResult(
|
||||
static_cast<AsyncIterableIterator<T>*>(this), aRv);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using AsyncIterableIteratorWrapFunc =
|
||||
bool (*)(JSContext* aCx, AsyncIterableIteratorNoReturn<T>* aObject,
|
||||
JS::MutableHandle<JSObject*> aReflector);
|
||||
|
||||
template <typename T, AsyncIterableIteratorWrapFunc<T> WrapFunc>
|
||||
class WrappableAsyncIterableIterator final
|
||||
: public AsyncIterableIteratorNoReturn<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);
|
||||
|
|
|
@ -8908,12 +8908,39 @@ class Parser(Tokenizer):
|
|||
else:
|
||||
nextReturnType = BuiltinTypes[IDLBuiltinType.Types.object]
|
||||
nextMethod = IDLMethod(
|
||||
iface.location,
|
||||
IDLUnresolvedIdentifier(iface.location, "next"),
|
||||
iterable.location,
|
||||
IDLUnresolvedIdentifier(iterable.location, "next"),
|
||||
nextReturnType,
|
||||
[],
|
||||
)
|
||||
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:
|
||||
|
@ -8930,7 +8957,7 @@ class Parser(Tokenizer):
|
|||
self.globalScope(),
|
||||
itr_ident,
|
||||
None,
|
||||
[nextMethod],
|
||||
methods,
|
||||
isKnownNonPartial=True,
|
||||
classNameOverride=classNameOverride,
|
||||
)
|
||||
|
|
|
@ -54,15 +54,16 @@ nsPIDOMWindowInner* TestInterfaceAsyncIterableDouble::GetParentObject() const {
|
|||
return mParent;
|
||||
}
|
||||
|
||||
already_AddRefed<Promise> TestInterfaceAsyncIterableDouble::GetNextPromise(
|
||||
Iterator* aIterator, ErrorResult& aRv) {
|
||||
already_AddRefed<Promise>
|
||||
TestInterfaceAsyncIterableDouble::GetNextIterationResult(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::GetNextPromise", this,
|
||||
"TestInterfaceAsyncIterableDouble::GetNextIterationResult", this,
|
||||
&TestInterfaceAsyncIterableDouble::ResolvePromise, aIterator, promise));
|
||||
|
||||
return promise.forget();
|
||||
|
|
|
@ -47,8 +47,8 @@ class TestInterfaceAsyncIterableDouble final : public nsISupports,
|
|||
void InitAsyncIteratorData(IteratorData& aData, Iterator::IteratorType aType,
|
||||
ErrorResult& aError) {}
|
||||
|
||||
already_AddRefed<Promise> GetNextPromise(Iterator* aIterator,
|
||||
ErrorResult& aRv);
|
||||
already_AddRefed<Promise> GetNextIterationResult(Iterator* aIterator,
|
||||
ErrorResult& aRv);
|
||||
|
||||
private:
|
||||
virtual ~TestInterfaceAsyncIterableDouble() = default;
|
||||
|
|
|
@ -60,7 +60,8 @@ nsPIDOMWindowInner* TestInterfaceAsyncIterableDoubleUnion::GetParentObject()
|
|||
return mParent;
|
||||
}
|
||||
|
||||
already_AddRefed<Promise> TestInterfaceAsyncIterableDoubleUnion::GetNextPromise(
|
||||
already_AddRefed<Promise>
|
||||
TestInterfaceAsyncIterableDoubleUnion::GetNextIterationResult(
|
||||
Iterator* aIterator, ErrorResult& aRv) {
|
||||
RefPtr<Promise> promise = Promise::Create(mParent->AsGlobal(), aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
|
@ -68,7 +69,7 @@ already_AddRefed<Promise> TestInterfaceAsyncIterableDoubleUnion::GetNextPromise(
|
|||
}
|
||||
|
||||
NS_DispatchToMainThread(NewRunnableMethod<RefPtr<Iterator>, RefPtr<Promise>>(
|
||||
"TestInterfaceAsyncIterableDoubleUnion::GetNextPromise", this,
|
||||
"TestInterfaceAsyncIterableDoubleUnion::GetNextIterationResult", this,
|
||||
&TestInterfaceAsyncIterableDoubleUnion::ResolvePromise, aIterator,
|
||||
promise));
|
||||
|
||||
|
|
|
@ -47,8 +47,8 @@ class TestInterfaceAsyncIterableDoubleUnion final : public nsISupports,
|
|||
void InitAsyncIteratorData(IteratorData& aData, Iterator::IteratorType aType,
|
||||
ErrorResult& aError) {}
|
||||
|
||||
already_AddRefed<Promise> GetNextPromise(Iterator* aIterator,
|
||||
ErrorResult& aRv);
|
||||
already_AddRefed<Promise> GetNextIterationResult(Iterator* aIterator,
|
||||
ErrorResult& aRv);
|
||||
|
||||
private:
|
||||
virtual ~TestInterfaceAsyncIterableDoubleUnion() = default;
|
||||
|
|
|
@ -66,12 +66,14 @@ void TestInterfaceAsyncIterableSingle::InitAsyncIteratorData(
|
|||
MOZ_ASSERT(aData.mMultiplier == 1);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise> TestInterfaceAsyncIterableSingle::GetNextPromise(
|
||||
Iterator* aIterator, ErrorResult& aRv) {
|
||||
return GetNextPromise(aIterator, aIterator->Data(), aRv);
|
||||
already_AddRefed<Promise>
|
||||
TestInterfaceAsyncIterableSingle::GetNextIterationResult(Iterator* aIterator,
|
||||
ErrorResult& aRv) {
|
||||
return GetNextIterationResult(aIterator, aIterator->Data(), aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise> TestInterfaceAsyncIterableSingle::GetNextPromise(
|
||||
already_AddRefed<Promise>
|
||||
TestInterfaceAsyncIterableSingle::GetNextIterationResult(
|
||||
IterableIteratorBase* aIterator, IteratorData& aData, ErrorResult& aRv) {
|
||||
RefPtr<Promise> promise = Promise::Create(mParent->AsGlobal(), aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
|
@ -81,7 +83,7 @@ already_AddRefed<Promise> TestInterfaceAsyncIterableSingle::GetNextPromise(
|
|||
nsCOMPtr<nsIRunnable> callResolvePromise =
|
||||
NewRunnableMethod<RefPtr<IterableIteratorBase>, IteratorData&,
|
||||
RefPtr<Promise>>(
|
||||
"TestInterfaceAsyncIterableSingle::GetNextPromise", this,
|
||||
"TestInterfaceAsyncIterableSingle::GetNextIterationResult", this,
|
||||
&TestInterfaceAsyncIterableSingle::ResolvePromise, aIterator, aData,
|
||||
promise);
|
||||
if (aData.mBlockingPromisesIndex < aData.mBlockingPromises.Length()) {
|
||||
|
|
|
@ -41,8 +41,8 @@ class TestInterfaceAsyncIterableSingle : public nsISupports,
|
|||
const TestInterfaceAsyncIterableSingleOptions& aOptions, ErrorResult& rv);
|
||||
|
||||
using Iterator = AsyncIterableIterator<TestInterfaceAsyncIterableSingle>;
|
||||
already_AddRefed<Promise> GetNextPromise(Iterator* aIterator,
|
||||
ErrorResult& aRv);
|
||||
already_AddRefed<Promise> GetNextIterationResult(Iterator* aIterator,
|
||||
ErrorResult& aRv);
|
||||
|
||||
struct IteratorData {
|
||||
void Traverse(nsCycleCollectionTraversalCallback& cb);
|
||||
|
@ -58,9 +58,8 @@ class TestInterfaceAsyncIterableSingle : public nsISupports,
|
|||
ErrorResult& aError);
|
||||
|
||||
protected:
|
||||
already_AddRefed<Promise> GetNextPromise(IterableIteratorBase* aIterator,
|
||||
IteratorData& aData,
|
||||
ErrorResult& aRv);
|
||||
already_AddRefed<Promise> GetNextIterationResult(
|
||||
IterableIteratorBase* aIterator, IteratorData& aData, ErrorResult& aRv);
|
||||
|
||||
virtual ~TestInterfaceAsyncIterableSingle() = default;
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
* 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"
|
||||
|
@ -13,6 +14,30 @@
|
|||
|
||||
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(
|
||||
|
@ -43,10 +68,26 @@ void TestInterfaceAsyncIterableSingleWithArgs::InitAsyncIteratorData(
|
|||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
TestInterfaceAsyncIterableSingleWithArgs::GetNextPromise(Iterator* aIterator,
|
||||
ErrorResult& aRv) {
|
||||
return TestInterfaceAsyncIterableSingle::GetNextPromise(
|
||||
TestInterfaceAsyncIterableSingleWithArgs::GetNextIterationResult(
|
||||
Iterator* aIterator, ErrorResult& aRv) {
|
||||
return TestInterfaceAsyncIterableSingle::GetNextIterationResult(
|
||||
aIterator, aIterator->Data(), aRv);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
} // namespace mozilla::dom
|
||||
|
|
|
@ -18,6 +18,11 @@ 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,8 +36,23 @@ class TestInterfaceAsyncIterableSingleWithArgs final
|
|||
const TestInterfaceAsyncIteratorOptions& aOptions,
|
||||
ErrorResult& aError);
|
||||
|
||||
already_AddRefed<Promise> GetNextPromise(Iterator* aIterator,
|
||||
already_AddRefed<Promise> GetNextIterationResult(Iterator* aIterator,
|
||||
ErrorResult& aRv);
|
||||
already_AddRefed<Promise> IteratorReturn(JSContext* aCx, Iterator* aIterator,
|
||||
JS::Handle<JS::Value> aValue,
|
||||
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
|
||||
|
|
|
@ -14,11 +14,13 @@ 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 = i * 9 % 7 * multiplier;
|
||||
let expected = singleValues[i] * multiplier;
|
||||
is(values[i], expected,
|
||||
`AsyncIterableSingle: should be ${expected}, get ${values[i]}`);
|
||||
}
|
||||
|
@ -107,6 +109,60 @@ async function test_data_single() {
|
|||
}
|
||||
|
||||
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() {
|
||||
|
|
|
@ -56,7 +56,7 @@ void FileSystemDirectoryHandle::InitAsyncIteratorData(
|
|||
fs::FileSystemDirectoryIteratorFactory::Create(mMetadata, aType);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise> FileSystemDirectoryHandle::GetNextPromise(
|
||||
already_AddRefed<Promise> FileSystemDirectoryHandle::GetNextIterationResult(
|
||||
FileSystemDirectoryHandle::iterator_t* aIterator, ErrorResult& aError) {
|
||||
return aIterator->Data().mImpl->Next(mGlobal, mManager, aError);
|
||||
}
|
||||
|
|
|
@ -53,8 +53,8 @@ class FileSystemDirectoryHandle final : public FileSystemHandle {
|
|||
iterator_t::IteratorType aType,
|
||||
ErrorResult& aError);
|
||||
|
||||
[[nodiscard]] already_AddRefed<Promise> GetNextPromise(iterator_t* aIterator,
|
||||
ErrorResult& aError);
|
||||
[[nodiscard]] already_AddRefed<Promise> GetNextIterationResult(
|
||||
iterator_t* aIterator, ErrorResult& aError);
|
||||
|
||||
already_AddRefed<Promise> GetFileHandle(
|
||||
const nsAString& aName, const FileSystemGetFileOptions& aOptions,
|
||||
|
|
|
@ -86,7 +86,8 @@ TEST_F(TestFileSystemDirectoryHandle, isNextPromiseReturned) {
|
|||
iterator->Data().mImpl = std::move(mockIter);
|
||||
|
||||
IgnoredErrorResult rv;
|
||||
RefPtr<Promise> promise = dirHandle->GetNextPromise(iterator.get(), rv);
|
||||
RefPtr<Promise> promise =
|
||||
dirHandle->GetNextIterationResult(iterator.get(), rv);
|
||||
ASSERT_TRUE(promise);
|
||||
}
|
||||
|
||||
|
|
|
@ -120,7 +120,12 @@ interface TestInterfaceAsyncIterableSingleWithArgs {
|
|||
[Throws]
|
||||
constructor();
|
||||
|
||||
[GenerateReturnMethod]
|
||||
async iterable<long>(optional TestInterfaceAsyncIteratorOptions options = {});
|
||||
|
||||
readonly attribute long returnCallCount;
|
||||
|
||||
readonly attribute any returnLastCalledWith;
|
||||
};
|
||||
|
||||
[Pref="dom.expose_test_interfaces",
|
||||
|
|
Загрузка…
Ссылка в новой задаче