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/. */
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
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
27 #ifndef mozilla_dom_IterableIterator_h
28 #define mozilla_dom_IterableIterator_h
30 #include "js/TypeDecls.h"
32 #include "nsISupports.h"
33 #include "mozilla/dom/IterableIteratorBinding.h"
34 #include "mozilla/dom/RootedDictionary.h"
35 #include "mozilla/dom/ToJSValue.h"
40 class IterableIteratorBase
: public nsISupports
{
42 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
43 NS_DECL_CYCLE_COLLECTION_CLASS(IterableIteratorBase
)
44 typedef enum { Keys
= 0, Values
, Entries
} IterableIteratorType
;
46 IterableIteratorBase() = default;
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.
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
);
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
);
94 class IterableIterator final
: public IterableIteratorBase
{
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
,
102 : mIterableObj(aIterableObj
),
103 mIteratorType(aIteratorType
),
104 mWrapFunc(aWrapFunc
),
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(),
116 bool GetValueAtIndex(JSContext
* aCx
, uint32_t aIndex
,
117 JS::MutableHandle
<JS::Value
> aResult
) {
118 return CallIterableGetter(aCx
, &T::GetValueAtIndex
, mIterableObj
.get(),
122 void Next(JSContext
* aCx
, JS::MutableHandle
<JSObject
*> aResult
,
124 JS::Rooted
<JS::Value
> value(aCx
, JS::UndefinedValue());
125 if (mIndex
>= this->mIterableObj
->GetIterableLength()) {
126 DictReturn(aCx
, aResult
, true, value
, aRv
);
129 switch (mIteratorType
) {
130 case IterableIteratorType::Keys
: {
131 if (!GetKeyAtIndex(aCx
, mIndex
, &value
)) {
132 aRv
.Throw(NS_ERROR_FAILURE
);
135 DictReturn(aCx
, aResult
, false, value
, aRv
);
138 case IterableIteratorType::Values
: {
139 if (!GetValueAtIndex(aCx
, mIndex
, &value
)) {
140 aRv
.Throw(NS_ERROR_FAILURE
);
143 DictReturn(aCx
, aResult
, false, value
, aRv
);
146 case IterableIteratorType::Entries
: {
147 JS::Rooted
<JS::Value
> key(aCx
);
148 if (!GetKeyAtIndex(aCx
, mIndex
, &key
)) {
149 aRv
.Throw(NS_ERROR_FAILURE
);
152 if (!GetValueAtIndex(aCx
, mIndex
, &value
)) {
153 aRv
.Throw(NS_ERROR_FAILURE
);
156 KeyAndValueReturn(aCx
, key
, value
, aResult
, aRv
);
160 MOZ_CRASH("Invalid iterator type!");
165 bool WrapObject(JSContext
* aCx
, JS::Handle
<JSObject
*> aGivenProto
,
166 JS::MutableHandle
<JSObject
*> aObj
) {
167 return (*mWrapFunc
)(aCx
, this, aGivenProto
, aObj
);
171 static void DictReturn(JSContext
* aCx
, JS::MutableHandle
<JSObject
*> aResult
,
172 bool aDone
, JS::Handle
<JS::Value
> aValue
,
174 RootedDictionary
<IterableKeyOrValueResult
> dict(aCx
);
176 dict
.mValue
= aValue
;
177 JS::Rooted
<JS::Value
> dictValue(aCx
);
178 if (!ToJSValue(aCx
, dict
, &dictValue
)) {
179 aRv
.Throw(NS_ERROR_FAILURE
);
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
,
189 RootedDictionary
<IterableKeyAndValueResult
> dict(aCx
);
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
);
197 if (!dict
.mValue
.AppendElement(aValue
, mozilla::fallible
)) {
198 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
201 JS::Rooted
<JS::Value
> dictValue(aCx
);
202 if (!ToJSValue(aCx
, dict
, &dictValue
)) {
203 aRv
.Throw(NS_ERROR_FAILURE
);
206 aResult
.set(&dictValue
.toObject());
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.
227 // Current index of iteration.
232 } // namespace mozilla
234 #endif // mozilla_dom_IterableIterator_h