blob: b235049b6175ad9973c0f144210d0197a75163f8 [file] [log] [blame]
{% from 'utilities.cpp.tmpl' import declare_enum_validation_variable, v8_value_to_local_cpp_value %}
{##############################################################################}
{% macro attribute_getter(attribute, world_suffix) %}
static void {{attribute.name}}AttributeGetter{{world_suffix}}(
{%- if attribute.is_data_type_property %}
const v8::PropertyCallbackInfo<v8::Value>& info
{%- else %}
const v8::FunctionCallbackInfo<v8::Value>& info
{%- endif %}) {
{% filter format_remove_duplicates(['ExceptionState exceptionState']) %}
{% set define_exception_state -%}
ExceptionState exceptionState(info.GetIsolate(), ExceptionState::kGetterContext, "{{interface_name}}", "{{attribute.name}}");
{%- endset %}
{% if attribute.is_lenient_this %}
// [LenientThis]
// Make sure that info.Holder() really points to an instance if [LenientThis].
if (!{{v8_class}}::hasInstance(info.Holder(), info.GetIsolate()))
return; // Return silently because of [LenientThis].
{% endif %}
{% if not attribute.is_static or attribute.is_save_same_object %}
v8::Local<v8::Object> holder = info.Holder();
{% endif %}
{% if attribute.is_save_same_object %}
// [SaveSameObject]
{% set same_object_private_key = interface_name + attribute.name[0]|capitalize + attribute.name[1:] %}
// If you see a compile error that
// V8PrivateProperty::GetSameObject{{same_object_private_key}}
// is not defined, then you need to register your attribute at
// V8_PRIVATE_PROPERTY_FOR_EACH defined in V8PrivateProperty.h as
// X(SameObject, {{same_object_private_key}})
auto privateSameObject = V8PrivateProperty::GetSameObject{{same_object_private_key}}(info.GetIsolate());
{
v8::Local<v8::Value> v8Value = privateSameObject.GetOrEmpty(holder);
if (!v8Value.IsEmpty()) {
V8SetReturnValue(info, v8Value);
return;
}
}
{% endif %}
{% if not attribute.is_static %}
{% set local_dom_window_only = interface_name == 'Window' and not attribute.has_cross_origin_getter %}
{% if local_dom_window_only %}
{% if attribute.is_check_security_for_receiver %}
{{cpp_class}}* uncheckedImpl = {{v8_class}}::toImpl(holder);
{% else %}
// Same-origin attribute getters are never exposed via the cross-origin
// interceptors. Since same-origin access requires a LocalDOMWindow, it is
// safe to downcast here.
LocalDOMWindow* impl = ToLocalDOMWindow({{v8_class}}::toImpl(holder));
{% endif %}{# attribute.is_check_security_for_receiver #}
{% else %}
{{cpp_class}}* impl = {{v8_class}}::toImpl(holder);
{% endif %}{# local_dom_window_only #}
{% endif %}{# not attribute.is_static #}
{% if attribute.cached_attribute_validation_method %}
// [CachedAttribute]
V8PrivateProperty::Symbol propertySymbol =
V8PrivateProperty::GetSymbol(info.GetIsolate(),
"{{cpp_class}}#{{attribute.name.capitalize()}}");
if (!impl->{{attribute.cached_attribute_validation_method}}()) {
v8::Local<v8::Value> v8Value = propertySymbol.GetOrUndefined(holder);
if (!v8Value->IsUndefined()) {
V8SetReturnValue(info, v8Value);
return;
}
}
{% endif %}
{% if attribute.is_check_security_for_receiver and not attribute.is_data_type_property %}
// Perform a security check for the receiver object.
{{define_exception_state}}
{% if local_dom_window_only %}
if (!BindingSecurity::ShouldAllowAccessTo(CurrentDOMWindow(info.GetIsolate()), uncheckedImpl, exceptionState)) {
{% else %}
if (!BindingSecurity::ShouldAllowAccessTo(CurrentDOMWindow(info.GetIsolate()), impl, exceptionState)) {
{% endif %}{# local_dom_window_only #}
V8SetReturnValueNull(info);
return;
}
{% if local_dom_window_only %}
LocalDOMWindow* impl = ToLocalDOMWindow(uncheckedImpl);
{% endif %}{# local_dom_window_only #}
{% endif %}
{% if attribute.is_check_security_for_return_value %}
// Perform a security check for the returned object.
{{define_exception_state}}
if (!BindingSecurity::ShouldAllowAccessTo(CurrentDOMWindow(info.GetIsolate()), {{attribute.cpp_value}}, exceptionState)) {
V8SetReturnValueNull(info);
return;
}
{% endif %}
{% if attribute.is_call_with_execution_context %}
ExecutionContext* executionContext = CurrentExecutionContext(info.GetIsolate());
{% endif %}
{% if attribute.is_call_with_script_state %}
{% if attribute.is_static %}
ScriptState* scriptState = ScriptState::ForFunctionObject(info);
{% else %}
ScriptState* scriptState = ScriptState::ForReceiverObject(info);
{% endif %}
{% endif %}
{% if attribute.is_getter_raises_exception %}
{{define_exception_state}}
{% endif %}
{% if attribute.is_explicit_nullable %}
bool isNull = false;
{% endif %}
{% if attribute.cpp_value_original %}
{{attribute.cpp_type}} {{attribute.cpp_value}}({{attribute.cpp_value_original}});
{% endif %}
{% if attribute.use_output_parameter_for_result %}
{{attribute.cpp_type}} result;
{{attribute.cpp_value}};
{% endif %}
{% if attribute.is_getter_raises_exception %}
if (UNLIKELY(exceptionState.HadException()))
return;
{% endif %}
{% if attribute.reflect_only %}
{{release_only_check(attribute.reflect_only, attribute.reflect_missing,
attribute.reflect_invalid, attribute.reflect_empty,
attribute.cpp_value)
| indent(2)}}
{% endif %}
{% if attribute.cached_attribute_validation_method %}
// [CachedAttribute]
v8::Local<v8::Value> v8Value({{attribute.cpp_value_to_v8_value}});
propertySymbol.Set(holder, v8Value);
{% endif %}
{% if attribute.is_explicit_nullable %}
if (isNull) {
V8SetReturnValueNull(info);
return;
}
{% endif %}
{% if attribute.is_keep_alive_for_gc %}
// Keep the wrapper object for the return value alive as long as |this|
// object is alive in order to save creation time of the wrapper object.
if ({{attribute.cpp_value}} && DOMDataStore::SetReturnValue{{world_suffix}}(info.GetReturnValue(), {{attribute.cpp_value_to_script_wrappable}}))
return;
v8::Local<v8::Value> v8Value(ToV8({{attribute.cpp_value_to_script_wrappable}}, holder, info.GetIsolate()));
V8PrivateProperty::GetSymbol(
info.GetIsolate(), "KeepAlive#{{interface_name}}#{{attribute.name}}")
.Set(holder, v8Value);
{% endif %}
{% if world_suffix %}
{{attribute.v8_set_return_value_for_main_world}};
{% else %}
{{attribute.v8_set_return_value}};
{% endif %}
{% if attribute.is_save_same_object %}
// [SaveSameObject]
privateSameObject.Set(holder, info.GetReturnValue().Get());
{% endif %}
{% endfilter %}{# format_remove_duplicates #}
}
{% endmacro %}
{######################################}
{% macro release_only_check(reflect_only_values, reflect_missing,
reflect_invalid, reflect_empty, cpp_value) %}
{# Attribute is limited to only known values: check that the attribute value is
one of those. If not, set it to the empty string.
http://www.whatwg.org/specs/web-apps/current-work/#limited-to-only-known-values #}
{% if reflect_empty %}
if ({{cpp_value}}.IsNull()) {
{% if reflect_missing %}
{{cpp_value}} = "{{reflect_missing}}";
{% else %}
;
{% endif %}
} else if ({{cpp_value}}.IsEmpty()) {
{{cpp_value}} = "{{reflect_empty}}";
{% else %}
if ({{cpp_value}}.IsEmpty()) {
{# FIXME: should use [ReflectEmpty] instead; need to change IDL files #}
{% if reflect_missing %}
{{cpp_value}} = "{{reflect_missing}}";
{% else %}
;
{% endif %}
{% endif %}
{% for value in reflect_only_values %}
} else if (EqualIgnoringASCIICase({{cpp_value}}, "{{value}}")) {
{{cpp_value}} = "{{value}}";
{% endfor %}
} else {
{{cpp_value}} = "{{reflect_invalid}}";
}
{% endmacro %}
{##############################################################################}
{% macro attribute_getter_callback(attribute, world_suffix) %}
void {{v8_class_or_partial}}::{{attribute.name}}AttributeGetterCallback{{world_suffix}}(
{%- if attribute.is_data_type_property %}
v8::Local<v8::Name>, const v8::PropertyCallbackInfo<v8::Value>& info
{%- else %}
const v8::FunctionCallbackInfo<v8::Value>& info
{%- endif %}) {
{% if attribute.deprecate_as %}
Deprecation::CountDeprecation(CurrentExecutionContext(info.GetIsolate()), UseCounter::k{{attribute.deprecate_as}});
{% endif %}
{% if attribute.measure_as %}
UseCounter::Count(CurrentExecutionContext(info.GetIsolate()), UseCounter::k{{attribute.measure_as('AttributeGetter')}});
{% endif %}
{% if world_suffix in attribute.activity_logging_world_list_for_getter %}
{% if attribute.is_static %}
ScriptState* scriptState = ScriptState::ForFunctionObject(info);
{% else %}
ScriptState* scriptState = ScriptState::ForReceiverObject(info);
{% endif %}
V8PerContextData* contextData = scriptState->PerContextData();
if (
{%- if attribute.activity_logging_world_check -%}
scriptState->World().IsIsolatedWorld() && {# one space at the end #}
{%- endif -%}
contextData && contextData->ActivityLogger()) {
contextData->ActivityLogger()->LogGetter("{{interface_name}}.{{attribute.name}}");
}
{% endif %}
{% if attribute.has_custom_getter %}
{{v8_class}}::{{attribute.name}}AttributeGetterCustom(info);
{% else %}
{{cpp_class_or_partial}}V8Internal::{{attribute.name}}AttributeGetter{{world_suffix}}(info);
{% endif %}
}
{% endmacro %}
{##############################################################################}
{% macro attribute_cached_property_key(attribute) %}
v8::Local<v8::Private> {{v8_class_or_partial}}::{{attribute.name}}CachedPropertyKey(v8::Isolate* isolate)
{
return V8PrivateProperty::Get{{attribute.cached_accessor_name}}(isolate).GetPrivate();
}
{% endmacro %}
{##############################################################################}
{% macro constructor_getter_callback(attribute, world_suffix) %}
void {{v8_class_or_partial}}::{{attribute.name}}ConstructorGetterCallback{{world_suffix}}(v8::Local<v8::Name> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
{% if attribute.deprecate_as %}
Deprecation::CountDeprecation(CurrentExecutionContext(info.GetIsolate()), UseCounter::k{{attribute.deprecate_as}});
{% endif %}
{% if attribute.measure_as %}
UseCounter::Count(CurrentExecutionContext(info.GetIsolate()), UseCounter::k{{attribute.measure_as('ConstructorGetter')}});
{% endif %}
V8ConstructorAttributeGetter(property, info);
}
{% endmacro %}
{##############################################################################}
{% macro attribute_setter(attribute, world_suffix) %}
static void {{attribute.name}}AttributeSetter{{world_suffix}}(
{%- if attribute.has_cross_origin_setter %}
v8::Local<v8::Value> v8Value, const V8CrossOriginSetterInfo& info
{%- elif attribute.is_data_type_property %}
v8::Local<v8::Value> v8Value, const v8::PropertyCallbackInfo<void>& info
{%- else %}
v8::Local<v8::Value> v8Value, const v8::FunctionCallbackInfo<v8::Value>& info
{%- endif %}) {
{% filter format_remove_duplicates(['ExceptionState exceptionState']) %}
v8::Isolate* isolate = info.GetIsolate();
ALLOW_UNUSED_LOCAL(isolate);
v8::Local<v8::Object> holder = info.Holder();
ALLOW_UNUSED_LOCAL(holder);
{% set define_exception_state -%}
ExceptionState exceptionState(isolate, ExceptionState::kSetterContext, "{{interface_name}}", "{{attribute.name}}");
{%- endset %}
{% if attribute.is_lenient_this %}
// [LenientThis]
// Make sure that info.Holder() really points to an instance if [LenientThis].
if (!{{v8_class}}::hasInstance(holder, isolate))
return; // Return silently because of [LenientThis].
{% endif %}
{% if not attribute.is_static and not attribute.is_replaceable and not attribute.is_put_forwards %}
{% set local_dom_window_only = interface_name == 'Window' and not attribute.has_cross_origin_setter %}
{% if local_dom_window_only %}
{% if attribute.is_check_security_for_receiver %}
{{cpp_class}}* uncheckedImpl = {{v8_class}}::toImpl(holder);
{% else %}
// Same-origin attributes setters are never exposed via the cross-origin
// interceptors. Since same-origin access requires a LocalDOMWindow, it is
// safe to downcast here.
LocalDOMWindow* impl = ToLocalDOMWindow({{v8_class}}::toImpl(holder));
{% endif %}{# attribute.is_check_security_for_receiver #}
{% else %}
{{cpp_class}}* impl = {{v8_class}}::toImpl(holder);
{% endif %}{# local_dom_window_only #}
{% endif %}
{% if attribute.is_check_security_for_receiver and not attribute.is_data_type_property %}
// Perform a security check for the receiver object.
{{define_exception_state}}
{% if local_dom_window_only %}
if (!BindingSecurity::ShouldAllowAccessTo(CurrentDOMWindow(isolate), uncheckedImpl, exceptionState)) {
{% else %}
if (!BindingSecurity::ShouldAllowAccessTo(CurrentDOMWindow(isolate), impl, exceptionState)) {
{% endif %}{# local_dom_window_only #}
V8SetReturnValue(info, v8Value);
return;
}
{% if local_dom_window_only %}
LocalDOMWindow* impl = ToLocalDOMWindow(uncheckedImpl);
{% endif %}{# local_dom_window_only #}
{% endif %}
{% if attribute.is_check_security_for_return_value %}
#error Attribute setter with the security check for the return value is not supported. Since the return value is the given value to be set, it\'s meaningless to perform the security check for the return value.
{% endif %}
{% if attribute.is_put_forwards %}
// [PutForwards] => {{attribute.name}}.{{attribute.target_attribute_name}}
{{define_exception_state}}
v8::Local<v8::Value> target;
if (!holder->Get(isolate->GetCurrentContext(), V8String(isolate, "{{attribute.name}}")).ToLocal(&target))
return;
if (!target->IsObject()) {
exceptionState.ThrowTypeError("The attribute value is not an object");
return;
}
bool result;
if (!target.As<v8::Object>()->Set(isolate->GetCurrentContext(), V8String(isolate, "{{attribute.target_attribute_name}}"), v8Value).To(&result))
return;
if (!result)
return;
{% else %}{# attribute.is_put_forwards #}
{% if attribute.is_custom_element_callbacks or
(attribute.is_reflect and not (attribute.idl_type == 'DOMString' and is_node)) %}
// Skip on compact node DOMString getters.
V0CustomElementProcessingStack::CallbackDeliveryScope deliveryScope;
{% endif %}
{% if attribute.has_setter_exception_state %}
{{define_exception_state}}
{% endif %}
// Prepare the value to be set.
{% if attribute.idl_type == 'EventHandler' %}
{% if not is_node %}
MoveEventListenerToNewWrapper(isolate, holder, {{attribute.event_handler_getter_expression}}, v8Value, {{v8_class}}::eventListenerCacheIndex);
{% endif %}
{% else %}{# not EventHandler #}
{{v8_value_to_local_cpp_value(attribute) | indent(2)}}
{% endif %}
{% if attribute.has_type_checking_interface %}
// Type check per: http://heycam.github.io/webidl/#es-interface
if (!cppValue{% if attribute.is_nullable %} && !IsUndefinedOrNull(v8Value){% endif %}) {
exceptionState.ThrowTypeError("The provided value is not of type '{{attribute.idl_type}}'.");
return;
}
{% endif %}
{% if attribute.enum_values %}
// Type check per: http://heycam.github.io/webidl/#dfn-attribute-setter
// Returns undefined without setting the value if the value is invalid.
DummyExceptionStateForTesting dummyExceptionState;
{{declare_enum_validation_variable(attribute.enum_values) | indent(2)}}
if (!IsValidEnum(cppValue, validValues, WTF_ARRAY_LENGTH(validValues), "{{attribute.enum_type}}", dummyExceptionState)) {
CurrentExecutionContext(isolate)->AddConsoleMessage(ConsoleMessage::Create(kJSMessageSource, kWarningMessageLevel, dummyExceptionState.Message()));
return;
}
{% endif %}
{% if attribute.is_call_with_execution_context or attribute.is_setter_call_with_execution_context %}
ExecutionContext* executionContext = CurrentExecutionContext(isolate);
{% endif %}
{% if attribute.is_call_with_script_state %}
{% if attribute.is_static %}
ScriptState* scriptState = ScriptState::ForFunctionObject(info);
{% else %}
ScriptState* scriptState = ScriptState::ForReceiverObject(info);
{% endif %}
{% endif %}
{% if attribute.is_replaceable %}
v8::Local<v8::String> propertyName = V8AtomicString(isolate, "{{attribute.name}}");
{% endif %}
{% if attribute.is_explicit_nullable %}
bool isNull = IsUndefinedOrNull(v8Value);
{% endif %}
{{attribute.cpp_setter}};
{% if attribute.cached_attribute_validation_method %}
// [CachedAttribute]
// Invalidate the cached value.
V8PrivateProperty::GetSymbol(
isolate, "{{cpp_class}}#{{attribute.name.capitalize()}}")
.DeleteProperty(holder, v8::Undefined(isolate));
{% endif %}
{% endif %}{# attribute.is_put_forwards #}
}
{% endfilter %}{# format_remove_duplicates #}
{% endmacro %}
{##############################################################################}
{% macro attribute_setter_callback(attribute, world_suffix) %}
void {{v8_class_or_partial}}::{{attribute.name}}AttributeSetterCallback{{world_suffix}}(
{%- if attribute.is_data_type_property %}
v8::Local<v8::Name>, v8::Local<v8::Value> v8Value, const v8::PropertyCallbackInfo<void>& info
{%- else %}
const v8::FunctionCallbackInfo<v8::Value>& info
{%- endif %}) {
{% if not attribute.is_data_type_property %}
v8::Local<v8::Value> v8Value = info[0];
{% endif %}
{% if attribute.deprecate_as %}
Deprecation::CountDeprecation(CurrentExecutionContext(info.GetIsolate()), UseCounter::k{{attribute.deprecate_as}});
{% endif %}
{% if attribute.measure_as %}
UseCounter::Count(CurrentExecutionContext(info.GetIsolate()), UseCounter::k{{attribute.measure_as('AttributeSetter')}});
{% endif %}
{% if world_suffix in attribute.activity_logging_world_list_for_setter %}
{% if attribute.is_static %}
ScriptState* scriptState = ScriptState::ForFunctionObject(info);
{% else %}
ScriptState* scriptState = ScriptState::ForReceiverObject(info);
{% endif %}
V8PerContextData* contextData = scriptState->PerContextData();
if (
{%- if attribute.activity_logging_world_check -%}
scriptState->World().IsIsolatedWorld() && {# one space at the end #}
{%- endif -%}
contextData && contextData->ActivityLogger()) {
contextData->ActivityLogger()->LogSetter("{{interface_name}}.{{attribute.name}}", v8Value);
}
{% endif %}
{% if attribute.is_ce_reactions %}
CEReactionsScope ceReactionsScope;
{% endif %}
{% if attribute.is_custom_element_callbacks or attribute.is_reflect %}
V0CustomElementProcessingStack::CallbackDeliveryScope deliveryScope;
{% endif %}
{% if attribute.has_custom_setter %}
{{v8_class}}::{{attribute.name}}AttributeSetterCustom(v8Value, info);
{% elif attribute.has_cross_origin_setter %}
{{cpp_class_or_partial}}V8Internal::{{attribute.name}}AttributeSetter(v8Value, V8CrossOriginSetterInfo(info.GetIsolate(), info.Holder()));
{% else %}
{{cpp_class_or_partial}}V8Internal::{{attribute.name}}AttributeSetter{{world_suffix}}(v8Value, info);
{% endif %}
}
{% endmacro %}
{##############################################################################}
{% macro build_attribute_or_accessor_configuration(attribute, config_type) %}
{% from 'utilities.cpp.tmpl' import property_location %}
{% if attribute.constructor_type %}
{% if attribute.needs_constructor_getter_callback %}
{% set getter_callback = '%s::%sConstructorGetterCallback' % (v8_class_or_partial, attribute.name) %}
{% else %}
{% set getter_callback = 'V8%s::NamedConstructorAttributeGetter' % (attribute.constructor_type)
if attribute.is_named_constructor else 'V8ConstructorAttributeGetter' %}
{% endif %}
{% set setter_callback = 'nullptr' %}
{% else %}{# regular attributes #}
{% set getter_callback = '%s::%sAttributeGetterCallback' %
(v8_class_or_partial, attribute.name) %}
{% set setter_callback = '%s::%sAttributeSetterCallback' %
(v8_class_or_partial, attribute.name)
if attribute.has_setter else 'nullptr' %}
{% endif %}
{% set wrapper_type_info =
'const_cast<WrapperTypeInfo*>(&V8%s::wrapperTypeInfo)' % attribute.constructor_type
if attribute.constructor_type else 'nullptr' %}
{% set property_attribute = 'static_cast<v8::PropertyAttribute>(%s)' %
' | '.join(attribute.property_attributes) %}
{% set cached_property_key =
'%s::%sCachedPropertyKey' % (v8_class_or_partial, attribute.name)
if attribute.is_cached_accessor else
'nullptr' %}
{% set holder_check = 'V8DOMConfiguration::kDoNotCheckHolder'
if attribute.is_lenient_this else 'V8DOMConfiguration::kCheckHolder' %}
{% if attribute.is_per_world_bindings %}
{% set getter_callback_for_main_world = '%sForMainWorld' % getter_callback %}
{% set setter_callback_for_main_world =
'%sForMainWorld' % setter_callback
if attribute.has_setter else 'nullptr' %}
{% endif %}
{% set config_pre = {
"main" : [
'"%s"' % attribute.name,
getter_callback_for_main_world,
setter_callback_for_main_world,
],
"non_main" : [
'"%s"' % attribute.name,
getter_callback,
setter_callback,
],
} %}
{% set accessor_only_fields = [] if config_type == 'attribute' else [
cached_property_key,
] %}
{% set config_post = [
wrapper_type_info,
property_attribute,
property_location(attribute),
holder_check,
] %}
{% if attribute.is_per_world_bindings %}
{% set main_config_list = config_pre["main"] + accessor_only_fields +
config_post + ['V8DOMConfiguration::kMainWorld'] %}
{% set non_main_config_list = config_pre["non_main"] + accessor_only_fields +
config_post + ['V8DOMConfiguration::kNonMainWorlds'] %}
{# Emit for main world then non-main.#}
{ {{main_config_list | join(', ')}} },
{ {{non_main_config_list | join(', ')}} }
{% else %}
{% set all_worlds_config_list = config_pre["non_main"] + accessor_only_fields +
config_post + ['V8DOMConfiguration::kAllWorlds'] %}
{# Emit only for all worlds #}
{ {{all_worlds_config_list | join(', ')}} }
{% endif %}
{% endmacro %}
{##############################################################################}
{% macro attribute_configuration(attribute) %}
{{build_attribute_or_accessor_configuration(attribute, 'attribute')}}
{% endmacro %}
{##############################################################################}
{% macro accessor_configuration(attribute) %}
{{build_attribute_or_accessor_configuration(attribute, 'accessor')}}
{% endmacro %}
{##############################################################################}
{% macro install_conditionally_enabled_attributes_on_prototype() %}
{% for attribute in conditionally_enabled_attributes if attribute.on_prototype %}
{% filter exposed(attribute.exposed_test) %}
{% filter secure_context(attribute.secure_context_test) %}
{% filter runtime_enabled(attribute.runtime_enabled_feature_name) %}
static const V8DOMConfiguration::AccessorConfiguration accessorConfiguration[] = {
{{accessor_configuration(attribute)}}
};
for (const auto& accessorConfig : accessorConfiguration)
V8DOMConfiguration::InstallAccessor(isolate, world, v8::Local<v8::Object>(), prototypeObject, interfaceObject, signature, accessorConfig);
{% endfilter %}{# runtime_enabled #}
{% endfilter %}{# secure_context #}
{% endfilter %}{# exposed #}
{% endfor %}
{% endmacro %}