blob: 65b479d8c92024db3fe7523b8e697c94d1337694 [file] [log] [blame]
/*
* Copyright (C) 2012 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "third_party/blink/renderer/bindings/core/v8/v8_dom_configuration.h"
#include "third_party/blink/renderer/platform/bindings/v8_object_constructor.h"
#include "third_party/blink/renderer/platform/bindings/v8_per_context_data.h"
#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
namespace blink {
namespace {
template <class Configuration>
bool WorldConfigurationApplies(const Configuration& config,
const DOMWrapperWorld& world) {
const auto current_world_config = world.IsMainWorld()
? V8DOMConfiguration::kMainWorld
: V8DOMConfiguration::kNonMainWorlds;
return config.world_configuration & current_world_config;
}
template <>
bool WorldConfigurationApplies(
const V8DOMConfiguration::SymbolKeyedMethodConfiguration&,
const DOMWrapperWorld&) {
return true;
}
void DoInstallAttribute(
v8::Local<v8::ObjectTemplate> object_template,
v8::Local<v8::Name> name,
const V8DOMConfiguration::AttributeConfiguration& attribute) {
v8::SideEffectType getter_side_effect_type =
attribute.getter_side_effect_type == V8DOMConfiguration::kHasNoSideEffect
? v8::SideEffectType::kHasNoSideEffect
: v8::SideEffectType::kHasSideEffect;
switch (static_cast<V8DOMConfiguration::AttributeGetterBehavior>(
attribute.getter_behavior)) {
case V8DOMConfiguration::kAlwaysCallGetter:
object_template->SetNativeDataProperty(
name, attribute.getter, attribute.setter, v8::Local<v8::Value>(),
static_cast<v8::PropertyAttribute>(attribute.attribute),
v8::Local<v8::AccessorSignature>(), v8::AccessControl::DEFAULT,
getter_side_effect_type);
break;
case V8DOMConfiguration::kReplaceWithDataProperty:
DCHECK(!attribute.setter);
DCHECK_EQ(attribute.world_configuration, V8DOMConfiguration::kAllWorlds);
object_template->SetLazyDataProperty(
name, attribute.getter, v8::Local<v8::Value>(),
static_cast<v8::PropertyAttribute>(attribute.attribute),
getter_side_effect_type);
break;
}
}
void DoInstallAttribute(
v8::Local<v8::Context> context,
v8::Local<v8::Object> object,
v8::Local<v8::Name> name,
const V8DOMConfiguration::AttributeConfiguration& attribute) {
v8::SideEffectType getter_side_effect_type =
attribute.getter_side_effect_type == V8DOMConfiguration::kHasNoSideEffect
? v8::SideEffectType::kHasNoSideEffect
: v8::SideEffectType::kHasSideEffect;
switch (static_cast<V8DOMConfiguration::AttributeGetterBehavior>(
attribute.getter_behavior)) {
case V8DOMConfiguration::kAlwaysCallGetter:
object
->SetNativeDataProperty(
context, name, attribute.getter, attribute.setter,
v8::Local<v8::Value>(),
static_cast<v8::PropertyAttribute>(attribute.attribute),
getter_side_effect_type)
.ToChecked();
break;
case V8DOMConfiguration::kReplaceWithDataProperty:
DCHECK(!attribute.setter);
DCHECK_EQ(attribute.world_configuration, V8DOMConfiguration::kAllWorlds);
object
->SetLazyDataProperty(
context, name, attribute.getter, v8::Local<v8::Value>(),
static_cast<v8::PropertyAttribute>(attribute.attribute),
getter_side_effect_type)
.ToChecked();
break;
}
}
void InstallAttributeInternal(
v8::Isolate* isolate,
v8::Local<v8::ObjectTemplate> instance_template,
v8::Local<v8::ObjectTemplate> prototype_template,
const V8DOMConfiguration::AttributeConfiguration& attribute,
const DOMWrapperWorld& world) {
if (!WorldConfigurationApplies(attribute, world))
return;
v8::Local<v8::Name> name = V8AtomicString(isolate, attribute.name);
const unsigned location = attribute.property_location_configuration;
DCHECK(location);
if (location & V8DOMConfiguration::kOnInstance)
DoInstallAttribute(instance_template, name, attribute);
if (location & V8DOMConfiguration::kOnPrototype)
DoInstallAttribute(prototype_template, name, attribute);
if (location & V8DOMConfiguration::kOnInterface)
NOTREACHED();
}
void InstallAttributeInternal(
v8::Isolate* isolate,
v8::Local<v8::Object> instance,
v8::Local<v8::Object> prototype,
const V8DOMConfiguration::AttributeConfiguration& config,
const DOMWrapperWorld& world) {
DCHECK(!instance.IsEmpty() || !prototype.IsEmpty());
if (!WorldConfigurationApplies(config, world))
return;
v8::Local<v8::Name> name = V8AtomicString(isolate, config.name);
const unsigned location = config.property_location_configuration;
DCHECK(location);
v8::Local<v8::Context> context = isolate->GetCurrentContext();
if (location & V8DOMConfiguration::kOnInstance && !instance.IsEmpty())
DoInstallAttribute(context, instance, name, config);
if (location & V8DOMConfiguration::kOnPrototype && !prototype.IsEmpty())
DoInstallAttribute(context, prototype, name, config);
if (location & V8DOMConfiguration::kOnInterface)
NOTREACHED();
}
enum class AccessorType {
Getter,
Setter,
};
template <class FunctionOrTemplate>
v8::Local<FunctionOrTemplate> CreateAccessorFunctionOrTemplate(
v8::Isolate*,
v8::FunctionCallback,
V8PrivateProperty::CachedAccessorSymbol,
v8::Local<v8::Value> data,
v8::Local<v8::Signature>,
const char* name,
AccessorType,
v8::SideEffectType side_effect_type = v8::SideEffectType::kHasSideEffect);
template <>
v8::Local<v8::FunctionTemplate>
CreateAccessorFunctionOrTemplate<v8::FunctionTemplate>(
v8::Isolate* isolate,
v8::FunctionCallback callback,
V8PrivateProperty::CachedAccessorSymbol cached_property_key,
v8::Local<v8::Value> data,
v8::Local<v8::Signature> signature,
const char* name,
AccessorType type,
v8::SideEffectType side_effect_type) {
v8::Local<v8::FunctionTemplate> function_template;
if (callback) {
// https://heycam.github.io/webidl/#dfn-attribute-getter has:
//
// 5. Perform ! SetFunctionLength(|F|, 0).
//
// https://heycam.github.io/webidl/#dfn-attribute-setter has:
//
// 7. Perform ! SetFunctionLength(|F|, 1).
int length = type == AccessorType::Getter ? 0 : 1;
if (cached_property_key != V8PrivateProperty::kNoCachedAccessor) {
function_template = v8::FunctionTemplate::NewWithCache(
isolate, callback,
V8PrivateProperty::GetCachedAccessor(isolate, cached_property_key)
.GetPrivate(),
data, signature, length, side_effect_type);
} else {
function_template = v8::FunctionTemplate::New(
isolate, callback, data, signature, length,
v8::ConstructorBehavior::kAllow, side_effect_type);
}
if (!function_template.IsEmpty()) {
function_template->RemovePrototype();
function_template->SetAcceptAnyReceiver(false);
// https://heycam.github.io/webidl/#dfn-attribute-getter has:
//
// 3. Let |name| be the string "get " prepended to |attribute|’s
// identifier.
//
// 4. Perform ! SetFunctionName(|F|, |name|).
//
// https://heycam.github.io/webidl/#dfn-attribute-setter has:
//
// 5. Let |name| be the string "set " prepended to |id|.
//
// 6. Perform ! SetFunctionName(|F|, |name|).
//
// SetClassName on a FunctionTemplate that doesn't have a prototype just
// sets the .name property of the generated function.
WTF::StringBuilder full_name_builder;
full_name_builder.Append(type == AccessorType::Getter ? "get " : "set ",
4);
full_name_builder.Append(name);
function_template->SetClassName(
V8AtomicString(isolate, full_name_builder.ToString()));
}
}
return function_template;
}
template <>
v8::Local<v8::Function> CreateAccessorFunctionOrTemplate<v8::Function>(
v8::Isolate* isolate,
v8::FunctionCallback callback,
V8PrivateProperty::CachedAccessorSymbol,
v8::Local<v8::Value> data,
v8::Local<v8::Signature> signature,
const char* name,
AccessorType type,
v8::SideEffectType side_effect_type) {
if (!callback)
return v8::Local<v8::Function>();
v8::Local<v8::FunctionTemplate> function_template =
CreateAccessorFunctionOrTemplate<v8::FunctionTemplate>(
isolate, callback, V8PrivateProperty::kNoCachedAccessor, data,
signature, name, type, side_effect_type);
if (function_template.IsEmpty())
return v8::Local<v8::Function>();
v8::Local<v8::Context> context = isolate->GetCurrentContext();
v8::Local<v8::Function> function;
if (!function_template->GetFunction(context).ToLocal(&function))
return v8::Local<v8::Function>();
return function;
}
// Returns true iff |target| is the empty handle to v8::Object.
// Returns false especially when |target| is a handle to v8::ObjectTemplate.
bool IsObjectAndEmpty(v8::Local<v8::Object> target) {
return target.IsEmpty();
}
bool IsObjectAndEmpty(v8::Local<v8::Function> target) {
return target.IsEmpty();
}
bool IsObjectAndEmpty(v8::Local<v8::ObjectTemplate> target) {
return false;
}
bool IsObjectAndEmpty(v8::Local<v8::FunctionTemplate> target) {
return false;
}
template <class ObjectOrTemplate, class FunctionOrTemplate>
void InstallAccessorInternal(
v8::Isolate* isolate,
v8::Local<ObjectOrTemplate> instance_or_template,
v8::Local<ObjectOrTemplate> prototype_or_template,
v8::Local<FunctionOrTemplate> interface_or_template,
v8::Local<v8::Signature> signature,
const V8DOMConfiguration::AccessorConfiguration& config,
const DOMWrapperWorld& world) {
DCHECK(!IsObjectAndEmpty(instance_or_template) ||
!IsObjectAndEmpty(prototype_or_template) ||
!IsObjectAndEmpty(interface_or_template));
DCHECK_EQ(config.getter_behavior, V8DOMConfiguration::kAlwaysCallGetter);
if (!WorldConfigurationApplies(config, world))
return;
v8::Local<v8::String> name = V8AtomicString(isolate, config.name);
v8::FunctionCallback getter_callback = config.getter;
v8::FunctionCallback setter_callback = config.setter;
auto cached_property_key = V8PrivateProperty::kNoCachedAccessor;
if (world.IsMainWorld()) {
cached_property_key = static_cast<V8PrivateProperty::CachedAccessorSymbol>(
config.cached_property_key);
}
// Support [LenientThis] and attributes with Promise types by not specifying
// the signature. V8 does not do the type checking against holder if no
// signature is specified. Note that info.Holder() passed to callbacks will
// be *unsafe*.
if (config.holder_check_configuration ==
V8DOMConfiguration::kDoNotCheckHolder)
signature = v8::Local<v8::Signature>();
const unsigned location = config.property_location_configuration;
v8::SideEffectType getter_side_effect_type =
config.getter_side_effect_type == V8DOMConfiguration::kHasNoSideEffect
? v8::SideEffectType::kHasNoSideEffect
: v8::SideEffectType::kHasSideEffect;
DCHECK(location);
if (location &
(V8DOMConfiguration::kOnInstance | V8DOMConfiguration::kOnPrototype)) {
v8::Local<FunctionOrTemplate> getter =
CreateAccessorFunctionOrTemplate<FunctionOrTemplate>(
isolate, getter_callback, cached_property_key,
v8::Local<v8::Value>(), signature, config.name,
AccessorType::Getter, getter_side_effect_type);
v8::Local<FunctionOrTemplate> setter =
CreateAccessorFunctionOrTemplate<FunctionOrTemplate>(
isolate, setter_callback, V8PrivateProperty::kNoCachedAccessor,
v8::Local<v8::Value>(), signature, config.name,
AccessorType::Setter);
if (location & V8DOMConfiguration::kOnInstance &&
!IsObjectAndEmpty(instance_or_template)) {
instance_or_template->SetAccessorProperty(
name, getter, setter,
static_cast<v8::PropertyAttribute>(config.attribute));
}
if (location & V8DOMConfiguration::kOnPrototype &&
!IsObjectAndEmpty(prototype_or_template)) {
prototype_or_template->SetAccessorProperty(
name, getter, setter,
static_cast<v8::PropertyAttribute>(config.attribute));
}
}
if (location & V8DOMConfiguration::kOnInterface &&
!IsObjectAndEmpty(interface_or_template)) {
// Attributes installed on the interface object must be static
// attributes, so no need to specify a signature, i.e. no need to do
// type check against a holder.
v8::Local<FunctionOrTemplate> getter =
CreateAccessorFunctionOrTemplate<FunctionOrTemplate>(
isolate, getter_callback, V8PrivateProperty::kNoCachedAccessor,
v8::Local<v8::Value>(), v8::Local<v8::Signature>(), config.name,
AccessorType::Getter, getter_side_effect_type);
v8::Local<FunctionOrTemplate> setter =
CreateAccessorFunctionOrTemplate<FunctionOrTemplate>(
isolate, setter_callback, V8PrivateProperty::kNoCachedAccessor,
v8::Local<v8::Value>(), v8::Local<v8::Signature>(), config.name,
AccessorType::Setter);
interface_or_template->SetAccessorProperty(
name, getter, setter,
static_cast<v8::PropertyAttribute>(config.attribute));
}
}
v8::Local<v8::Primitive> ValueForConstant(
v8::Isolate* isolate,
const V8DOMConfiguration::ConstantConfiguration& constant) {
v8::Local<v8::Primitive> value;
switch (constant.type) {
case V8DOMConfiguration::kConstantTypeShort:
case V8DOMConfiguration::kConstantTypeLong:
case V8DOMConfiguration::kConstantTypeUnsignedShort:
value = v8::Integer::New(isolate, constant.ivalue);
break;
case V8DOMConfiguration::kConstantTypeUnsignedLong:
value = v8::Integer::NewFromUnsigned(isolate, constant.ivalue);
break;
case V8DOMConfiguration::kConstantTypeFloat:
case V8DOMConfiguration::kConstantTypeDouble:
value = v8::Number::New(isolate, constant.dvalue);
break;
default:
NOTREACHED();
}
return value;
}
void InstallConstantInternal(
v8::Isolate* isolate,
v8::Local<v8::FunctionTemplate> interface_template,
v8::Local<v8::ObjectTemplate> prototype_template,
const V8DOMConfiguration::ConstantConfiguration& constant) {
v8::Local<v8::String> constant_name = V8AtomicString(isolate, constant.name);
v8::PropertyAttribute attributes =
static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete);
v8::Local<v8::Primitive> value = ValueForConstant(isolate, constant);
interface_template->Set(constant_name, value, attributes);
prototype_template->Set(constant_name, value, attributes);
}
void InstallConstantInternal(
v8::Isolate* isolate,
v8::Local<v8::Function> interface,
v8::Local<v8::Object> prototype,
const V8DOMConfiguration::ConstantConfiguration& constant) {
v8::Local<v8::Context> context = isolate->GetCurrentContext();
v8::Local<v8::Name> name = V8AtomicString(isolate, constant.name);
v8::PropertyAttribute attributes =
static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete);
v8::Local<v8::Primitive> value = ValueForConstant(isolate, constant);
interface->DefineOwnProperty(context, name, value, attributes).ToChecked();
prototype->DefineOwnProperty(context, name, value, attributes).ToChecked();
}
template <class Configuration>
void AddMethodToTemplate(v8::Isolate* isolate,
v8::Local<v8::Template> v8_template,
v8::Local<v8::FunctionTemplate> function_template,
const Configuration& method) {
v8_template->Set(method.MethodName(isolate), function_template,
static_cast<v8::PropertyAttribute>(method.attribute));
}
template <>
void AddMethodToTemplate(
v8::Isolate* isolate,
v8::Local<v8::Template> v8_template,
v8::Local<v8::FunctionTemplate> function_template,
const V8DOMConfiguration::SymbolKeyedMethodConfiguration& method) {
// The order matters here: if the Symbol is added first, the Function object
// will have no associated name. For example, WebIDL states, among other
// things, that a pair iterator's @@iterator Function object's name must be
// set to "entries".
if (method.symbol_alias) {
v8_template->Set(V8AtomicString(isolate, method.symbol_alias),
function_template);
}
v8_template->Set(method.MethodName(isolate), function_template,
static_cast<v8::PropertyAttribute>(method.attribute));
}
template <class Configuration>
void InstallMethodInternal(v8::Isolate* isolate,
v8::Local<v8::ObjectTemplate> instance_template,
v8::Local<v8::ObjectTemplate> prototype_template,
v8::Local<v8::FunctionTemplate> interface_template,
v8::Local<v8::Signature> signature,
const Configuration& method,
const DOMWrapperWorld& world) {
if (!WorldConfigurationApplies(method, world))
return;
v8::FunctionCallback callback = method.callback;
// Promise-returning functions need to return a reject promise when
// an exception occurs. This includes a case that the receiver object is not
// of the type. So, we disable the type check of the receiver object on V8
// side so that V8 won't throw. Instead, we do the check on Blink side and
// convert an exception to a reject promise.
if (method.holder_check_configuration ==
V8DOMConfiguration::kDoNotCheckHolder)
signature = v8::Local<v8::Signature>();
DCHECK(method.property_location_configuration);
v8::SideEffectType side_effect_type =
method.side_effect_type == V8DOMConfiguration::kHasNoSideEffect
? v8::SideEffectType::kHasNoSideEffect
: v8::SideEffectType::kHasSideEffect;
if (method.property_location_configuration &
(V8DOMConfiguration::kOnInstance | V8DOMConfiguration::kOnPrototype)) {
// TODO(luoe): use ConstructorBehavior::kThrow for non-constructor methods.
v8::Local<v8::FunctionTemplate> function_template =
v8::FunctionTemplate::New(
isolate, callback, v8::Local<v8::Value>(), signature, method.length,
v8::ConstructorBehavior::kAllow, side_effect_type);
function_template->RemovePrototype();
if (method.access_check_configuration == V8DOMConfiguration::kCheckAccess)
function_template->SetAcceptAnyReceiver(false);
if (method.property_location_configuration &
V8DOMConfiguration::kOnInstance) {
AddMethodToTemplate(isolate, instance_template, function_template,
method);
}
if (method.property_location_configuration &
V8DOMConfiguration::kOnPrototype) {
AddMethodToTemplate(isolate, prototype_template, function_template,
method);
}
}
if (method.property_location_configuration &
V8DOMConfiguration::kOnInterface) {
// Operations installed on the interface object must be static methods, so
// no need to specify a signature, i.e. no need to do type check against a
// holder.
// TODO(luoe): use ConstructorBehavior::kThrow for non-constructor methods.
v8::Local<v8::FunctionTemplate> function_template =
v8::FunctionTemplate::New(isolate, callback, v8::Local<v8::Value>(),
v8::Local<v8::Signature>(), method.length,
v8::ConstructorBehavior::kAllow,
side_effect_type);
function_template->RemovePrototype();
// Similarly, there is no need to do an access check for static methods, as
// there is no holder to check against.
AddMethodToTemplate(isolate, interface_template, function_template, method);
}
}
void InstallMethodInternal(
v8::Isolate* isolate,
v8::Local<v8::Object> instance,
v8::Local<v8::Object> prototype,
v8::Local<v8::Function> interface,
v8::Local<v8::Signature> signature,
const V8DOMConfiguration::MethodConfiguration& config,
const DOMWrapperWorld& world) {
DCHECK(!instance.IsEmpty() || !prototype.IsEmpty() || !interface.IsEmpty());
if (!WorldConfigurationApplies(config, world))
return;
v8::Local<v8::Name> name = config.MethodName(isolate);
v8::FunctionCallback callback = config.callback;
// Promise-returning functions need to return a reject promise when
// an exception occurs. This includes a case that the receiver object is not
// of the type. So, we disable the type check of the receiver object on V8
// side so that V8 won't throw. Instead, we do the check on Blink side and
// convert an exception to a reject promise.
if (config.holder_check_configuration ==
V8DOMConfiguration::kDoNotCheckHolder)
signature = v8::Local<v8::Signature>();
const unsigned location = config.property_location_configuration;
v8::SideEffectType side_effect_type =
config.side_effect_type == V8DOMConfiguration::kHasNoSideEffect
? v8::SideEffectType::kHasNoSideEffect
: v8::SideEffectType::kHasSideEffect;
DCHECK(location);
if (location &
(V8DOMConfiguration::kOnInstance | V8DOMConfiguration::kOnPrototype)) {
// TODO(luoe): use ConstructorBehavior::kThrow for non-constructor methods.
v8::Local<v8::FunctionTemplate> function_template =
v8::FunctionTemplate::New(
isolate, callback, v8::Local<v8::Value>(), signature, config.length,
v8::ConstructorBehavior::kAllow, side_effect_type);
function_template->RemovePrototype();
if (config.access_check_configuration == V8DOMConfiguration::kCheckAccess) {
function_template->SetAcceptAnyReceiver(false);
}
v8::Local<v8::Function> function =
function_template->GetFunction(isolate->GetCurrentContext())
.ToLocalChecked();
if (location & V8DOMConfiguration::kOnInstance && !instance.IsEmpty()) {
instance
->DefineOwnProperty(
isolate->GetCurrentContext(), name, function,
static_cast<v8::PropertyAttribute>(config.attribute))
.ToChecked();
}
if (location & V8DOMConfiguration::kOnPrototype && !prototype.IsEmpty()) {
prototype
->DefineOwnProperty(
isolate->GetCurrentContext(), name, function,
static_cast<v8::PropertyAttribute>(config.attribute))
.ToChecked();
}
}
if (location & V8DOMConfiguration::kOnInterface && !interface.IsEmpty()) {
// Operations installed on the interface object must be static
// operations, so no need to specify a signature, i.e. no need to do
// type check against a holder.
// TODO(luoe): use ConstructorBehavior::kThrow for non-constructor methods.
v8::Local<v8::FunctionTemplate> function_template =
v8::FunctionTemplate::New(isolate, callback, v8::Local<v8::Value>(),
v8::Local<v8::Signature>(), config.length,
v8::ConstructorBehavior::kAllow,
side_effect_type);
function_template->RemovePrototype();
v8::Local<v8::Function> function =
function_template->GetFunction(isolate->GetCurrentContext())
.ToLocalChecked();
interface->DefineOwnProperty(isolate->GetCurrentContext(), name, function, static_cast<v8::PropertyAttribute>(config.attribute)).ToChecked();
}
}
} // namespace
void V8DOMConfiguration::InstallAttributes(
v8::Isolate* isolate,
const DOMWrapperWorld& world,
v8::Local<v8::ObjectTemplate> instance_template,
v8::Local<v8::ObjectTemplate> prototype_template,
const AttributeConfiguration* attributes,
size_t attribute_count) {
for (size_t i = 0; i < attribute_count; ++i)
InstallAttributeInternal(isolate, instance_template, prototype_template,
attributes[i], world);
}
void V8DOMConfiguration::InstallAttributes(
v8::Isolate* isolate,
const DOMWrapperWorld& world,
v8::Local<v8::Object> instance,
v8::Local<v8::Object> prototype,
const AttributeConfiguration* attributes,
size_t attribute_count) {
for (size_t i = 0; i < attribute_count; ++i) {
InstallAttributeInternal(isolate, instance, prototype, attributes[i],
world);
}
}
void V8DOMConfiguration::InstallAttribute(
v8::Isolate* isolate,
const DOMWrapperWorld& world,
v8::Local<v8::ObjectTemplate> instance_template,
v8::Local<v8::ObjectTemplate> prototype_template,
const AttributeConfiguration& attribute) {
InstallAttributeInternal(isolate, instance_template, prototype_template,
attribute, world);
}
void V8DOMConfiguration::InstallAttribute(
v8::Isolate* isolate,
const DOMWrapperWorld& world,
v8::Local<v8::Object> instance,
v8::Local<v8::Object> prototype,
const AttributeConfiguration& attribute) {
InstallAttributeInternal(isolate, instance, prototype, attribute, world);
}
void V8DOMConfiguration::InstallAccessors(
v8::Isolate* isolate,
const DOMWrapperWorld& world,
v8::Local<v8::ObjectTemplate> instance_template,
v8::Local<v8::ObjectTemplate> prototype_template,
v8::Local<v8::FunctionTemplate> interface_template,
v8::Local<v8::Signature> signature,
const AccessorConfiguration* accessors,
size_t accessor_count) {
for (size_t i = 0; i < accessor_count; ++i)
InstallAccessorInternal(isolate, instance_template, prototype_template,
interface_template, signature, accessors[i], world);
}
void V8DOMConfiguration::InstallAccessors(
v8::Isolate* isolate,
const DOMWrapperWorld& world,
v8::Local<v8::Object> instance,
v8::Local<v8::Object> prototype,
v8::Local<v8::Function> interface,
v8::Local<v8::Signature> signature,
const AccessorConfiguration* accessors,
size_t accessor_count) {
for (size_t i = 0; i < accessor_count; ++i) {
InstallAccessorInternal(isolate, instance, prototype, interface, signature,
accessors[i], world);
}
}
void V8DOMConfiguration::InstallAccessor(
v8::Isolate* isolate,
const DOMWrapperWorld& world,
v8::Local<v8::ObjectTemplate> instance_template,
v8::Local<v8::ObjectTemplate> prototype_template,
v8::Local<v8::FunctionTemplate> interface_template,
v8::Local<v8::Signature> signature,
const AccessorConfiguration& accessor) {
InstallAccessorInternal(isolate, instance_template, prototype_template,
interface_template, signature, accessor, world);
}
void V8DOMConfiguration::InstallAccessor(
v8::Isolate* isolate,
const DOMWrapperWorld& world,
v8::Local<v8::Object> instance,
v8::Local<v8::Object> prototype,
v8::Local<v8::Function> interface,
v8::Local<v8::Signature> signature,
const AccessorConfiguration& accessor) {
InstallAccessorInternal(isolate, instance, prototype, interface, signature,
accessor, world);
}
void V8DOMConfiguration::InstallConstants(
v8::Isolate* isolate,
v8::Local<v8::FunctionTemplate> interface_template,
v8::Local<v8::ObjectTemplate> prototype_template,
const ConstantConfiguration* constants,
size_t constant_count) {
for (size_t i = 0; i < constant_count; ++i)
InstallConstantInternal(isolate, interface_template, prototype_template,
constants[i]);
}
void V8DOMConfiguration::InstallConstant(
v8::Isolate* isolate,
v8::Local<v8::FunctionTemplate> interface_template,
v8::Local<v8::ObjectTemplate> prototype_template,
const ConstantConfiguration& constant) {
InstallConstantInternal(isolate, interface_template, prototype_template,
constant);
}
void V8DOMConfiguration::InstallConstant(
v8::Isolate* isolate,
v8::Local<v8::Function> interface,
v8::Local<v8::Object> prototype,
const ConstantConfiguration& constant) {
InstallConstantInternal(isolate, interface, prototype, constant);
}
void V8DOMConfiguration::InstallConstantWithGetter(
v8::Isolate* isolate,
v8::Local<v8::FunctionTemplate> interface_template,
v8::Local<v8::ObjectTemplate> prototype_template,
const char* name,
v8::AccessorNameGetterCallback getter) {
v8::Local<v8::String> constant_name = V8AtomicString(isolate, name);
v8::PropertyAttribute attributes =
static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete);
interface_template->SetNativeDataProperty(constant_name, getter, nullptr,
v8::Local<v8::Value>(), attributes);
prototype_template->SetNativeDataProperty(constant_name, getter, nullptr,
v8::Local<v8::Value>(), attributes);
}
void V8DOMConfiguration::InstallMethods(
v8::Isolate* isolate,
const DOMWrapperWorld& world,
v8::Local<v8::ObjectTemplate> instance_template,
v8::Local<v8::ObjectTemplate> prototype_template,
v8::Local<v8::FunctionTemplate> interface_template,
v8::Local<v8::Signature> signature,
const MethodConfiguration* methods,
size_t method_count) {
for (size_t i = 0; i < method_count; ++i)
InstallMethodInternal(isolate, instance_template, prototype_template,
interface_template, signature, methods[i], world);
}
void V8DOMConfiguration::InstallMethod(
v8::Isolate* isolate,
const DOMWrapperWorld& world,
v8::Local<v8::ObjectTemplate> instance_template,
v8::Local<v8::ObjectTemplate> prototype_template,
v8::Local<v8::FunctionTemplate> interface_template,
v8::Local<v8::Signature> signature,
const MethodConfiguration& method) {
InstallMethodInternal(isolate, instance_template, prototype_template,
interface_template, signature, method, world);
}
void V8DOMConfiguration::InstallMethod(v8::Isolate* isolate,
const DOMWrapperWorld& world,
v8::Local<v8::Object> instance,
v8::Local<v8::Object> prototype,
v8::Local<v8::Function> interface,
v8::Local<v8::Signature> signature,
const MethodConfiguration& method) {
InstallMethodInternal(isolate, instance, prototype, interface, signature,
method, world);
}
void V8DOMConfiguration::InstallMethod(
v8::Isolate* isolate,
const DOMWrapperWorld& world,
v8::Local<v8::ObjectTemplate> prototype_template,
v8::Local<v8::Signature> signature,
const SymbolKeyedMethodConfiguration& method) {
InstallMethodInternal(isolate, v8::Local<v8::ObjectTemplate>(),
prototype_template, v8::Local<v8::FunctionTemplate>(),
signature, method, world);
}
void V8DOMConfiguration::InitializeDOMInterfaceTemplate(
v8::Isolate* isolate,
v8::Local<v8::FunctionTemplate> interface_template,
const char* interface_name,
v8::Local<v8::FunctionTemplate> parent_interface_template,
uint32_t v8_internal_field_count) {
interface_template->SetClassName(V8AtomicString(isolate, interface_name));
interface_template->ReadOnlyPrototype();
v8::Local<v8::ObjectTemplate> instance_template =
interface_template->InstanceTemplate();
v8::Local<v8::ObjectTemplate> prototype_template =
interface_template->PrototypeTemplate();
instance_template->SetInternalFieldCount(v8_internal_field_count);
// We intentionally don't set the class string to the platform object
// (|instanceTemplate|), and set the class string "InterfaceName", without
// "Prototype", to the prototype object (|prototypeTemplate|) despite a fact
// that the current WebIDL spec (as of Feb 2017) requires to set the class
// string "InterfaceName" for the platform objects and
// "InterfaceNamePrototype" for the interface prototype object, because we
// think it's more consistent with ECMAScript 2016.
// See also https://crbug.com/643712
// https://heycam.github.io/webidl/#es-platform-objects
// https://heycam.github.io/webidl/#interface-prototype-object
//
// Note that V8 minor GC does not collect an object which has an own property.
// So, if we set the class string to the platform object as an own property,
// it prevents V8 minor GC to collect the object (V8 minor GC only collects
// an empty object). If set, a layout test fast/dom/minor-dom-gc.html fails.
SetClassString(isolate, prototype_template, interface_name);
if (!parent_interface_template.IsEmpty()) {
interface_template->Inherit(parent_interface_template);
// Marks the prototype object as one of native-backed objects.
// This is needed since bug 110436 asks WebKit to tell native-initiated
// prototypes from pure-JS ones. This doesn't mark kinds "root" classes
// like Node, where setting this changes prototype chain structure.
// The value of this field is not used, only the count.
prototype_template->SetInternalFieldCount(kV8PrototypeInternalFieldcount);
}
}
v8::Local<v8::FunctionTemplate> V8DOMConfiguration::DomClassTemplate(
v8::Isolate* isolate,
const DOMWrapperWorld& world,
WrapperTypeInfo* wrapper_type_info,
InstallTemplateFunction configure_dom_class_template) {
V8PerIsolateData* data = V8PerIsolateData::From(isolate);
v8::Local<v8::FunctionTemplate> interface_template =
data->FindInterfaceTemplate(world, wrapper_type_info);
if (!interface_template.IsEmpty())
return interface_template;
// We assume all constructors have no JS-observable side effect.
interface_template = v8::FunctionTemplate::New(
isolate, V8ObjectConstructor::IsValidConstructorMode);
configure_dom_class_template(isolate, world, interface_template);
data->SetInterfaceTemplate(world, wrapper_type_info, interface_template);
return interface_template;
}
void V8DOMConfiguration::SetClassString(
v8::Isolate* isolate,
v8::Local<v8::ObjectTemplate> object_template,
const char* class_string) {
object_template->Set(
v8::Symbol::GetToStringTag(isolate),
V8AtomicString(isolate, class_string),
static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
}
} // namespace blink