blob: 19668cac36fcc43ad1663c42113addcbb0ac86da [file] [log] [blame]
// Copyright 2014 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_PLATFORM_BINDINGS_TO_V8_H_
#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_TO_V8_H_
// ToV8() provides C++ -> V8 conversion. Note that ToV8() can return an empty
// handle. Call sites must check IsEmpty() before using return value.
#include <utility>
#include "base/containers/span.h"
#include "base/optional.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/dom_data_store.h"
#include "third_party/blink/renderer/platform/bindings/script_state.h"
#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
#include "third_party/blink/renderer/platform/bindings/v8_binding.h"
#include "third_party/blink/renderer/platform/wtf/forward.h"
#include "v8/include/v8.h"
namespace blink {
// ScriptWrappable
inline v8::Local<v8::Value> ToV8(ScriptWrappable* impl,
v8::Local<v8::Object> creation_context,
v8::Isolate* isolate) {
if (UNLIKELY(!impl))
return v8::Null(isolate);
v8::Local<v8::Value> wrapper = DOMDataStore::GetWrapper(impl, isolate);
if (!wrapper.IsEmpty())
return wrapper;
wrapper = impl->Wrap(isolate, creation_context);
DCHECK(!wrapper.IsEmpty());
return wrapper;
}
// Callback function
inline v8::Local<v8::Value> ToV8(CallbackFunctionBase* callback,
v8::Local<v8::Object> creation_context,
v8::Isolate* isolate) {
// |creation_context| is intentionally ignored. Callback functions are not
// wrappers nor clonable. ToV8 on a callback function must be used only when
// it's the same origin-domain in the same world.
DCHECK(!callback || (callback->CallbackRelevantScriptState()->GetContext() ==
creation_context->CreationContext()));
return callback ? callback->CallbackFunction().As<v8::Value>()
: v8::Null(isolate).As<v8::Value>();
}
// Callback interface
inline v8::Local<v8::Value> ToV8(CallbackInterfaceBase* callback,
v8::Local<v8::Object> creation_context,
v8::Isolate* isolate) {
// |creation_context| is intentionally ignored. Callback interface objects
// are not wrappers nor clonable. ToV8 on a callback interface object must
// be used only when it's the same origin-domain in the same world.
DCHECK(!callback || (callback->CallbackRelevantScriptState()->GetContext() ==
creation_context->CreationContext()));
return callback ? callback->CallbackObject().As<v8::Value>()
: v8::Null(isolate).As<v8::Value>();
}
// Primitives
inline v8::Local<v8::Value> ToV8(const String& value,
v8::Local<v8::Object> creation_context,
v8::Isolate* isolate) {
return V8String(isolate, value);
}
inline v8::Local<v8::Value> ToV8(const char* value,
v8::Local<v8::Object> creation_context,
v8::Isolate* isolate) {
return V8String(isolate, value);
}
template <size_t sizeOfValue>
inline v8::Local<v8::Value> ToV8SignedIntegerInternal(int64_t value,
v8::Isolate*);
template <>
inline v8::Local<v8::Value> ToV8SignedIntegerInternal<4>(int64_t value,
v8::Isolate* isolate) {
return v8::Integer::New(isolate, static_cast<int32_t>(value));
}
template <>
inline v8::Local<v8::Value> ToV8SignedIntegerInternal<8>(int64_t value,
v8::Isolate* isolate) {
int32_t value_in32_bit = static_cast<int32_t>(value);
if (value_in32_bit == value)
return v8::Integer::New(isolate, value_in32_bit);
// V8 doesn't have a 64-bit integer implementation.
return v8::Number::New(isolate, value);
}
template <size_t sizeOfValue>
inline v8::Local<v8::Value> ToV8UnsignedIntegerInternal(uint64_t value,
v8::Isolate*);
template <>
inline v8::Local<v8::Value> ToV8UnsignedIntegerInternal<4>(
uint64_t value,
v8::Isolate* isolate) {
return v8::Integer::NewFromUnsigned(isolate, static_cast<uint32_t>(value));
}
template <>
inline v8::Local<v8::Value> ToV8UnsignedIntegerInternal<8>(
uint64_t value,
v8::Isolate* isolate) {
uint32_t value_in32_bit = static_cast<uint32_t>(value);
if (value_in32_bit == value)
return v8::Integer::NewFromUnsigned(isolate, value_in32_bit);
// V8 doesn't have a 64-bit integer implementation.
return v8::Number::New(isolate, value);
}
inline v8::Local<v8::Value> ToV8(int value,
v8::Local<v8::Object> creation_context,
v8::Isolate* isolate) {
return ToV8SignedIntegerInternal<sizeof value>(value, isolate);
}
inline v8::Local<v8::Value> ToV8(long value,
v8::Local<v8::Object> creation_context,
v8::Isolate* isolate) {
return ToV8SignedIntegerInternal<sizeof value>(value, isolate);
}
inline v8::Local<v8::Value> ToV8(long long value,
v8::Local<v8::Object> creation_context,
v8::Isolate* isolate) {
return ToV8SignedIntegerInternal<sizeof value>(value, isolate);
}
inline v8::Local<v8::Value> ToV8(unsigned value,
v8::Local<v8::Object> creation_context,
v8::Isolate* isolate) {
return ToV8UnsignedIntegerInternal<sizeof value>(value, isolate);
}
inline v8::Local<v8::Value> ToV8(unsigned long value,
v8::Local<v8::Object> creation_context,
v8::Isolate* isolate) {
return ToV8UnsignedIntegerInternal<sizeof value>(value, isolate);
}
inline v8::Local<v8::Value> ToV8(unsigned long long value,
v8::Local<v8::Object> creation_context,
v8::Isolate* isolate) {
return ToV8UnsignedIntegerInternal<sizeof value>(value, isolate);
}
inline v8::Local<v8::Value> ToV8(double value,
v8::Local<v8::Object> creation_context,
v8::Isolate* isolate) {
return v8::Number::New(isolate, value);
}
inline v8::Local<v8::Value> ToV8(bool value,
v8::Local<v8::Object> creation_context,
v8::Isolate* isolate) {
return v8::Boolean::New(isolate, value);
}
// Identity operator
inline v8::Local<v8::Value> ToV8(v8::Local<v8::Value> value,
v8::Local<v8::Object> creation_context,
v8::Isolate*) {
return value;
}
// Undefined
struct ToV8UndefinedGenerator {
DISALLOW_NEW();
}; // Used only for having toV8 return v8::Undefined.
inline v8::Local<v8::Value> ToV8(const ToV8UndefinedGenerator& value,
v8::Local<v8::Object> creation_context,
v8::Isolate* isolate) {
return v8::Undefined(isolate);
}
// Forward declaration to allow interleaving with sequences.
template <typename InnerType>
inline v8::Local<v8::Value> ToV8(const base::Optional<InnerType>& value,
v8::Local<v8::Object> creation_context,
v8::Isolate*);
// Array
// Declare the function here but define it later so it can call the ToV8()
// overloads below.
template <typename Sequence>
inline v8::Local<v8::Value> ToV8SequenceInternal(
const Sequence&,
v8::Local<v8::Object> creation_context,
v8::Isolate*);
template <typename T, size_t Extent>
inline v8::Local<v8::Value> ToV8(base::span<T, Extent> value,
v8::Local<v8::Object> creation_context,
v8::Isolate* isolate) {
return ToV8SequenceInternal(value, creation_context, isolate);
}
template <typename T, wtf_size_t inlineCapacity>
inline v8::Local<v8::Value> ToV8(const Vector<T, inlineCapacity>& value,
v8::Local<v8::Object> creation_context,
v8::Isolate* isolate) {
return ToV8SequenceInternal(value, creation_context, isolate);
}
template <typename T, wtf_size_t inlineCapacity>
inline v8::Local<v8::Value> ToV8(const HeapVector<T, inlineCapacity>& value,
v8::Local<v8::Object> creation_context,
v8::Isolate* isolate) {
return ToV8SequenceInternal(value, creation_context, isolate);
}
// The following two overloads are also used to convert record<K,V> IDL types
// back into ECMAScript Objects.
template <typename T>
inline v8::Local<v8::Value> ToV8(const Vector<std::pair<String, T>>& value,
v8::Local<v8::Object> creation_context,
v8::Isolate* isolate) {
v8::Local<v8::Object> object;
{
v8::Context::Scope context_scope(creation_context->CreationContext());
object = v8::Object::New(isolate);
}
v8::Local<v8::Context> context = isolate->GetCurrentContext();
for (unsigned i = 0; i < value.size(); ++i) {
v8::Local<v8::Value> v8_value = ToV8(value[i].second, object, isolate);
if (v8_value.IsEmpty())
v8_value = v8::Undefined(isolate);
bool created_property;
if (!object
->CreateDataProperty(
context, V8AtomicString(isolate, value[i].first), v8_value)
.To(&created_property) ||
!created_property) {
return v8::Local<v8::Value>();
}
}
return object;
}
template <typename T>
inline v8::Local<v8::Value> ToV8(const HeapVector<std::pair<String, T>>& value,
v8::Local<v8::Object> creation_context,
v8::Isolate* isolate) {
v8::Local<v8::Object> object;
{
v8::Context::Scope context_scope(creation_context->CreationContext());
object = v8::Object::New(isolate);
}
v8::Local<v8::Context> context = isolate->GetCurrentContext();
for (unsigned i = 0; i < value.size(); ++i) {
v8::Local<v8::Value> v8_value = ToV8(value[i].second, object, isolate);
if (v8_value.IsEmpty())
v8_value = v8::Undefined(isolate);
bool created_property;
if (!object
->CreateDataProperty(
context, V8AtomicString(isolate, value[i].first), v8_value)
.To(&created_property) ||
!created_property) {
return v8::Local<v8::Value>();
}
}
return object;
}
template <typename Sequence>
inline v8::Local<v8::Value> ToV8SequenceInternal(
const Sequence& sequence,
v8::Local<v8::Object> creation_context,
v8::Isolate* isolate) {
RUNTIME_CALL_TIMER_SCOPE(isolate,
RuntimeCallStats::CounterId::kToV8SequenceInternal);
v8::Local<v8::Array> array;
{
v8::Context::Scope context_scope(creation_context->CreationContext());
array = v8::Array::New(isolate, sequence.size());
}
v8::Local<v8::Context> context = isolate->GetCurrentContext();
uint32_t index = 0;
typename Sequence::const_iterator end = sequence.end();
for (typename Sequence::const_iterator iter = sequence.begin(); iter != end;
++iter) {
v8::Local<v8::Value> value = ToV8(*iter, array, isolate);
if (value.IsEmpty())
value = v8::Undefined(isolate);
bool created_property;
if (!array->CreateDataProperty(context, index++, value)
.To(&created_property) ||
!created_property) {
return v8::Local<v8::Value>();
}
}
return array;
}
// Nullable
template <typename InnerType>
inline v8::Local<v8::Value> ToV8(const base::Optional<InnerType>& value,
v8::Local<v8::Object> creation_context,
v8::Isolate* isolate) {
if (!value)
return v8::Null(isolate);
return ToV8(*value, creation_context, isolate);
}
// In all cases allow script state instead of creation context + isolate.
// Use this function only if the call site does not otherwise need the global,
// since v8::Context::Global is heavy.
template <typename T>
inline v8::Local<v8::Value> ToV8(T&& value, ScriptState* script_state) {
return ToV8(std::forward<T>(value), script_state->GetContext()->Global(),
script_state->GetIsolate());
}
// Only declare ToV8(void*,...) for checking function overload mismatch.
// This ToV8(void*,...) should be never used. So we will find mismatch
// because of "unresolved external symbol".
// Without ToV8(void*, ...), call to toV8 with T* will match with
// ToV8(bool, ...) if T is not a subclass of ScriptWrappable or if T is
// declared but not defined (so it's not clear that T is a subclass of
// ScriptWrappable).
// This hack helps detect such unwanted implicit conversions from T* to bool.
v8::Local<v8::Value> ToV8(void* value,
v8::Local<v8::Object> creation_context,
v8::Isolate*) = delete;
} // namespace blink
#endif // ToV8ForPlatform_h