no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / dom / bindings / IterableIterator.h
blobdb9969da370f9e785a4cf9818dc5b0ab8d4821bf
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /**
8 * The IterableIterator class is used for WebIDL interfaces that have a
9 * iterable<> member defined with two types (so a pair iterator). It handles
10 * the ES6 Iterator-like functions that are generated for the iterable
11 * interface.
13 * For iterable interfaces with a pair iterator, the implementation class will
14 * need to implement these two functions:
16 * - size_t GetIterableLength()
17 * - Returns the number of elements available to iterate over
18 * - [type] GetValueAtIndex(size_t index)
19 * - Returns the value at the requested index.
20 * - [type] GetKeyAtIndex(size_t index)
21 * - Returns the key at the requested index
23 * Examples of iterable interface implementations can be found in the bindings
24 * test directory.
27 #ifndef mozilla_dom_IterableIterator_h
28 #define mozilla_dom_IterableIterator_h
30 #include "js/RootingAPI.h"
31 #include "js/TypeDecls.h"
32 #include "js/Value.h"
33 #include "nsISupports.h"
34 #include "mozilla/AlreadyAddRefed.h"
35 #include "mozilla/dom/IterableIteratorBinding.h"
36 #include "mozilla/dom/Promise.h"
37 #include "mozilla/dom/RootedDictionary.h"
38 #include "mozilla/dom/ToJSValue.h"
39 #include "mozilla/WeakPtr.h"
41 namespace mozilla::dom {
43 namespace binding_details {
45 // JS::MagicValue(END_OF_ITERATION) is the value we use for
46 // https://webidl.spec.whatwg.org/#end-of-iteration. It shouldn't be returned to
47 // JS, because AsyncIterableIteratorBase::NextSteps will detect it and will
48 // return the result of CreateIterResultObject(undefined, true) instead
49 // (discarding the magic value).
50 static const JSWhyMagic END_OF_ITERATION = JS_GENERIC_MAGIC;
52 } // namespace binding_details
54 namespace iterator_utils {
56 void DictReturn(JSContext* aCx, JS::MutableHandle<JSObject*> aResult,
57 bool aDone, JS::Handle<JS::Value> aValue, ErrorResult& aRv);
59 void KeyAndValueReturn(JSContext* aCx, JS::Handle<JS::Value> aKey,
60 JS::Handle<JS::Value> aValue,
61 JS::MutableHandle<JSObject*> aResult, ErrorResult& aRv);
63 inline void ResolvePromiseForFinished(Promise* aPromise) {
64 aPromise->MaybeResolve(JS::MagicValue(binding_details::END_OF_ITERATION));
67 template <typename Key, typename Value>
68 void ResolvePromiseWithKeyAndValue(Promise* aPromise, const Key& aKey,
69 const Value& aValue) {
70 aPromise->MaybeResolve(std::make_tuple(aKey, aValue));
73 } // namespace iterator_utils
75 class IterableIteratorBase {
76 public:
77 NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(IterableIteratorBase)
78 NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(IterableIteratorBase)
80 typedef enum { Keys = 0, Values, Entries } IteratorType;
82 IterableIteratorBase() = default;
84 protected:
85 virtual ~IterableIteratorBase() = default;
86 virtual void UnlinkHelper() = 0;
87 virtual void TraverseHelper(nsCycleCollectionTraversalCallback& cb) = 0;
90 // Helpers to call iterator getter methods with the correct arguments, depending
91 // on the types they return, and convert the result to JS::Values.
93 // Helper for Get[Key,Value]AtIndex(uint32_t) methods, which accept an index and
94 // return a type supported by ToJSValue.
95 template <typename T, typename U>
96 bool CallIterableGetter(JSContext* aCx, U (T::*aMethod)(uint32_t), T* aInst,
97 uint32_t aIndex, JS::MutableHandle<JS::Value> aResult) {
98 return ToJSValue(aCx, (aInst->*aMethod)(aIndex), aResult);
101 template <typename T, typename U>
102 bool CallIterableGetter(JSContext* aCx, U (T::*aMethod)(uint32_t) const,
103 const T* aInst, uint32_t aIndex,
104 JS::MutableHandle<JS::Value> aResult) {
105 return ToJSValue(aCx, (aInst->*aMethod)(aIndex), aResult);
108 // Helper for Get[Key,Value]AtIndex(JSContext*, uint32_t, MutableHandleValue)
109 // methods, which accept a JS context, index, and mutable result value handle,
110 // and return true on success or false on failure.
111 template <typename T>
112 bool CallIterableGetter(JSContext* aCx,
113 bool (T::*aMethod)(JSContext*, uint32_t,
114 JS::MutableHandle<JS::Value>),
115 T* aInst, uint32_t aIndex,
116 JS::MutableHandle<JS::Value> aResult) {
117 return (aInst->*aMethod)(aCx, aIndex, aResult);
120 template <typename T>
121 bool CallIterableGetter(JSContext* aCx,
122 bool (T::*aMethod)(JSContext*, uint32_t,
123 JS::MutableHandle<JS::Value>) const,
124 const T* aInst, uint32_t aIndex,
125 JS::MutableHandle<JS::Value> aResult) {
126 return (aInst->*aMethod)(aCx, aIndex, aResult);
129 template <typename T>
130 class IterableIterator : public IterableIteratorBase {
131 public:
132 IterableIterator(T* aIterableObj, IteratorType aIteratorType)
133 : mIterableObj(aIterableObj), mIteratorType(aIteratorType), mIndex(0) {
134 MOZ_ASSERT(mIterableObj);
137 bool GetKeyAtIndex(JSContext* aCx, uint32_t aIndex,
138 JS::MutableHandle<JS::Value> aResult) {
139 return CallIterableGetter(aCx, &T::GetKeyAtIndex, mIterableObj.get(),
140 aIndex, aResult);
143 bool GetValueAtIndex(JSContext* aCx, uint32_t aIndex,
144 JS::MutableHandle<JS::Value> aResult) {
145 return CallIterableGetter(aCx, &T::GetValueAtIndex, mIterableObj.get(),
146 aIndex, aResult);
149 void Next(JSContext* aCx, JS::MutableHandle<JSObject*> aResult,
150 ErrorResult& aRv) {
151 JS::Rooted<JS::Value> value(aCx, JS::UndefinedValue());
152 if (mIndex >= this->mIterableObj->GetIterableLength()) {
153 iterator_utils::DictReturn(aCx, aResult, true, value, aRv);
154 return;
156 switch (mIteratorType) {
157 case IteratorType::Keys: {
158 if (!GetKeyAtIndex(aCx, mIndex, &value)) {
159 aRv.Throw(NS_ERROR_FAILURE);
160 return;
162 iterator_utils::DictReturn(aCx, aResult, false, value, aRv);
163 break;
165 case IteratorType::Values: {
166 if (!GetValueAtIndex(aCx, mIndex, &value)) {
167 aRv.Throw(NS_ERROR_FAILURE);
168 return;
170 iterator_utils::DictReturn(aCx, aResult, false, value, aRv);
171 break;
173 case IteratorType::Entries: {
174 JS::Rooted<JS::Value> key(aCx);
175 if (!GetKeyAtIndex(aCx, mIndex, &key)) {
176 aRv.Throw(NS_ERROR_FAILURE);
177 return;
179 if (!GetValueAtIndex(aCx, mIndex, &value)) {
180 aRv.Throw(NS_ERROR_FAILURE);
181 return;
183 iterator_utils::KeyAndValueReturn(aCx, key, value, aResult, aRv);
184 break;
186 default:
187 MOZ_CRASH("Invalid iterator type!");
189 ++mIndex;
192 protected:
193 virtual ~IterableIterator() = default;
195 // Since we're templated on a binding, we need to possibly CC it, but can't do
196 // that through macros. So it happens here.
197 void UnlinkHelper() final { mIterableObj = nullptr; }
199 virtual void TraverseHelper(nsCycleCollectionTraversalCallback& cb) override {
200 IterableIterator<T>* tmp = this;
201 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIterableObj);
204 // Binding Implementation object that we're iterating over.
205 RefPtr<T> mIterableObj;
206 // Tells whether this is a key, value, or entries iterator.
207 IteratorType mIteratorType;
208 // Current index of iteration.
209 uint32_t mIndex;
212 namespace binding_detail {
214 class AsyncIterableNextImpl;
215 class AsyncIterableReturnImpl;
217 } // namespace binding_detail
219 class AsyncIterableIteratorBase : public IterableIteratorBase {
220 public:
221 IteratorType GetIteratorType() { return mIteratorType; }
223 protected:
224 explicit AsyncIterableIteratorBase(IteratorType aIteratorType)
225 : mIteratorType(aIteratorType) {}
227 void UnlinkHelper() override {
228 AsyncIterableIteratorBase* tmp = this;
229 NS_IMPL_CYCLE_COLLECTION_UNLINK(mOngoingPromise);
232 void TraverseHelper(nsCycleCollectionTraversalCallback& cb) override {
233 AsyncIterableIteratorBase* tmp = this;
234 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOngoingPromise);
237 private:
238 friend class binding_detail::AsyncIterableNextImpl;
239 friend class binding_detail::AsyncIterableReturnImpl;
241 // 3.7.10.1. Default asynchronous iterator objects
242 // Target is in AsyncIterableIterator
243 // Kind
244 IteratorType mIteratorType;
245 // Ongoing promise
246 RefPtr<Promise> mOngoingPromise;
247 // Is finished
248 bool mIsFinished = false;
251 template <typename T>
252 class AsyncIterableIterator : public AsyncIterableIteratorBase {
253 private:
254 using IteratorData = typename T::IteratorData;
256 public:
257 AsyncIterableIterator(T* aIterableObj, IteratorType aIteratorType)
258 : AsyncIterableIteratorBase(aIteratorType), mIterableObj(aIterableObj) {
259 MOZ_ASSERT(mIterableObj);
262 IteratorData& Data() { return mData; }
264 protected:
265 // We'd prefer to use ImplCycleCollectionTraverse/ImplCycleCollectionUnlink on
266 // the iterator data, but unfortunately that doesn't work because it's
267 // dependent on the template parameter. Instead we detect if the data
268 // structure has Traverse and Unlink functions and call those.
269 template <typename Data>
270 auto TraverseData(Data& aData, nsCycleCollectionTraversalCallback& aCallback,
271 int) -> decltype(aData.Traverse(aCallback)) {
272 return aData.Traverse(aCallback);
274 template <typename Data>
275 void TraverseData(Data& aData, nsCycleCollectionTraversalCallback& aCallback,
276 double) {}
278 template <typename Data>
279 auto UnlinkData(Data& aData, int) -> decltype(aData.Unlink()) {
280 return aData.Unlink();
282 template <typename Data>
283 void UnlinkData(Data& aData, double) {}
285 // Since we're templated on a binding, we need to possibly CC it, but can't do
286 // that through macros. So it happens here.
287 void UnlinkHelper() final {
288 AsyncIterableIteratorBase::UnlinkHelper();
290 AsyncIterableIterator<T>* tmp = this;
291 NS_IMPL_CYCLE_COLLECTION_UNLINK(mIterableObj);
292 UnlinkData(tmp->mData, 0);
295 void TraverseHelper(nsCycleCollectionTraversalCallback& cb) final {
296 AsyncIterableIteratorBase::TraverseHelper(cb);
298 AsyncIterableIterator<T>* tmp = this;
299 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIterableObj);
300 TraverseData(tmp->mData, cb, 0);
303 // 3.7.10.1. Default asynchronous iterator objects
304 // Target
305 RefPtr<T> mIterableObj;
306 // Kind
307 // Ongoing promise
308 // Is finished
309 // See AsyncIterableIteratorBase
311 // Opaque data of the backing object.
312 IteratorData mData;
315 namespace binding_detail {
317 template <typename T>
318 using IterableIteratorWrapFunc =
319 bool (*)(JSContext* aCx, IterableIterator<T>* aObject,
320 JS::MutableHandle<JSObject*> aReflector);
322 template <typename T, IterableIteratorWrapFunc<T> WrapFunc>
323 class WrappableIterableIterator final : public IterableIterator<T> {
324 public:
325 using IterableIterator<T>::IterableIterator;
327 bool WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto,
328 JS::MutableHandle<JSObject*> aObj) {
329 MOZ_ASSERT(!aGivenProto);
330 return (*WrapFunc)(aCx, this, aObj);
334 class AsyncIterableNextImpl {
335 protected:
336 MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> Next(
337 JSContext* aCx, AsyncIterableIteratorBase* aObject,
338 nsISupports* aGlobalObject, ErrorResult& aRv);
339 MOZ_CAN_RUN_SCRIPT virtual already_AddRefed<Promise> GetNextResult(
340 ErrorResult& aRv) = 0;
342 private:
343 MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> NextSteps(
344 JSContext* aCx, AsyncIterableIteratorBase* aObject,
345 nsIGlobalObject* aGlobalObject, ErrorResult& aRv);
348 class AsyncIterableReturnImpl {
349 protected:
350 MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> Return(
351 JSContext* aCx, AsyncIterableIteratorBase* aObject,
352 nsISupports* aGlobalObject, JS::Handle<JS::Value> aValue,
353 ErrorResult& aRv);
354 MOZ_CAN_RUN_SCRIPT virtual already_AddRefed<Promise> GetReturnPromise(
355 JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv) = 0;
357 private:
358 MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> ReturnSteps(
359 JSContext* aCx, AsyncIterableIteratorBase* aObject,
360 nsIGlobalObject* aGlobalObject, JS::Handle<JS::Value> aValue,
361 ErrorResult& aRv);
364 template <typename T>
365 class AsyncIterableIteratorNoReturn : public AsyncIterableIterator<T>,
366 public AsyncIterableNextImpl {
367 public:
368 using AsyncIterableIterator<T>::AsyncIterableIterator;
370 MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> Next(JSContext* aCx,
371 ErrorResult& aRv) {
372 nsCOMPtr<nsISupports> parentObject = this->mIterableObj->GetParentObject();
373 return AsyncIterableNextImpl::Next(aCx, this, parentObject, aRv);
376 protected:
377 MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> GetNextResult(
378 ErrorResult& aRv) override {
379 RefPtr<T> iterableObj(this->mIterableObj);
380 return iterableObj->GetNextIterationResult(
381 static_cast<AsyncIterableIterator<T>*>(this), aRv);
385 template <typename T>
386 class AsyncIterableIteratorWithReturn : public AsyncIterableIteratorNoReturn<T>,
387 public AsyncIterableReturnImpl {
388 public:
389 MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> Return(
390 JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv) {
391 nsCOMPtr<nsISupports> parentObject = this->mIterableObj->GetParentObject();
392 return AsyncIterableReturnImpl::Return(aCx, this, parentObject, aValue,
393 aRv);
396 protected:
397 using AsyncIterableIteratorNoReturn<T>::AsyncIterableIteratorNoReturn;
399 MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> GetReturnPromise(
400 JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv) override {
401 RefPtr<T> iterableObj(this->mIterableObj);
402 return iterableObj->IteratorReturn(
403 aCx, static_cast<AsyncIterableIterator<T>*>(this), aValue, aRv);
407 template <typename T, bool NeedReturnMethod>
408 using AsyncIterableIteratorNative =
409 std::conditional_t<NeedReturnMethod, AsyncIterableIteratorWithReturn<T>,
410 AsyncIterableIteratorNoReturn<T>>;
412 template <typename T, bool NeedReturnMethod>
413 using AsyncIterableIteratorWrapFunc = bool (*)(
414 JSContext* aCx, AsyncIterableIteratorNative<T, NeedReturnMethod>* aObject,
415 JS::MutableHandle<JSObject*> aReflector);
417 template <typename T, bool NeedReturnMethod,
418 AsyncIterableIteratorWrapFunc<T, NeedReturnMethod> WrapFunc,
419 typename Base = AsyncIterableIteratorNative<T, NeedReturnMethod>>
420 class WrappableAsyncIterableIterator final : public Base {
421 public:
422 using Base::Base;
424 bool WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto,
425 JS::MutableHandle<JSObject*> aObj) {
426 MOZ_ASSERT(!aGivenProto);
427 return (*WrapFunc)(aCx, this, aObj);
431 } // namespace binding_detail
433 } // namespace mozilla::dom
435 #endif // mozilla_dom_IterableIterator_h