blob: 2233edae8587a2980efd32f3f8564e516a98e8b2 [file] [log] [blame]
// Copyright 2021 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_TO_V8_TRAITS_H_
#define THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_TO_V8_TRAITS_H_
#include "third_party/blink/renderer/bindings/core/v8/idl_dictionary_base.h"
#include "third_party/blink/renderer/bindings/core/v8/idl_types.h"
#include "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h"
#include "third_party/blink/renderer/platform/bindings/callback_function_base.h"
#include "third_party/blink/renderer/platform/bindings/callback_interface_base.h"
#include "third_party/blink/renderer/platform/bindings/dictionary_base.h"
#include "third_party/blink/renderer/platform/bindings/dom_data_store.h"
#include "third_party/blink/renderer/platform/bindings/enumeration_base.h"
#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
#include "third_party/blink/renderer/platform/bindings/v8_binding.h"
#include "v8/include/v8.h"
namespace blink {
// ToV8Traits provides C++ -> V8 conversion.
// Currently, you can use ToV8() which is defined in to_v8.h for this
// conversion, but it cannot throw an exception when an error occurs.
// We will solve this problem and replace ToV8() in to_v8.h with
// ToV8Traits::ToV8().
// TODO(canonmukai): Replace existing ToV8() with ToV8Traits<>.
// Primary template for ToV8Traits.
template <typename T, typename SFINAEHelper = void>
struct ToV8Traits;
// Any
template <>
struct ToV8Traits<IDLAny> {
static v8::MaybeLocal<v8::Value> ToV8(ScriptState* script_state,
const ScriptValue& script_value)
WARN_UNUSED_RESULT {
DCHECK(!script_value.IsEmpty());
return script_value.V8Value();
}
};
// Boolean
template <>
struct ToV8Traits<IDLBoolean> {
static v8::MaybeLocal<v8::Value> ToV8(ScriptState* script_state,
bool value) WARN_UNUSED_RESULT {
return v8::Boolean::New(script_state->GetIsolate(), value);
}
};
// Integer
// int8_t
template <bindings::IDLIntegerConvMode mode>
struct ToV8Traits<IDLIntegerTypeBase<int8_t, mode>> {
static v8::MaybeLocal<v8::Value> ToV8(ScriptState* script_state,
int8_t value) WARN_UNUSED_RESULT {
return v8::Integer::New(script_state->GetIsolate(), value);
}
};
// int16_t
template <bindings::IDLIntegerConvMode mode>
struct ToV8Traits<IDLIntegerTypeBase<int16_t, mode>> {
static v8::MaybeLocal<v8::Value> ToV8(ScriptState* script_state,
int16_t value) WARN_UNUSED_RESULT {
return v8::Integer::New(script_state->GetIsolate(), value);
}
};
// int32_t
template <bindings::IDLIntegerConvMode mode>
struct ToV8Traits<IDLIntegerTypeBase<int32_t, mode>> {
static v8::MaybeLocal<v8::Value> ToV8(ScriptState* script_state,
int32_t value) WARN_UNUSED_RESULT {
return v8::Integer::New(script_state->GetIsolate(), value);
}
};
// int64_t
template <bindings::IDLIntegerConvMode mode>
struct ToV8Traits<IDLIntegerTypeBase<int64_t, mode>> {
static v8::MaybeLocal<v8::Value> ToV8(ScriptState* script_state,
int64_t value) WARN_UNUSED_RESULT {
int32_t value_in_32bit = static_cast<int32_t>(value);
if (value_in_32bit == value)
return v8::Integer::New(script_state->GetIsolate(), value_in_32bit);
// v8::Integer cannot represent 64-bit integers.
return v8::Number::New(script_state->GetIsolate(), value);
}
};
// uint8_t
template <bindings::IDLIntegerConvMode mode>
struct ToV8Traits<IDLIntegerTypeBase<uint8_t, mode>> {
static v8::MaybeLocal<v8::Value> ToV8(ScriptState* script_state,
uint8_t value) WARN_UNUSED_RESULT {
return v8::Integer::NewFromUnsigned(script_state->GetIsolate(), value);
}
};
// uint16_t
template <bindings::IDLIntegerConvMode mode>
struct ToV8Traits<IDLIntegerTypeBase<uint16_t, mode>> {
static v8::MaybeLocal<v8::Value> ToV8(ScriptState* script_state,
uint16_t value) WARN_UNUSED_RESULT {
return v8::Integer::NewFromUnsigned(script_state->GetIsolate(), value);
}
};
// uint32_t
template <bindings::IDLIntegerConvMode mode>
struct ToV8Traits<IDLIntegerTypeBase<uint32_t, mode>> {
static v8::MaybeLocal<v8::Value> ToV8(ScriptState* script_state,
uint32_t value) WARN_UNUSED_RESULT {
return v8::Integer::NewFromUnsigned(script_state->GetIsolate(), value);
}
};
// uint64_t
template <bindings::IDLIntegerConvMode mode>
struct ToV8Traits<IDLIntegerTypeBase<uint64_t, mode>> {
static v8::MaybeLocal<v8::Value> ToV8(ScriptState* script_state,
uint64_t value) WARN_UNUSED_RESULT {
uint32_t value_in_32bit = static_cast<uint32_t>(value);
if (value_in_32bit == value) {
return v8::Integer::NewFromUnsigned(script_state->GetIsolate(),
value_in_32bit);
}
// v8::Integer cannot represent 64-bit integers.
return v8::Number::New(script_state->GetIsolate(), value);
}
};
// Floating Point Number
template <typename T, bindings::IDLFloatingPointNumberConvMode mode>
struct ToV8Traits<IDLFloatingPointNumberTypeBase<T, mode>> {
static v8::MaybeLocal<v8::Value> ToV8(ScriptState* script_state,
T value) WARN_UNUSED_RESULT {
return v8::Number::New(script_state->GetIsolate(), value);
}
};
// String
template <typename T>
struct ToV8Traits<
T,
typename std::enable_if_t<std::is_base_of<IDLStringTypeBase, T>::value>> {
static v8::MaybeLocal<v8::Value> ToV8(ScriptState* script_state,
const String& value)
WARN_UNUSED_RESULT {
// if |value| is a null string, V8String() returns an empty string.
return V8String(script_state->GetIsolate(), value);
}
};
// Object
template <>
struct ToV8Traits<IDLObject> {
static v8::MaybeLocal<v8::Value> ToV8(ScriptState* script_state,
const ScriptValue& script_value)
WARN_UNUSED_RESULT {
DCHECK(!script_value.IsEmpty());
v8::Local<v8::Value> v8_value = script_value.V8Value();
DCHECK(v8_value->IsObject());
return v8_value;
}
};
// Promise
template <>
struct ToV8Traits<IDLPromise> {
static v8::MaybeLocal<v8::Value> ToV8(ScriptState* script_state,
const ScriptPromise& script_promise)
WARN_UNUSED_RESULT {
DCHECK(!script_promise.IsEmpty());
return script_promise.V8Value();
}
};
// ScriptWrappable
namespace bindings {
// Helper function for ScriptWrappable
inline v8::MaybeLocal<v8::Value> ToV8HelperScriptWrappable(
ScriptState* script_state,
ScriptWrappable* script_wrappable) {
CHECK(script_wrappable);
v8::Local<v8::Value> wrapper =
DOMDataStore::GetWrapper(script_wrappable, script_state->GetIsolate());
if (!wrapper.IsEmpty()) {
return wrapper;
}
if (!script_wrappable->WrapV2(script_state).ToLocal(&wrapper)) {
return v8::MaybeLocal<v8::Value>();
}
return wrapper;
}
// For optimization
inline v8::MaybeLocal<v8::Value> ToV8HelperScriptWrappable(
v8::Isolate* isolate,
ScriptWrappable* script_wrappable,
v8::Local<v8::Object> creation_context_object) {
CHECK(script_wrappable);
v8::Local<v8::Value> wrapper =
DOMDataStore::GetWrapper(script_wrappable, isolate);
if (!wrapper.IsEmpty()) {
return wrapper;
}
CHECK(!creation_context_object.IsEmpty());
ScriptState* script_state =
ScriptState::From(creation_context_object->CreationContext());
if (!script_wrappable->WrapV2(script_state).ToLocal(&wrapper)) {
return v8::MaybeLocal<v8::Value>();
}
return wrapper;
}
} // namespace bindings
template <typename T>
struct ToV8Traits<
T,
typename std::enable_if_t<std::is_base_of<ScriptWrappable, T>::value>> {
static v8::MaybeLocal<v8::Value> ToV8(ScriptState* script_state,
T* script_wrappable)
WARN_UNUSED_RESULT {
return bindings::ToV8HelperScriptWrappable(script_state, script_wrappable);
}
// This overload is used for the case when a ToV8 caller does not have
// |script_state| but has a receiver object (a creation context object)
// which is needed to create a wrapper. If a wrapper object corresponding to
// the receiver object exists, ToV8 can return it without a call to
// CreationContext() which is slow.
static v8::MaybeLocal<v8::Value> ToV8(
v8::Isolate* isolate,
T* script_wrappable,
v8::Local<v8::Object> creation_context_object) WARN_UNUSED_RESULT {
return bindings::ToV8HelperScriptWrappable(isolate, script_wrappable,
creation_context_object);
}
};
// Dictionary
template <typename T>
struct ToV8Traits<T,
typename std::enable_if_t<
std::is_base_of<bindings::DictionaryBase, T>::value>> {
static v8::MaybeLocal<v8::Value> ToV8(ScriptState* script_state,
const T* dictionary)
WARN_UNUSED_RESULT {
DCHECK(dictionary);
v8::Local<v8::Value> v8_value = dictionary->CreateV8Object(
script_state->GetIsolate(), script_state->GetContext()->Global());
DCHECK(!v8_value.IsEmpty());
return v8_value;
}
};
// Old implementation of Dictionary
template <typename T>
struct ToV8Traits<
T,
typename std::enable_if_t<std::is_base_of<IDLDictionaryBase, T>::value>> {
static v8::MaybeLocal<v8::Value> ToV8(ScriptState* script_state,
const T* dictionary)
WARN_UNUSED_RESULT {
DCHECK(dictionary);
return dictionary->ToV8Impl(script_state->GetContext()->Global(),
script_state->GetIsolate());
}
};
// Callback function
template <typename T>
struct ToV8Traits<T,
typename std::enable_if_t<
std::is_base_of<CallbackFunctionBase, T>::value>> {
static v8::MaybeLocal<v8::Value> ToV8(ScriptState* script_state,
T* callback) WARN_UNUSED_RESULT {
// creation_context (|script_state->GetContext()|) is intentionally ignored.
// Callback functions are not wrappers nor clonable. ToV8 on a callback
// function must be used only when it's in the same world.
DCHECK(callback);
DCHECK(&callback->GetWorld() == &script_state->World());
return callback->CallbackObject().template As<v8::Value>();
}
};
// Callback interface
template <typename T>
struct ToV8Traits<T,
typename std::enable_if_t<
std::is_base_of<CallbackInterfaceBase, T>::value>> {
static v8::MaybeLocal<v8::Value> ToV8(ScriptState* script_state,
T* callback) WARN_UNUSED_RESULT {
// creation_context (|script_state->GetContext()|) is intentionally ignored.
// Callback Interfaces are not wrappers nor clonable. ToV8 on a callback
// interface must be used only when it's in the same world.
DCHECK(callback);
DCHECK(&callback->GetWorld() == &script_state->World());
return callback->CallbackObject().template As<v8::Value>();
}
};
// Enumeration
template <typename T>
struct ToV8Traits<T,
typename std::enable_if_t<
std::is_base_of<bindings::EnumerationBase, T>::value>> {
static v8::MaybeLocal<v8::Value> ToV8(ScriptState* script_state,
const T& enumeration)
WARN_UNUSED_RESULT {
return V8String(script_state->GetIsolate(), enumeration.AsCStr());
}
};
// NotShared
template <typename T>
struct ToV8Traits<NotShared<T>> {
static v8::MaybeLocal<v8::Value> ToV8(ScriptState* script_state,
NotShared<T> value) WARN_UNUSED_RESULT {
return ToV8Traits<T>::ToV8(script_state, value.Get());
}
};
// MaybeShared
template <typename T>
struct ToV8Traits<MaybeShared<T>> {
static v8::MaybeLocal<v8::Value> ToV8(ScriptState* script_state,
MaybeShared<T> value)
WARN_UNUSED_RESULT {
return ToV8Traits<T>::ToV8(script_state, value.Get());
}
};
// Array
namespace bindings {
// Helper function for IDLSequence
template <typename ElementIDLType, typename VectorType>
inline v8::MaybeLocal<v8::Value> ToV8HelperSequence(ScriptState* script_state,
const VectorType& vector) {
v8::Isolate* isolate = script_state->GetIsolate();
v8::Local<v8::Array> array;
{
v8::Context::Scope context_scope(script_state->GetContext());
array = v8::Array::New(isolate, SafeCast<int>(vector.size()));
}
v8::Local<v8::Context> context = script_state->GetContext();
uint32_t index = 0;
typename VectorType::const_iterator end = vector.end();
for (typename VectorType::const_iterator iter = vector.begin(); iter != end;
++iter) {
v8::Local<v8::Value> v8_value;
if (!ToV8Traits<ElementIDLType>::ToV8(script_state, *iter)
.ToLocal(&v8_value)) {
return v8::MaybeLocal<v8::Value>();
}
bool is_property_created;
if (!array->CreateDataProperty(context, index++, v8_value)
.To(&is_property_created) ||
!is_property_created) {
return v8::Local<v8::Value>();
}
}
return array;
}
// Helper function for IDLRecord
template <typename ValueIDLType, typename VectorType>
inline v8::MaybeLocal<v8::Value> ToV8HelperRecord(ScriptState* script_state,
const VectorType& vector) {
v8::Isolate* isolate = script_state->GetIsolate();
v8::Local<v8::Object> object;
{
v8::Context::Scope context_scope(script_state->GetContext());
object = v8::Object::New(isolate);
}
v8::Local<v8::Context> context = script_state->GetContext();
typename VectorType::const_iterator end = vector.end();
for (typename VectorType::const_iterator iter = vector.begin(); iter != end;
++iter) {
v8::Local<v8::Value> v8_value;
if (!ToV8Traits<ValueIDLType>::ToV8(script_state, iter->second)
.ToLocal(&v8_value)) {
return v8::MaybeLocal<v8::Value>();
}
bool is_property_created;
if (!object
->CreateDataProperty(context, V8AtomicString(isolate, iter->first),
v8_value)
.To(&is_property_created) ||
!is_property_created) {
return v8::Local<v8::Value>();
}
}
return object;
}
} // namespace bindings
// IDLSequence
template <typename T>
struct ToV8Traits<IDLSequence<T>> {
static v8::MaybeLocal<v8::Value> ToV8(
ScriptState* script_state,
const typename IDLSequence<T>::ImplType& value) {
return bindings::ToV8HelperSequence<T>(script_state, value);
}
static v8::MaybeLocal<v8::Value> ToV8(
ScriptState* script_state,
const typename IDLSequence<T>::ImplType* value) {
return bindings::ToV8HelperSequence<T>(script_state, *value);
}
};
// IDLRecord
// K must be based of IDL String types.
template <typename K, typename V>
struct ToV8Traits<IDLRecord<K, V>> {
static v8::MaybeLocal<v8::Value> ToV8(
ScriptState* script_state,
const typename IDLRecord<K, V>::ImplType& value) {
return bindings::ToV8HelperRecord<V>(script_state, value);
}
static v8::MaybeLocal<v8::Value> ToV8(
ScriptState* script_state,
const typename IDLRecord<K, V>::ImplType* value) {
return bindings::ToV8HelperRecord<V>(script_state, *value);
}
};
// Nullable
// IDLNullable<IDLNullable<T>> must not be used.
template <typename T>
struct ToV8Traits<IDLNullable<IDLNullable<T>>>;
// Nullable Boolean
template <>
struct ToV8Traits<IDLNullable<IDLBoolean>> {
static v8::MaybeLocal<v8::Value> ToV8(ScriptState* script_state,
const base::Optional<bool>& value)
WARN_UNUSED_RESULT {
if (!value)
return v8::Null(script_state->GetIsolate());
return ToV8Traits<IDLBoolean>::ToV8(script_state, *value);
}
};
// Nullable Integers
template <typename T, bindings::IDLIntegerConvMode mode>
struct ToV8Traits<IDLNullable<IDLIntegerTypeBase<T, mode>>> {
static v8::MaybeLocal<v8::Value> ToV8(ScriptState* script_state,
const base::Optional<T>& value)
WARN_UNUSED_RESULT {
if (!value)
return v8::Null(script_state->GetIsolate());
return ToV8Traits<IDLIntegerTypeBase<T, mode>>::ToV8(script_state, *value);
}
};
// Nullable Floating Point Number
template <typename T, bindings::IDLFloatingPointNumberConvMode mode>
struct ToV8Traits<IDLNullable<IDLFloatingPointNumberTypeBase<T, mode>>> {
static v8::MaybeLocal<v8::Value> ToV8(ScriptState* script_state,
const base::Optional<T>& value)
WARN_UNUSED_RESULT {
if (!value)
return v8::Null(script_state->GetIsolate());
return ToV8Traits<IDLFloatingPointNumberTypeBase<T, mode>>::ToV8(
script_state, *value);
}
};
// Nullable Strings
template <typename T>
struct ToV8Traits<
IDLNullable<T>,
typename std::enable_if_t<std::is_base_of<IDLStringTypeBase, T>::value>> {
static v8::MaybeLocal<v8::Value> ToV8(ScriptState* script_state,
const String& value)
WARN_UNUSED_RESULT {
if (!value)
return v8::Null(script_state->GetIsolate());
return ToV8Traits<T>::ToV8(script_state, value);
}
};
// Nullable ScriptWrappable
template <typename T>
struct ToV8Traits<
IDLNullable<T>,
typename std::enable_if_t<std::is_base_of<ScriptWrappable, T>::value>> {
static v8::MaybeLocal<v8::Value> ToV8(ScriptState* script_state,
T* script_wrappable)
WARN_UNUSED_RESULT {
if (!script_wrappable)
return v8::Null(script_state->GetIsolate());
return ToV8Traits<T>::ToV8(script_state, script_wrappable);
}
static v8::MaybeLocal<v8::Value> ToV8(v8::Isolate* isolate,
ScriptWrappable* script_wrappable,
v8::Local<v8::Object> creation_context)
WARN_UNUSED_RESULT {
if (!script_wrappable)
return v8::Null(isolate);
return ToV8Traits<T>::ToV8(isolate, script_wrappable, creation_context);
}
};
// Nullable Dictionary
template <typename T>
struct ToV8Traits<IDLNullable<T>,
typename std::enable_if_t<
std::is_base_of<bindings::DictionaryBase, T>::value>> {
static v8::MaybeLocal<v8::Value> ToV8(ScriptState* script_state,
const T* dictionary)
WARN_UNUSED_RESULT {
if (!dictionary)
return v8::Null(script_state->GetIsolate());
return ToV8Traits<T>::ToV8(script_state, dictionary);
}
};
// Nullable Dictionary (Old implementation)
template <typename T>
struct ToV8Traits<
IDLNullable<T>,
typename std::enable_if_t<std::is_base_of<IDLDictionaryBase, T>::value>> {
static v8::MaybeLocal<v8::Value> ToV8(ScriptState* script_state,
const T* dictionary)
WARN_UNUSED_RESULT {
if (!dictionary)
return v8::Null(script_state->GetIsolate());
return ToV8Traits<T>::ToV8(script_state, dictionary);
}
};
// Nullable Callback function
template <typename T>
struct ToV8Traits<IDLNullable<T>,
typename std::enable_if_t<
std::is_base_of<CallbackFunctionBase, T>::value>> {
static v8::MaybeLocal<v8::Value> ToV8(ScriptState* script_state,
T* callback) WARN_UNUSED_RESULT {
if (!callback)
return v8::Null(script_state->GetIsolate());
return ToV8Traits<T>::ToV8(script_state, callback);
}
};
// Nullable Callback interface
template <typename T>
struct ToV8Traits<IDLNullable<T>,
typename std::enable_if_t<
std::is_base_of<CallbackInterfaceBase, T>::value>> {
static v8::MaybeLocal<v8::Value> ToV8(ScriptState* script_state,
T* callback) WARN_UNUSED_RESULT {
if (!callback)
return v8::Null(script_state->GetIsolate());
return ToV8Traits<T>::ToV8(script_state, callback);
}
};
// Nullable Enumeration
template <typename T>
struct ToV8Traits<IDLNullable<T>,
typename std::enable_if_t<
std::is_base_of<bindings::EnumerationBase, T>::value>> {
static v8::MaybeLocal<v8::Value> ToV8(ScriptState* script_state,
const base::Optional<T>& enumeration)
WARN_UNUSED_RESULT {
if (!enumeration)
return v8::Null(script_state->GetIsolate());
return ToV8Traits<T>::ToV8(script_state, *enumeration);
}
};
// Nullable Date
// IDLDate must be used as IDLNullable<IDLDate>.
template <>
struct ToV8Traits<IDLNullable<IDLDate>> {
static v8::MaybeLocal<v8::Value> ToV8(ScriptState* script_state,
const base::Optional<base::Time> date)
WARN_UNUSED_RESULT {
if (!date)
return v8::Null(script_state->GetIsolate());
return v8::Date::New(script_state->GetContext(),
date->ToJsTimeIgnoringNull())
.ToLocalChecked();
}
};
// Union types
namespace bindings {
// Helper function for Union
template <typename T>
inline v8::MaybeLocal<v8::Value> ToV8HelperUnion(ScriptState* script_state,
const T& value) {
return ToV8(value, script_state->GetContext()->Global(),
script_state->GetIsolate());
}
} // namespace bindings
template <typename T>
struct ToV8Traits<IDLUnionNotINT<T>> {
static v8::MaybeLocal<v8::Value> ToV8(ScriptState* script_state,
const T& value) WARN_UNUSED_RESULT {
return bindings::ToV8HelperUnion(script_state, value);
}
};
// Optional
template <typename T>
struct ToV8Traits<IDLOptional<T>> {
static v8::MaybeLocal<v8::Value> ToV8(ScriptState* script_state,
const T* value) WARN_UNUSED_RESULT {
if (!value)
return v8::Undefined(script_state->GetIsolate());
return ToV8Traits<T>::ToV8(script_state, value);
}
static v8::MaybeLocal<v8::Value> ToV8(ScriptState* script_state,
const ScriptValue& value)
WARN_UNUSED_RESULT {
if (value.IsEmpty())
return v8::Undefined(script_state->GetIsolate());
return ToV8Traits<T>::ToV8(script_state, value);
}
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_TO_V8_TRAITS_H_