blob: d5087624a6888067ff198f97c5f255604798de04 [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef Iterable_h
#define Iterable_h
#include "bindings/core/v8/V8IteratorResultValue.h"
#include "bindings/core/v8/V8ScriptRunner.h"
#include "core/dom/Iterator.h"
namespace blink {
// Typically, you should use PairIterable<> (below) instead.
// Also, note that value iterators are set up automatically by the bindings
// code and the operations below come directly from V8.
template <typename KeyType, typename ValueType>
class Iterable {
public:
Iterator* keysForBinding(ScriptState* scriptState,
ExceptionState& exceptionState) {
IterationSource* source = this->startIteration(scriptState, exceptionState);
if (!source)
return nullptr;
return new IterableIterator<KeySelector>(source);
}
Iterator* valuesForBinding(ScriptState* scriptState,
ExceptionState& exceptionState) {
IterationSource* source = this->startIteration(scriptState, exceptionState);
if (!source)
return nullptr;
return new IterableIterator<ValueSelector>(source);
}
Iterator* entriesForBinding(ScriptState* scriptState,
ExceptionState& exceptionState) {
IterationSource* source = this->startIteration(scriptState, exceptionState);
if (!source)
return nullptr;
return new IterableIterator<EntrySelector>(source);
}
void forEachForBinding(ScriptState* scriptState,
const ScriptValue& thisValue,
const ScriptValue& callback,
const ScriptValue& thisArg,
ExceptionState& exceptionState) {
IterationSource* source = this->startIteration(scriptState, exceptionState);
v8::Isolate* isolate = scriptState->isolate();
v8::TryCatch tryCatch(isolate);
v8::Local<v8::Object> creationContext(thisValue.v8Value().As<v8::Object>());
v8::Local<v8::Function> v8Callback(callback.v8Value().As<v8::Function>());
v8::Local<v8::Value> v8ThisArg(thisArg.v8Value());
v8::Local<v8::Value> args[3];
args[2] = thisValue.v8Value();
while (true) {
KeyType key;
ValueType value;
if (!source->next(scriptState, key, value, exceptionState))
return;
ASSERT(!exceptionState.hadException());
args[0] = ToV8(value, creationContext, isolate);
args[1] = ToV8(key, creationContext, isolate);
if (args[0].IsEmpty() || args[1].IsEmpty()) {
if (tryCatch.HasCaught())
exceptionState.rethrowV8Exception(tryCatch.Exception());
return;
}
v8::Local<v8::Value> result;
if (!V8ScriptRunner::callFunction(v8Callback,
scriptState->getExecutionContext(),
v8ThisArg, 3, args, isolate)
.ToLocal(&result)) {
exceptionState.rethrowV8Exception(tryCatch.Exception());
return;
}
}
}
class IterationSource : public GarbageCollectedFinalized<IterationSource> {
public:
virtual ~IterationSource() {}
// If end of iteration has been reached or an exception thrown: return
// false. Otherwise: set |key| and |value| and return true.
virtual bool next(ScriptState*, KeyType&, ValueType&, ExceptionState&) = 0;
DEFINE_INLINE_VIRTUAL_TRACE() {}
};
private:
virtual IterationSource* startIteration(ScriptState*, ExceptionState&) = 0;
struct KeySelector {
STATIC_ONLY(KeySelector);
static const KeyType& select(ScriptState*,
const KeyType& key,
const ValueType& value) {
return key;
}
};
struct ValueSelector {
STATIC_ONLY(ValueSelector);
static const ValueType& select(ScriptState*,
const KeyType& key,
const ValueType& value) {
return value;
}
};
struct EntrySelector {
STATIC_ONLY(EntrySelector);
static Vector<ScriptValue, 2> select(ScriptState* scriptState,
const KeyType& key,
const ValueType& value) {
v8::Local<v8::Object> creationContext = scriptState->context()->Global();
v8::Isolate* isolate = scriptState->isolate();
Vector<ScriptValue, 2> entry;
entry.push_back(
ScriptValue(scriptState, ToV8(key, creationContext, isolate)));
entry.push_back(
ScriptValue(scriptState, ToV8(value, creationContext, isolate)));
return entry;
}
};
template <typename Selector>
class IterableIterator final : public Iterator {
public:
explicit IterableIterator(IterationSource* source) : m_source(source) {}
ScriptValue next(ScriptState* scriptState,
ExceptionState& exceptionState) override {
KeyType key;
ValueType value;
if (!m_source->next(scriptState, key, value, exceptionState))
return v8IteratorResultDone(scriptState);
return v8IteratorResult(scriptState,
Selector::select(scriptState, key, value));
}
ScriptValue next(ScriptState* scriptState,
ScriptValue,
ExceptionState& exceptionState) override {
return next(scriptState, exceptionState);
}
DEFINE_INLINE_VIRTUAL_TRACE() {
visitor->trace(m_source);
Iterator::trace(visitor);
}
private:
Member<IterationSource> m_source;
};
};
// Utiltity mixin base-class for classes implementing IDL interfaces with
// "iterable<T1, T2>".
template <typename KeyType, typename ValueType>
class PairIterable : public Iterable<KeyType, ValueType> {
public:
Iterator* iterator(ScriptState* scriptState, ExceptionState& exceptionState) {
return this->entriesForBinding(scriptState, exceptionState);
}
};
} // namespace blink
#endif // Iterable_h