| {% 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_name}}.h" |
| |
| {% 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" steps 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) | indent(4)}} |
| ExceptionState exception_state(GetIsolate(), |
| ExceptionState::kExecutionContext, |
| "{{callback_function_name}}", |
| "invoke"); |
| if (!IsValidEnum({{argument.name}}, {{valid_enum_variables}}, WTF_ARRAY_LENGTH({{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. |
| 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 %} |
| {% if arguments %} |
| 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) | 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 %} |
| |
| } // namespace blink |
| |
| {% endfilter %}{# format_blink_cpp_source_code #} |