blob: b8eaa2f5a676524194c63cc6ba4ae6eadbd61ead [file] [log] [blame]
{% from 'utilities.cpp.tmpl' import declare_enum_validation_variable, v8_value_to_local_cpp_value %}
{% 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 {
v8::Maybe<{{return_cpp_type}}> {{cpp_class}}::Invoke({{argument_declarations | join(', ')}}) {
// This function implements "invoke" algorithm defined in
// "3.10. Invoking callback functions".
// https://heycam.github.io/webidl/#es-invoking-callback-functions
{# TODO(yukishiino): This implementation does not support a return type of
promise type, in which case this function needs to convert any exception
into a rejected promise. See also step 14.4. to 14.6. #}
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(!CallbackFunction().IsEmpty());
v8::Context::Scope context_scope(CallbackFunction()->CreationContext());
V8ThrowException::ThrowError(
GetIsolate(),
ExceptionMessages::FailedToExecute(
"invoke",
"{{callback_function_name}}",
"The provided callback is no longer runnable."));
return v8::Nothing<{{return_cpp_type}}>();
}
// step 4. If ! IsCallable(F) is false:
//
// As Blink no longer supports [TreatNonObjectAsNull], there must be no such a
// case.
#if DCHECK_IS_ON()
{
v8::HandleScope handle_scope(GetIsolate());
DCHECK(CallbackFunction()->IsFunction());
}
#endif
// step 8. Prepare to run script with relevant settings.
ScriptState::Scope callback_relevant_context_scope(
CallbackRelevantScriptState());
// step 9. Prepare to run a callback with stored settings.
{# TODO(yukishiino): Callback function 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(
"invoke",
"{{callback_function_name}}",
"The provided callback is no longer runnable."));
return v8::Nothing<{{return_cpp_type}}>();
}
v8::Context::BackupIncumbentScope backup_incumbent_scope(
IncumbentScriptState()->GetContext());
v8::Local<v8::Value> 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,
"{{callback_function_name}}",
"invoke");
if (!IsValidEnum({{argument.name}}, {{valid_enum_variables}}, base::size({{valid_enum_variables}}), "{{argument.enum_type}}", exception_state)) {
NOTREACHED();
return v8::Nothing<{{return_cpp_type}}>();
}
}
#endif
{% endfor %}
// step 10. 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 arguments %}
v8::Local<v8::Object> argument_creation_context =
CallbackRelevantScriptState()->GetContext()->Global();
ALLOW_UNUSED_LOCAL(argument_creation_context);
{% for argument in arguments %}
v8::Local<v8::Value> {{argument.v8_name}} = {{argument.cpp_value_to_v8_value}};
{% endfor %}
v8::Local<v8::Value> argv[] = { {{arguments | join(', ', 'v8_name')}} };
{% else %}
{# Zero-length arrays are ill-formed in C++. #}
v8::Local<v8::Value> *argv = nullptr;
{% endif %}
// step 11. Let callResult be Call(X, thisArg, esArgs).
v8::Local<v8::Value> call_result;
if (!V8ScriptRunner::CallFunction(
CallbackFunction(),
ExecutionContext::From(CallbackRelevantScriptState()),
this_arg,
{{arguments | length}},
argv,
GetIsolate()).ToLocal(&call_result)) {
// step 12. If callResult is an abrupt completion, set completion to
// callResult and jump to the step labeled return.
return v8::Nothing<{{return_cpp_type}}>();
}
// step 13. Set completion to the result of converting callResult.[[Value]] to
// an IDL value of the same type as the operation's return type.
{% if idl_type == 'void' %}
return v8::JustVoid();
{% else %}
{
ExceptionState exceptionState(GetIsolate(),
ExceptionState::kExecutionContext,
"{{callback_function_name}}",
"invoke");
{{v8_value_to_local_cpp_value(return_value_conversion) | trim | indent(4)}}
return v8::Just<{{return_cpp_type}}>(native_result);
}
{% endif %}
}
{% if idl_type == 'void' %}
void {{cpp_class}}::InvokeAndReportException({{argument_declarations | join(', ')}}) {
v8::TryCatch try_catch(GetIsolate());
try_catch.SetVerbose(true);
v8::Maybe<void> maybe_result =
Invoke({{
(['callback_this_value'] +
(arguments|map(attribute='name')|list)
)|join(', ')
}});
// An exception if any is killed with the v8::TryCatch above.
ALLOW_UNUSED_LOCAL(maybe_result);
}
{% endif %}
{{exported|replace('_EXPORT', '_TEMPLATE_EXPORT')|trim}}
v8::Maybe<{{return_cpp_type}}> V8PersistentCallbackFunction<{{cpp_class}}>::Invoke({{argument_declarations | join(', ')}}) {
return Proxy()->Invoke(
{{
(['callback_this_value'] +
(arguments|map(attribute='name')|list)
)|join(', ')
}});
}
{% if idl_type == 'void' %}
{{exported|replace('_EXPORT', '_TEMPLATE_EXPORT')|trim}}
void V8PersistentCallbackFunction<{{cpp_class}}>::InvokeAndReportException({{argument_declarations | join(', ')}}) {
Proxy()->InvokeAndReportException(
{{
(['callback_this_value'] +
(arguments|map(attribute='name')|list)
)|join(', ')
}});
}
{% endif %}
} // namespace blink
{% endfilter %}{# format_blink_cpp_source_code #}