Index: third_party/WebKit/Source/bindings/core/v8/NativeValueTraitsImpl.h |
diff --git a/third_party/WebKit/Source/bindings/core/v8/NativeValueTraitsImpl.h b/third_party/WebKit/Source/bindings/core/v8/NativeValueTraitsImpl.h |
index 81ac17909631c209bd314b72a5317d50e8c55b72..d033478761cfac44b45198d7b8ec59350cdf7574 100644 |
--- a/third_party/WebKit/Source/bindings/core/v8/NativeValueTraitsImpl.h |
+++ b/third_party/WebKit/Source/bindings/core/v8/NativeValueTraitsImpl.h |
@@ -283,17 +283,121 @@ struct NativeValueTraits<IDLSequence<T>> |
// Nondependent types need to be explicitly qualified to be accessible. |
using typename NativeValueTraitsBase<IDLSequence<T>>::ImplType; |
+ // https://heycam.github.io/webidl/#es-sequence |
static ImplType NativeValue(v8::Isolate* isolate, |
v8::Local<v8::Value> value, |
ExceptionState& exception_state) { |
- return NativeValue(isolate, value, exception_state, 0); |
+ if (!value->IsObject()) { |
+ exception_state.ThrowTypeError( |
+ "The provided value cannot be converted to a sequence."); |
+ return ImplType(); |
+ } |
+ |
+ ImplType result; |
+ // TODO(rakuco): Checking for IsArray() may not be enough. Other engines |
+ // also prefer regular array iteration over a custom @@iterator when the |
+ // latter is defined, but it is not clear if this is a valid optimization. |
+ if (value->IsArray()) { |
+ ConvertSequenceFast(isolate, value.As<v8::Array>(), exception_state, |
+ result); |
+ } else { |
+ ConvertSequenceSlow(isolate, value.As<v8::Object>(), exception_state, |
+ result); |
+ } |
+ |
+ if (exception_state.HadException()) |
+ return ImplType(); |
+ return result; |
} |
- static ImplType NativeValue(v8::Isolate* isolate, |
- v8::Local<v8::Value> value, |
- ExceptionState& exception_state, |
- int index) { |
- return ToImplArray<ImplType, T>(value, index, isolate, exception_state); |
+ private: |
+ // Fast case: we're interating over an Array that adheres to |
+ // %ArrayIteratorPrototype%'s protocol. |
+ static void ConvertSequenceFast(v8::Isolate* isolate, |
+ v8::Local<v8::Array> v8_array, |
+ ExceptionState& exception_state, |
+ ImplType& result) { |
+ const uint32_t length = v8_array->Length(); |
+ if (length > ImplType::MaxCapacity()) { |
+ exception_state.ThrowRangeError("Array length exceeds supported limit."); |
+ return; |
+ } |
+ result.ReserveInitialCapacity(length); |
+ v8::TryCatch block(isolate); |
+ for (uint32_t i = 0; i < length; ++i) { |
+ v8::Local<v8::Value> element; |
+ if (!v8_array->Get(isolate->GetCurrentContext(), i).ToLocal(&element)) { |
+ exception_state.RethrowV8Exception(block.Exception()); |
+ return; |
+ } |
+ result.UncheckedAppend( |
+ NativeValueTraits<T>::NativeValue(isolate, element, exception_state)); |
+ if (exception_state.HadException()) |
+ return; |
+ } |
+ } |
+ |
+ // Slow case: follow WebIDL's "Creating a sequence from an iterable" steps to |
+ // iterate through each element. |
+ // https://heycam.github.io/webidl/#create-sequence-from-iterable |
+ static void ConvertSequenceSlow(v8::Isolate* isolate, |
+ v8::Local<v8::Object> v8_object, |
+ ExceptionState& exception_state, |
+ ImplType& result) { |
+ v8::TryCatch block(isolate); |
+ |
+ v8::Local<v8::Object> iterator = |
+ GetEsIterator(isolate, v8_object, exception_state); |
+ if (exception_state.HadException()) |
+ return; |
+ |
+ v8::Local<v8::String> next_key = V8String(isolate, "next"); |
+ v8::Local<v8::String> value_key = V8String(isolate, "value"); |
+ v8::Local<v8::String> done_key = V8String(isolate, "done"); |
+ v8::Local<v8::Context> context = isolate->GetCurrentContext(); |
+ while (true) { |
+ v8::Local<v8::Value> next; |
+ if (!iterator->Get(context, next_key).ToLocal(&next)) { |
+ exception_state.RethrowV8Exception(block.Exception()); |
+ return; |
+ } |
+ if (!next->IsFunction()) { |
+ exception_state.ThrowTypeError("Iterator.next should be callable."); |
+ return; |
+ } |
+ v8::Local<v8::Value> next_result; |
+ if (!V8ScriptRunner::CallFunction(next.As<v8::Function>(), |
+ ToExecutionContext(context), iterator, |
+ 0, nullptr, isolate) |
+ .ToLocal(&next_result)) { |
+ exception_state.RethrowV8Exception(block.Exception()); |
+ return; |
+ } |
+ if (!next_result->IsObject()) { |
+ exception_state.ThrowTypeError( |
+ "Iterator.next() did not return an object."); |
+ return; |
+ } |
+ v8::Local<v8::Object> result_object = next_result.As<v8::Object>(); |
+ v8::Local<v8::Value> element; |
+ v8::Local<v8::Value> done; |
+ if (!result_object->Get(context, value_key).ToLocal(&element) || |
+ !result_object->Get(context, done_key).ToLocal(&done)) { |
+ exception_state.RethrowV8Exception(block.Exception()); |
+ return; |
+ } |
+ bool done_boolean; |
+ if (!done->BooleanValue(context).To(&done_boolean)) { |
+ exception_state.RethrowV8Exception(block.Exception()); |
+ return; |
+ } |
+ if (done_boolean) |
+ break; |
+ result.emplace_back( |
+ NativeValueTraits<T>::NativeValue(isolate, element, exception_state)); |
+ if (exception_state.HadException()) |
+ return; |
+ } |
} |
}; |