| {% 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 #} |