Bug 1707290 [wpt PR 28671] - Auto-expand details elements for find-in-page, a=testonly
[gecko.git] / dom / bindings / IterableIterator.h
blob590c70e3ac0cd8a0b68cda6ef8249451bb2cc087
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/TypeDecls.h"
31 #include "js/Value.h"
32 #include "nsISupports.h"
33 #include "mozilla/dom/IterableIteratorBinding.h"
34 #include "mozilla/dom/RootedDictionary.h"
35 #include "mozilla/dom/ToJSValue.h"
37 namespace mozilla {
38 namespace dom {
40 class IterableIteratorBase : public nsISupports {
41 public:
42 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
43 NS_DECL_CYCLE_COLLECTION_CLASS(IterableIteratorBase)
44 typedef enum { Keys = 0, Values, Entries } IterableIteratorType;
46 IterableIteratorBase() = default;
48 protected:
49 virtual ~IterableIteratorBase() = default;
50 virtual void UnlinkHelper() = 0;
51 virtual void TraverseHelper(nsCycleCollectionTraversalCallback& cb) = 0;
54 // Helpers to call iterator getter methods with the correct arguments, depending
55 // on the types they return, and convert the result to JS::Values.
57 // Helper for Get[Key,Value]AtIndex(uint32_t) methods, which accept an index and
58 // return a type supported by ToJSValue.
59 template <typename T, typename U>
60 bool CallIterableGetter(JSContext* aCx, U (T::*aMethod)(uint32_t), T* aInst,
61 uint32_t aIndex, JS::MutableHandle<JS::Value> aResult) {
62 return ToJSValue(aCx, (aInst->*aMethod)(aIndex), aResult);
65 template <typename T, typename U>
66 bool CallIterableGetter(JSContext* aCx, U (T::*aMethod)(uint32_t) const,
67 const T* aInst, uint32_t aIndex,
68 JS::MutableHandle<JS::Value> aResult) {
69 return ToJSValue(aCx, (aInst->*aMethod)(aIndex), aResult);
72 // Helper for Get[Key,Value]AtIndex(JSContext*, uint32_t, MutableHandleValue)
73 // methods, which accept a JS context, index, and mutable result value handle,
74 // and return true on success or false on failure.
75 template <typename T>
76 bool CallIterableGetter(JSContext* aCx,
77 bool (T::*aMethod)(JSContext*, uint32_t,
78 JS::MutableHandle<JS::Value>),
79 T* aInst, uint32_t aIndex,
80 JS::MutableHandle<JS::Value> aResult) {
81 return (aInst->*aMethod)(aCx, aIndex, aResult);
84 template <typename T>
85 bool CallIterableGetter(JSContext* aCx,
86 bool (T::*aMethod)(JSContext*, uint32_t,
87 JS::MutableHandle<JS::Value>) const,
88 const T* aInst, uint32_t aIndex,
89 JS::MutableHandle<JS::Value> aResult) {
90 return (aInst->*aMethod)(aCx, aIndex, aResult);
93 template <typename T>
94 class IterableIterator final : public IterableIteratorBase {
95 public:
96 typedef bool (*WrapFunc)(JSContext* aCx, IterableIterator<T>* aObject,
97 JS::Handle<JSObject*> aGivenProto,
98 JS::MutableHandle<JSObject*> aReflector);
100 explicit IterableIterator(T* aIterableObj, IterableIteratorType aIteratorType,
101 WrapFunc aWrapFunc)
102 : mIterableObj(aIterableObj),
103 mIteratorType(aIteratorType),
104 mWrapFunc(aWrapFunc),
105 mIndex(0) {
106 MOZ_ASSERT(mIterableObj);
107 MOZ_ASSERT(mWrapFunc);
110 bool GetKeyAtIndex(JSContext* aCx, uint32_t aIndex,
111 JS::MutableHandle<JS::Value> aResult) {
112 return CallIterableGetter(aCx, &T::GetKeyAtIndex, mIterableObj.get(),
113 aIndex, aResult);
116 bool GetValueAtIndex(JSContext* aCx, uint32_t aIndex,
117 JS::MutableHandle<JS::Value> aResult) {
118 return CallIterableGetter(aCx, &T::GetValueAtIndex, mIterableObj.get(),
119 aIndex, aResult);
122 void Next(JSContext* aCx, JS::MutableHandle<JSObject*> aResult,
123 ErrorResult& aRv) {
124 JS::Rooted<JS::Value> value(aCx, JS::UndefinedValue());
125 if (mIndex >= this->mIterableObj->GetIterableLength()) {
126 DictReturn(aCx, aResult, true, value, aRv);
127 return;
129 switch (mIteratorType) {
130 case IterableIteratorType::Keys: {
131 if (!GetKeyAtIndex(aCx, mIndex, &value)) {
132 aRv.Throw(NS_ERROR_FAILURE);
133 return;
135 DictReturn(aCx, aResult, false, value, aRv);
136 break;
138 case IterableIteratorType::Values: {
139 if (!GetValueAtIndex(aCx, mIndex, &value)) {
140 aRv.Throw(NS_ERROR_FAILURE);
141 return;
143 DictReturn(aCx, aResult, false, value, aRv);
144 break;
146 case IterableIteratorType::Entries: {
147 JS::Rooted<JS::Value> key(aCx);
148 if (!GetKeyAtIndex(aCx, mIndex, &key)) {
149 aRv.Throw(NS_ERROR_FAILURE);
150 return;
152 if (!GetValueAtIndex(aCx, mIndex, &value)) {
153 aRv.Throw(NS_ERROR_FAILURE);
154 return;
156 KeyAndValueReturn(aCx, key, value, aResult, aRv);
157 break;
159 default:
160 MOZ_CRASH("Invalid iterator type!");
162 ++mIndex;
165 bool WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto,
166 JS::MutableHandle<JSObject*> aObj) {
167 return (*mWrapFunc)(aCx, this, aGivenProto, aObj);
170 protected:
171 static void DictReturn(JSContext* aCx, JS::MutableHandle<JSObject*> aResult,
172 bool aDone, JS::Handle<JS::Value> aValue,
173 ErrorResult& aRv) {
174 RootedDictionary<IterableKeyOrValueResult> dict(aCx);
175 dict.mDone = aDone;
176 dict.mValue = aValue;
177 JS::Rooted<JS::Value> dictValue(aCx);
178 if (!ToJSValue(aCx, dict, &dictValue)) {
179 aRv.Throw(NS_ERROR_FAILURE);
180 return;
182 aResult.set(&dictValue.toObject());
185 static void KeyAndValueReturn(JSContext* aCx, JS::Handle<JS::Value> aKey,
186 JS::Handle<JS::Value> aValue,
187 JS::MutableHandle<JSObject*> aResult,
188 ErrorResult& aRv) {
189 RootedDictionary<IterableKeyAndValueResult> dict(aCx);
190 dict.mDone = false;
191 // Dictionary values are a Sequence, which is a FallibleTArray, so we need
192 // to check returns when appending.
193 if (!dict.mValue.AppendElement(aKey, mozilla::fallible)) {
194 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
195 return;
197 if (!dict.mValue.AppendElement(aValue, mozilla::fallible)) {
198 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
199 return;
201 JS::Rooted<JS::Value> dictValue(aCx);
202 if (!ToJSValue(aCx, dict, &dictValue)) {
203 aRv.Throw(NS_ERROR_FAILURE);
204 return;
206 aResult.set(&dictValue.toObject());
209 protected:
210 virtual ~IterableIterator() = default;
212 // Since we're templated on a binding, we need to possibly CC it, but can't do
213 // that through macros. So it happens here.
214 void UnlinkHelper() final { mIterableObj = nullptr; }
216 virtual void TraverseHelper(nsCycleCollectionTraversalCallback& cb) override {
217 IterableIterator<T>* tmp = this;
218 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIterableObj);
221 // Binding Implementation object that we're iterating over.
222 RefPtr<T> mIterableObj;
223 // Tells whether this is a key, value, or entries iterator.
224 IterableIteratorType mIteratorType;
225 // Function pointer to binding-type-specific Wrap() call for this iterator.
226 WrapFunc mWrapFunc;
227 // Current index of iteration.
228 uint32_t mIndex;
231 } // namespace dom
232 } // namespace mozilla
234 #endif // mozilla_dom_IterableIterator_h