blob: 8fbe90e096afe1fd0abce2322e46280cafe45819 [file] [log] [blame]
{% filter format_blink_cpp_source_code %}
{% include 'copyright_block.txt' %}
#include "{{this_include_header_path}}"
{% for filename in cpp_includes %}
#include "{{filename}}"
{% endfor %}
namespace blink {
{% if is_legacy_callback_interface %}
// Support of "legacy callback interface"
// Suppress warning: global constructors, because struct WrapperTypeInfo is
// trivial and does not depend on another global objects.
#if defined(COMPONENT_BUILD) && defined(WIN32) && defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wglobal-constructors"
#endif
const WrapperTypeInfo {{v8_class}}::wrapperTypeInfo = {
gin::kEmbedderBlink,
{{v8_class}}::DomTemplate,
nullptr,
"{{interface_name}}",
nullptr,
WrapperTypeInfo::kWrapperTypeNoPrototype,
WrapperTypeInfo::kObjectClassId,
WrapperTypeInfo::kNotInheritFromActiveScriptWrappable,
};
#if defined(COMPONENT_BUILD) && defined(WIN32) && defined(__clang__)
#pragma clang diagnostic pop
#endif
{% from 'constants.cpp.tmpl' import install_constants with context %}
static void Install{{v8_class}}Template(v8::Isolate* isolate, const DOMWrapperWorld& world, v8::Local<v8::FunctionTemplate> interface_template) {
// Legacy callback interface must not have a prototype object.
interface_template->RemovePrototype();
// Initialize the interface object's template.
V8DOMConfiguration::InitializeDOMInterfaceTemplate(
isolate, interface_template,
{{v8_class}}::wrapperTypeInfo.interface_name,
v8::Local<v8::FunctionTemplate>(),
kV8DefaultWrapperInternalFieldCount);
interface_template->SetLength(0);
// Register IDL constants.
{# |install_constants| requires |interfaceTemplate| and |prototypeTemplate|. #}
v8::Local<v8::FunctionTemplate> interfaceTemplate = interface_template;
v8::Local<v8::ObjectTemplate> prototypeTemplate =
interface_template->PrototypeTemplate();
{{install_constants() | trim | indent(2)}}
}
v8::Local<v8::FunctionTemplate> {{v8_class}}::DomTemplate(v8::Isolate* isolate, const DOMWrapperWorld& world) {
return V8DOMConfiguration::DomClassTemplate(
isolate,
world,
const_cast<WrapperTypeInfo*>(&wrapperTypeInfo),
Install{{v8_class}}Template);
}
{% endif %}{# is_legacy_callback_interface #}
{% for method in methods %}
v8::Maybe<{{method.cpp_type}}> {{v8_class}}::{{method.name}}({{method.argument_declarations | join(', ')}}) {
// This function implements "call a user object's operation".
// https://heycam.github.io/webidl/#call-a-user-objects-operation
if (!IsCallbackFunctionRunnable(CallbackRelevantScriptState())) {
// Wrapper-tracing for the callback function makes the function object and
// its creation context alive. Thus it's safe to use the creation context
// of the callback function here.
v8::HandleScope handle_scope(GetIsolate());
CHECK(!CallbackObject().IsEmpty());
v8::Context::Scope context_scope(CallbackObject()->CreationContext());
V8ThrowException::ThrowError(
GetIsolate(),
ExceptionMessages::FailedToExecute(
"{{method.name}}",
"{{cpp_class}}",
"The provided callback is no longer runnable."));
return v8::Nothing<{{method.cpp_type}}>();
}
// step 7. Prepare to run script with relevant settings.
ScriptState::Scope callback_relevant_context_scope(
CallbackRelevantScriptState());
// step 8. Prepare to run a callback with stored settings.
{# TODO(yukishiino): Callback interface type value must make the incumbent
environment alive, i.e. the reference to v8::Context must be strong. #}
if (IncumbentScriptState()->GetContext().IsEmpty()) {
V8ThrowException::ThrowError(
GetIsolate(),
ExceptionMessages::FailedToExecute(
"{{method.name}}",
"{{cpp_class}}",
"The provided callback is no longer runnable."));
return v8::Nothing<{{method.cpp_type}}>();
}
v8::Context::BackupIncumbentScope backup_incumbent_scope(
IncumbentScriptState()->GetContext());
v8::Local<v8::Function> function;
if (IsCallbackObjectCallable()) {
// step 9.1. If value's interface is a single operation callback interface
// and !IsCallable(O) is true, then set X to O.
function = CallbackObject().As<v8::Function>();
} else {
// step 9.2.1. Let getResult be Get(O, opName).
// step 9.2.2. If getResult is an abrupt completion, set completion to
// getResult and jump to the step labeled return.
v8::Local<v8::Value> value;
if (!CallbackObject()->Get(CallbackRelevantScriptState()->GetContext(),
V8String(GetIsolate(), "{{method.name}}"))
.ToLocal(&value)) {
return v8::Nothing<{{method.cpp_type}}>();
}
// step 10. If !IsCallable(X) is false, then set completion to a new
// Completion{[[Type]]: throw, [[Value]]: a newly created TypeError
// object, [[Target]]: empty}, and jump to the step labeled return.
if (!value->IsFunction()) {
V8ThrowException::ThrowTypeError(
GetIsolate(),
ExceptionMessages::FailedToExecute(
"{{method.name}}",
"{{cpp_class}}",
"The provided callback is not callable."));
return v8::Nothing<{{method.cpp_type}}>();
}
function = value.As<v8::Function>();
}
v8::Local<v8::Value> this_arg;
if (!IsCallbackObjectCallable()) {
// step 11. If value's interface is not a single operation callback
// interface, or if !IsCallable(O) is false, set thisArg to O (overriding
// the provided value).
this_arg = CallbackObject();
} else if (!callback_this_value) {
// step 2. If thisArg was not given, let thisArg be undefined.
this_arg = v8::Undefined(GetIsolate());
} else {
this_arg = ToV8(callback_this_value, CallbackRelevantScriptState());
}
{% for argument in arguments if argument.enum_values %}
// Enum values provided by Blink must be valid, otherwise typo.
#if DCHECK_IS_ON()
{
{% set valid_enum_variables = 'valid_' + argument.name + '_values' %}
{{declare_enum_validation_variable(argument.enum_values, valid_enum_variables) | trim | indent(4)}}
ExceptionState exception_state(GetIsolate(),
ExceptionState::kExecutionContext,
"{{cpp_class}}",
"{{method.name}}");
if (!IsValidEnum({{argument.name}}, {{valid_enum_variables}}, base::size({{valid_enum_variables}}), "{{argument.enum_type}}", exception_state)) { //
NOTREACHED();
return v8::Nothing<{{method.cpp_type}}>();
}
}
#endif
{% endfor %}
// step 12. Let esArgs be the result of converting args to an ECMAScript
// arguments list. If this throws an exception, set completion to the
// completion value representing the thrown exception and jump to the step
// labeled return.
{% if method.arguments %}
v8::Local<v8::Object> argument_creation_context =
CallbackRelevantScriptState()->GetContext()->Global();
ALLOW_UNUSED_LOCAL(argument_creation_context);
{% for argument in method.arguments %}
v8::Local<v8::Value> {{argument.handle}} = {{argument.cpp_value_to_v8_value}};
{% endfor %}
v8::Local<v8::Value> argv[] = { {{method.arguments | join(', ', 'handle')}} };
{% else %}
{# Zero-length arrays are ill-formed in C++. #}
v8::Local<v8::Value> *argv = nullptr;
{% endif %}
// step 13. Let callResult be Call(X, thisArg, esArgs).
v8::Local<v8::Value> call_result;
if (!V8ScriptRunner::CallFunction(
function,
ExecutionContext::From(CallbackRelevantScriptState()),
this_arg,
{{method.arguments | length}},
argv,
GetIsolate()).ToLocal(&call_result)) {
// step 14. If callResult is an abrupt completion, set completion to
// callResult and jump to the step labeled return.
return v8::Nothing<{{method.cpp_type}}>();
}
// step 15. Set completion to the result of converting callResult.[[Value]] to
// an IDL value of the same type as the operation's return type.
{% if method.idl_type == 'void' %}
return v8::JustVoid();
{% else %}
{
ExceptionState exception_state(GetIsolate(),
ExceptionState::kExecutionContext,
"{{cpp_class}}",
"{{method.name}}");
auto native_result =
NativeValueTraits<{{method.native_value_traits_tag}}>::NativeValue(
GetIsolate(), call_result, exception_state);
if (exception_state.HadException())
return v8::Nothing<{{method.cpp_type}}>();
else
return v8::Just<{{method.cpp_type}}>(native_result);
}
{% endif %}
}
{% endfor %}
{% if methods|length == 1 and methods[0].idl_type == 'void' %}
void {{v8_class}}::InvokeAndReportException({{methods[0].argument_declarations | join(', ')}}) {
v8::TryCatch try_catch(GetIsolate());
try_catch.SetVerbose(true);
v8::Maybe<void> maybe_result =
{{methods[0].name}}({{
(['callback_this_value'] +
(methods[0].arguments|map(attribute='name')|list)
)|join(', ')
}});
// An exception if any is killed with the v8::TryCatch above.
ALLOW_UNUSED_LOCAL(maybe_result);
}
{% endif %}
{% for method in methods %}
{{exported|replace('_EXPORT', '_EXTERN_TEMPLATE_EXPORT')|trim}}
v8::Maybe<{{method.cpp_type}}> V8PersistentCallbackInterface<{{v8_class}}>::{{method.name}}({{method.argument_declarations | join(', ')}}) {
return Proxy()->{{method.name}}(
{{
(['callback_this_value'] +
(method.arguments|map(attribute='name')|list)
)|join(', ')
}});
}
{% endfor %}
{% if methods|length == 1 and methods[0].idl_type == 'void' %}
{{exported|replace('_EXPORT', '_EXTERN_TEMPLATE_EXPORT')|trim}}
void V8PersistentCallbackInterface<{{v8_class}}>::InvokeAndReportException({{methods[0].argument_declarations | join(', ')}}) {
Proxy()->InvokeAndReportException(
{{
(['callback_this_value'] +
(methods[0].arguments|map(attribute='name')|list)
)|join(', ')
}});
}
{% endif %}
} // namespace blink
{% endfilter %}{# format_blink_cpp_source_code #}