blob: ca71a4f04589be083cc8219d0adb320d767e10c4 [file] [log] [blame]
// Copyright (c) 2017 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 THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_NATIVE_VALUE_TRAITS_IMPL_H_
#define THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_NATIVE_VALUE_TRAITS_IMPL_H_
#include "third_party/blink/renderer/bindings/core/v8/idl_types.h"
#include "third_party/blink/renderer/bindings/core/v8/native_value_traits.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
namespace blink {
class CallbackFunctionBase;
// Boolean
template <>
struct CORE_EXPORT NativeValueTraits<IDLBoolean>
: public NativeValueTraitsBase<IDLBoolean> {
static bool NativeValue(v8::Isolate* isolate,
v8::Local<v8::Value> value,
ExceptionState& exception_state) {
return ToBoolean(isolate, value, exception_state);
}
};
// Integers
//
// All integer specializations offer a second nativeValue() besides the default
// one: it takes an IntegerConversionConfiguration argument to let callers
// specify how the integers should be converted. The default nativeValue()
// overload will always use NormalConversion.
template <>
struct CORE_EXPORT NativeValueTraits<IDLByte>
: public NativeValueTraitsBase<IDLByte> {
static int8_t NativeValue(v8::Isolate* isolate,
v8::Local<v8::Value> value,
ExceptionState& exception_state) {
return ToInt8(isolate, value, kNormalConversion, exception_state);
}
};
template <>
struct CORE_EXPORT NativeValueTraits<IDLOctet>
: public NativeValueTraitsBase<IDLOctet> {
static uint8_t NativeValue(v8::Isolate* isolate,
v8::Local<v8::Value> value,
ExceptionState& exception_state) {
return ToUInt8(isolate, value, kNormalConversion, exception_state);
}
};
template <>
struct CORE_EXPORT NativeValueTraits<IDLShort>
: public NativeValueTraitsBase<IDLShort> {
static int16_t NativeValue(v8::Isolate* isolate,
v8::Local<v8::Value> value,
ExceptionState& exception_state) {
return ToInt16(isolate, value, kNormalConversion, exception_state);
}
};
template <>
struct CORE_EXPORT NativeValueTraits<IDLUnsignedShort>
: public NativeValueTraitsBase<IDLUnsignedShort> {
static uint16_t NativeValue(v8::Isolate* isolate,
v8::Local<v8::Value> value,
ExceptionState& exception_state) {
return ToUInt16(isolate, value, kNormalConversion, exception_state);
}
};
template <>
struct CORE_EXPORT NativeValueTraits<IDLLong>
: public NativeValueTraitsBase<IDLLong> {
static int32_t NativeValue(v8::Isolate* isolate,
v8::Local<v8::Value> value,
ExceptionState& exception_state) {
return ToInt32(isolate, value, kNormalConversion, exception_state);
}
};
template <>
struct CORE_EXPORT NativeValueTraits<IDLUnsignedLong>
: public NativeValueTraitsBase<IDLUnsignedLong> {
static uint32_t NativeValue(v8::Isolate* isolate,
v8::Local<v8::Value> value,
ExceptionState& exception_state) {
return ToUInt32(isolate, value, kNormalConversion, exception_state);
}
};
template <>
struct CORE_EXPORT NativeValueTraits<IDLLongLong>
: public NativeValueTraitsBase<IDLLongLong> {
static int64_t NativeValue(v8::Isolate* isolate,
v8::Local<v8::Value> value,
ExceptionState& exception_state) {
return ToInt64(isolate, value, kNormalConversion, exception_state);
}
};
template <>
struct CORE_EXPORT NativeValueTraits<IDLUnsignedLongLong>
: public NativeValueTraitsBase<IDLUnsignedLongLong> {
static uint64_t NativeValue(v8::Isolate* isolate,
v8::Local<v8::Value> value,
ExceptionState& exception_state) {
return ToUInt64(isolate, value, kNormalConversion, exception_state);
}
};
// [Clamp] Integers
template <>
struct CORE_EXPORT NativeValueTraits<IDLByteClamp>
: public NativeValueTraitsBase<IDLByte> {
static int8_t NativeValue(v8::Isolate* isolate,
v8::Local<v8::Value> value,
ExceptionState& exception_state) {
return ToInt8(isolate, value, kClamp, exception_state);
}
};
template <>
struct CORE_EXPORT NativeValueTraits<IDLOctetClamp>
: public NativeValueTraitsBase<IDLOctet> {
static uint8_t NativeValue(v8::Isolate* isolate,
v8::Local<v8::Value> value,
ExceptionState& exception_state) {
return ToUInt8(isolate, value, kClamp, exception_state);
}
};
template <>
struct CORE_EXPORT NativeValueTraits<IDLShortClamp>
: public NativeValueTraitsBase<IDLShort> {
static int16_t NativeValue(v8::Isolate* isolate,
v8::Local<v8::Value> value,
ExceptionState& exception_state) {
return ToInt16(isolate, value, kClamp, exception_state);
}
};
template <>
struct CORE_EXPORT NativeValueTraits<IDLUnsignedShortClamp>
: public NativeValueTraitsBase<IDLUnsignedShort> {
static uint16_t NativeValue(v8::Isolate* isolate,
v8::Local<v8::Value> value,
ExceptionState& exception_state) {
return ToUInt16(isolate, value, kClamp, exception_state);
}
};
template <>
struct CORE_EXPORT NativeValueTraits<IDLLongClamp>
: public NativeValueTraitsBase<IDLLong> {
static int32_t NativeValue(v8::Isolate* isolate,
v8::Local<v8::Value> value,
ExceptionState& exception_state) {
return ToInt32(isolate, value, kClamp, exception_state);
}
};
template <>
struct CORE_EXPORT NativeValueTraits<IDLUnsignedLongClamp>
: public NativeValueTraitsBase<IDLUnsignedLong> {
static uint32_t NativeValue(v8::Isolate* isolate,
v8::Local<v8::Value> value,
ExceptionState& exception_state) {
return ToUInt32(isolate, value, kClamp, exception_state);
}
};
template <>
struct CORE_EXPORT NativeValueTraits<IDLLongLongClamp>
: public NativeValueTraitsBase<IDLLongLong> {
static int64_t NativeValue(v8::Isolate* isolate,
v8::Local<v8::Value> value,
ExceptionState& exception_state) {
return ToInt64(isolate, value, kClamp, exception_state);
}
};
template <>
struct CORE_EXPORT NativeValueTraits<IDLUnsignedLongLongClamp>
: public NativeValueTraitsBase<IDLUnsignedLongLong> {
static uint64_t NativeValue(v8::Isolate* isolate,
v8::Local<v8::Value> value,
ExceptionState& exception_state) {
return ToUInt64(isolate, value, kClamp, exception_state);
}
};
// [EnforceRange] Integers
template <>
struct CORE_EXPORT NativeValueTraits<IDLByteEnforceRange>
: public NativeValueTraitsBase<IDLByte> {
static int8_t NativeValue(v8::Isolate* isolate,
v8::Local<v8::Value> value,
ExceptionState& exception_state) {
return ToInt8(isolate, value, kEnforceRange, exception_state);
}
};
template <>
struct CORE_EXPORT NativeValueTraits<IDLOctetEnforceRange>
: public NativeValueTraitsBase<IDLOctet> {
static uint8_t NativeValue(v8::Isolate* isolate,
v8::Local<v8::Value> value,
ExceptionState& exception_state) {
return ToUInt8(isolate, value, kEnforceRange, exception_state);
}
};
template <>
struct CORE_EXPORT NativeValueTraits<IDLShortEnforceRange>
: public NativeValueTraitsBase<IDLShort> {
static int16_t NativeValue(v8::Isolate* isolate,
v8::Local<v8::Value> value,
ExceptionState& exception_state) {
return ToInt16(isolate, value, kEnforceRange, exception_state);
}
};
template <>
struct CORE_EXPORT NativeValueTraits<IDLUnsignedShortEnforceRange>
: public NativeValueTraitsBase<IDLUnsignedShort> {
static uint16_t NativeValue(v8::Isolate* isolate,
v8::Local<v8::Value> value,
ExceptionState& exception_state) {
return ToUInt16(isolate, value, kEnforceRange, exception_state);
}
};
template <>
struct CORE_EXPORT NativeValueTraits<IDLLongEnforceRange>
: public NativeValueTraitsBase<IDLLong> {
static int32_t NativeValue(v8::Isolate* isolate,
v8::Local<v8::Value> value,
ExceptionState& exception_state) {
return ToInt32(isolate, value, kEnforceRange, exception_state);
}
};
template <>
struct CORE_EXPORT NativeValueTraits<IDLUnsignedLongEnforceRange>
: public NativeValueTraitsBase<IDLUnsignedLong> {
static uint32_t NativeValue(v8::Isolate* isolate,
v8::Local<v8::Value> value,
ExceptionState& exception_state) {
return ToUInt32(isolate, value, kEnforceRange, exception_state);
}
};
template <>
struct CORE_EXPORT NativeValueTraits<IDLLongLongEnforceRange>
: public NativeValueTraitsBase<IDLLongLong> {
static int64_t NativeValue(v8::Isolate* isolate,
v8::Local<v8::Value> value,
ExceptionState& exception_state) {
return ToInt64(isolate, value, kEnforceRange, exception_state);
}
};
template <>
struct CORE_EXPORT NativeValueTraits<IDLUnsignedLongLongEnforceRange>
: public NativeValueTraitsBase<IDLUnsignedLongLong> {
static uint64_t NativeValue(v8::Isolate* isolate,
v8::Local<v8::Value> value,
ExceptionState& exception_state) {
return ToUInt64(isolate, value, kEnforceRange, exception_state);
}
};
// Strings
template <V8StringResourceMode Mode>
struct NativeValueTraits<IDLByteStringBase<Mode>>
: public NativeValueTraitsBase<IDLByteStringBase<Mode>> {
// http://heycam.github.io/webidl/#es-ByteString
static String NativeValue(v8::Isolate* isolate,
v8::Local<v8::Value> value,
ExceptionState& exception_state) {
V8StringResource<Mode> string_resource(value);
// 1. Let x be ToString(v)
if (!string_resource.Prepare(isolate, exception_state))
return String();
String x = string_resource;
// 2. If the value of any element of x is greater than 255, then throw a
// TypeError.
if (!x.ContainsOnlyLatin1OrEmpty()) {
exception_state.ThrowTypeError("Value is not a valid ByteString.");
return String();
}
// 3. Return an IDL ByteString value whose length is the length of x, and
// where the value of each element is the value of the corresponding
// element of x.
// Blink: A ByteString is simply a String with a range constrained per
// the above, so this is the identity operation.
return x;
}
static String NullValue() { return String(); }
};
template <V8StringResourceMode Mode>
struct NativeValueTraits<IDLStringBase<Mode>>
: public NativeValueTraitsBase<IDLStringBase<Mode>> {
// https://heycam.github.io/webidl/#es-DOMString
static String NativeValue(v8::Isolate* isolate,
v8::Local<v8::Value> value,
ExceptionState& exception_state) {
V8StringResource<Mode> string(value);
if (!string.Prepare(isolate, exception_state))
return String();
return string;
}
static String NullValue() { return String(); }
};
template <V8StringResourceMode Mode>
struct NativeValueTraits<IDLUSVStringBase<Mode>>
: public NativeValueTraitsBase<IDLUSVStringBase<Mode>> {
// http://heycam.github.io/webidl/#es-USVString
static String NativeValue(v8::Isolate* isolate,
v8::Local<v8::Value> value,
ExceptionState& exception_state) {
// 1. Let string be the result of converting V to a DOMString.
V8StringResource<Mode> string(value);
if (!string.Prepare(isolate, exception_state))
return String();
// 2. Return an IDL USVString value that is the result of converting string
// to a sequence of Unicode scalar values.
return ReplaceUnmatchedSurrogates(string);
}
static String NullValue() { return String(); }
};
// Floats and doubles
template <>
struct CORE_EXPORT NativeValueTraits<IDLDouble>
: public NativeValueTraitsBase<IDLDouble> {
static double NativeValue(v8::Isolate* isolate,
v8::Local<v8::Value> value,
ExceptionState& exception_state) {
return ToRestrictedDouble(isolate, value, exception_state);
}
};
template <>
struct CORE_EXPORT NativeValueTraits<IDLUnrestrictedDouble>
: public NativeValueTraitsBase<IDLUnrestrictedDouble> {
static double NativeValue(v8::Isolate* isolate,
v8::Local<v8::Value> value,
ExceptionState& exception_state) {
return ToDouble(isolate, value, exception_state);
}
};
template <>
struct CORE_EXPORT NativeValueTraits<IDLFloat>
: public NativeValueTraitsBase<IDLFloat> {
static float NativeValue(v8::Isolate* isolate,
v8::Local<v8::Value> value,
ExceptionState& exception_state) {
return ToRestrictedFloat(isolate, value, exception_state);
}
};
template <>
struct CORE_EXPORT NativeValueTraits<IDLUnrestrictedFloat>
: public NativeValueTraitsBase<IDLUnrestrictedFloat> {
static float NativeValue(v8::Isolate* isolate,
v8::Local<v8::Value> value,
ExceptionState& exception_state) {
return ToFloat(isolate, value, exception_state);
}
};
// Promises
template <>
struct CORE_EXPORT NativeValueTraits<IDLPromise>
: public NativeValueTraitsBase<IDLPromise> {
static ScriptPromise NativeValue(v8::Isolate* isolate,
v8::Local<v8::Value> value,
ExceptionState& exception_state) {
return NativeValue(isolate, value);
}
static ScriptPromise NativeValue(v8::Isolate* isolate,
v8::Local<v8::Value> value) {
return ScriptPromise::Cast(ScriptState::Current(isolate), value);
}
};
// Type-specific overloads
template <>
struct CORE_EXPORT NativeValueTraits<IDLDate>
: public NativeValueTraitsBase<IDLDate> {
static double NativeValue(v8::Isolate* isolate,
v8::Local<v8::Value> value,
ExceptionState& exception_state) {
return ToCoreDate(isolate, value, exception_state);
}
};
// Sequences
template <typename T>
struct NativeValueTraits<IDLSequence<T>>
: public NativeValueTraitsBase<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) {
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;
}
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 = V8AtomicString(isolate, "next");
v8::Local<v8::String> value_key = V8AtomicString(isolate, "value");
v8::Local<v8::String> done_key = V8AtomicString(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;
}
if (done->BooleanValue(isolate))
break;
result.emplace_back(
NativeValueTraits<T>::NativeValue(isolate, element, exception_state));
if (exception_state.HadException())
return;
}
}
};
// Records
template <typename K, typename V>
struct NativeValueTraits<IDLRecord<K, V>>
: public NativeValueTraitsBase<IDLRecord<K, V>> {
// Nondependent types need to be explicitly qualified to be accessible.
using typename NativeValueTraitsBase<IDLRecord<K, V>>::ImplType;
// Converts a JavaScript value |O| to an IDL record<K, V> value. In C++, a
// record is represented as a Vector<std::pair<k, v>> (or a HeapVector if
// necessary). See https://heycam.github.io/webidl/#es-record.
static ImplType NativeValue(v8::Isolate* isolate,
v8::Local<v8::Value> v8_value,
ExceptionState& exception_state) {
v8::Local<v8::Context> context = isolate->GetCurrentContext();
// "1. If Type(O) is not Object, throw a TypeError."
if (!v8_value->IsObject()) {
exception_state.ThrowTypeError(
"Only objects can be converted to record<K,V> types");
return ImplType();
}
v8::Local<v8::Object> v8_object = v8::Local<v8::Object>::Cast(v8_value);
v8::TryCatch block(isolate);
// "3. Let keys be ? O.[[OwnPropertyKeys]]()."
v8::Local<v8::Array> keys;
// While we could pass v8::ONLY_ENUMERABLE below, doing so breaks
// web-platform-tests' headers-record.html and deviates from the spec
// algorithm.
if (!v8_object
->GetOwnPropertyNames(context,
static_cast<v8::PropertyFilter>(
v8::PropertyFilter::ALL_PROPERTIES))
.ToLocal(&keys)) {
exception_state.RethrowV8Exception(block.Exception());
return ImplType();
}
if (keys->Length() > ImplType::MaxCapacity()) {
exception_state.ThrowRangeError("Array length exceeds supported limit.");
return ImplType();
}
// "2. Let result be a new empty instance of record<K, V>."
ImplType result;
result.ReserveInitialCapacity(keys->Length());
// The conversion algorithm needs a data structure with fast insertion at
// the end while at the same time requiring fast checks for previous insert
// of a given key. |seenKeys| is a key/position in |result| map that aids in
// the latter part.
HashMap<String, uint32_t> seen_keys;
for (uint32_t i = 0; i < keys->Length(); ++i) {
// "4. Repeat, for each element key of keys in List order:"
v8::Local<v8::Value> key;
if (!keys->Get(context, i).ToLocal(&key)) {
exception_state.RethrowV8Exception(block.Exception());
return ImplType();
}
// V8's GetOwnPropertyNames() does not convert numeric property indices
// to strings, so we have to do it ourselves.
if (!key->IsName())
key = key->ToString(context).ToLocalChecked();
// "4.1. Let desc be ? O.[[GetOwnProperty]](key)."
v8::Local<v8::Value> desc;
if (!v8_object->GetOwnPropertyDescriptor(context, key.As<v8::Name>())
.ToLocal(&desc)) {
exception_state.RethrowV8Exception(block.Exception());
return ImplType();
}
// "4.2. If desc is not undefined and desc.[[Enumerable]] is true:"
// We can call ToLocalChecked() and ToChecked() here because
// GetOwnPropertyDescriptor is responsible for catching any exceptions
// and failures, and if we got to this point of the code we have a proper
// object that was not created by a user.
if (desc->IsUndefined())
continue;
DCHECK(desc->IsObject());
v8::Local<v8::Value> enumerable =
v8::Local<v8::Object>::Cast(desc)
->Get(context, V8String(isolate, "enumerable"))
.ToLocalChecked();
if (!enumerable->BooleanValue(isolate))
continue;
// "4.2.1. Let typedKey be key converted to an IDL value of type K."
String typed_key =
NativeValueTraits<K>::NativeValue(isolate, key, exception_state);
if (exception_state.HadException())
return ImplType();
// "4.2.2. Let value be ? Get(O, key)."
v8::Local<v8::Value> value;
if (!v8_object->Get(context, key).ToLocal(&value)) {
exception_state.RethrowV8Exception(block.Exception());
return ImplType();
}
// "4.2.3. Let typedValue be value converted to an IDL value of type V."
typename ImplType::ValueType::second_type typed_value =
NativeValueTraits<V>::NativeValue(isolate, value, exception_state);
if (exception_state.HadException())
return ImplType();
if (seen_keys.Contains(typed_key)) {
// "4.2.4. If typedKey is already a key in result, set its value to
// typedValue.
// Note: This can happen when O is a proxy object."
const uint32_t pos = seen_keys.at(typed_key);
result[pos] = std::make_pair(typed_key, typed_value);
} else {
// "4.2.5. Otherwise, append to result a mapping (typedKey,
// typedValue)."
// Note we can take this shortcut because we are always appending.
const uint32_t pos = result.size();
seen_keys.Set(typed_key, pos);
result.UncheckedAppend(std::make_pair(typed_key, typed_value));
}
}
// "5. Return result."
return result;
}
};
// Callback functions
template <typename T>
struct NativeValueTraits<
T,
typename std::enable_if<
std::is_base_of<CallbackFunctionBase, T>::value>::type>
: public NativeValueTraitsBase<T> {
static T* NativeValue(v8::Isolate* isolate,
v8::Local<v8::Value> value,
ExceptionState& exception_state) {
// Not implemented because of no use case so far.
CHECK(false)
// Emit a message so that NativeValueTraitsImplTest.IDLCallbackFunction
// test can confirm that it's hitting this specific failure. i.e.
// the template resolution is working as expected.
<< "NativeValueTraits<CallbackFunctionBase>::NativeValue "
<< "is not yet implemented.";
return nullptr;
}
};
// Nullable
template <typename InnerType>
struct NativeValueTraits<IDLNullable<InnerType>>
: public NativeValueTraitsBase<IDLNullable<InnerType>> {
// https://heycam.github.io/webidl/#es-nullable-type
static typename IDLNullable<InnerType>::ResultType NativeValue(
v8::Isolate* isolate,
v8::Local<v8::Value> v8_value,
ExceptionState& exception_state) {
if (v8_value->IsNullOrUndefined())
return IDLNullable<InnerType>::NullValue();
return NativeValueTraits<InnerType>::NativeValue(isolate, v8_value,
exception_state);
}
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_NATIVE_VALUE_TRAITS_IMPL_H_