blob: 76bddeb95a729560211f8728fc098dc0cde08875 [file] [log] [blame]
{% filter format_blink_cpp_source_code %}
{% include 'copyright_block.txt' %}
#include "{{this_include_header_name}}"
{% for filename in cpp_includes %}
#include "{{filename}}"
{% endfor %}
namespace blink {
{% 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}}>();
}
}
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) | indent(4)}}
ExceptionState exception_state(GetIsolate(),
ExceptionState::kExecutionContext,
"{{cpp_class}}",
"{{method.name}}");
if (!IsValidEnum({{argument.name}}, {{valid_enum_variables}}, WTF_ARRAY_LENGTH({{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.
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 %}
{% if method.arguments %}
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}}");
{% set idl_return_type = 'IDLBoolean' if method.cpp_type == 'bool' %}
auto native_result =
NativeValueTraits<{{idl_return_type}}>::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 #}