| {# |
| # Copyright 2017 Google Inc. All Rights Reserved. |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| #} |
| |
| {% from 'macros.cc.template' import add_extra_arguments %} |
| {% from 'macros.cc.template' import call_cobalt_function %} |
| {% from 'macros.cc.template' import check_if_object_implements_interface with context %} |
| {% from 'macros.cc.template' import constructor_implementation with context %} |
| {% from 'macros.cc.template' import function_implementation with context %} |
| {% from 'macros.cc.template' import get_impl_class_instance %} |
| {% from 'macros.cc.template' import nonstatic_function_prologue %} |
| {% from 'macros.cc.template' import overload_resolution_implementation with context %} |
| {% from 'macros.cc.template' import set_attribute_implementation with context %} |
| {% from 'macros.cc.template' import static_function_prologue %} |
| |
| {% extends "interface-base.cc.template" %} |
| |
| {% block includes %} |
| {{ super() }} |
| #include "{{generated_conversion_include}}" |
| |
| #include "cobalt/script/callback_interface_traits.h" |
| #include "cobalt/script/v8c/v8c_array_buffer.h" |
| #include "cobalt/script/v8c/v8c_array_buffer_view.h" |
| #include "cobalt/script/v8c/callback_function_conversion.h" |
| #include "cobalt/script/v8c/conversion_helpers.h" |
| #include "cobalt/script/v8c/entry_scope.h" |
| #include "cobalt/script/v8c/helpers.h" |
| #include "cobalt/script/v8c/native_promise.h" |
| #include "cobalt/script/v8c/type_traits.h" |
| #include "cobalt/script/v8c/v8c_typed_arrays.h" |
| #include "cobalt/script/v8c/v8c_data_view.h" |
| #include "cobalt/script/v8c/v8c_callback_function.h" |
| #include "cobalt/script/v8c/v8c_callback_interface_holder.h" |
| #include "cobalt/script/v8c/v8c_engine.h" |
| #include "cobalt/script/v8c/v8c_exception_state.h" |
| #include "cobalt/script/v8c/v8c_global_environment.h" |
| #include "cobalt/script/v8c/v8c_property_enumerator.h" |
| #include "cobalt/script/v8c/v8c_value_handle.h" |
| #include "cobalt/script/v8c/wrapper_private.h" |
| #include "v8/include/v8.h" |
| |
| {% endblock includes %} |
| |
| {% block using_directives %} |
| {{ super() }} |
| using cobalt::script::v8c::EntryScope; |
| using cobalt::script::v8c::EscapableEntryScope; |
| using cobalt::script::v8c::FromJSValue; |
| using cobalt::script::v8c::kConversionFlagClamped; |
| using cobalt::script::v8c::kConversionFlagNullable; |
| using cobalt::script::v8c::kConversionFlagObjectOnly; |
| using cobalt::script::v8c::kConversionFlagRestricted; |
| using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString; |
| using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString; |
| using cobalt::script::v8c::kNoConversionFlags; |
| using cobalt::script::v8c::NewInternalString; |
| using cobalt::script::v8c::ToJSValue; |
| using cobalt::script::v8c::TypeTraits; |
| using cobalt::script::v8c::V8cExceptionState; |
| using cobalt::script::v8c::V8cGlobalEnvironment; |
| using cobalt::script::v8c::V8cPropertyEnumerator; |
| using cobalt::script::v8c::WrapperFactory; |
| using cobalt::script::v8c::WrapperPrivate; |
| {% endblock using_directives %} |
| |
| {% block enumeration_declarations %} |
| {% if enumerations|length %} |
| // Declare and define these in the same namespace that the other overloads |
| // were brought into with the using declaration. |
| {% for enumeration in enumerations %} |
| void ToJSValue( |
| v8::Isolate* isolate, |
| {{impl_class}}::{{enumeration.name}} in_enum, |
| v8::Local<v8::Value>* out_value); |
| void FromJSValue(v8::Isolate* context, v8::Local<v8::Value> value, |
| int conversion_flags, ExceptionState* exception_state, |
| {{impl_class}}::{{enumeration.name}}* out_enum); |
| {% endfor %} |
| {% endif %} |
| {% endblock enumeration_declarations %} |
| |
| {% block top_level_unnamed_namespace %} |
| {% endblock top_level_unnamed_namespace %} |
| |
| {% block implementation %} |
| |
| namespace { |
| |
| const int kInterfaceUniqueId = {{unique_id}}; |
| |
| {% if named_property_getter %} |
| |
| void NamedPropertyGetterCallback( |
| v8::Local<v8::Name> property, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| v8::Isolate* isolate = info.GetIsolate(); |
| v8::Local<v8::Object> object = info.Holder(); |
| {{ nonstatic_function_prologue(impl_class) }} |
| std::string property_name = *v8::String::Utf8Value(isolate, property); |
| if (!impl->CanQueryNamedProperty(property_name)) { |
| return; |
| } |
| {{ call_cobalt_function(impl_class, named_property_getter.type, |
| named_property_getter.name, ["property_name"], |
| named_property_getter.raises_exception, |
| named_property_getter.call_with) }} |
| if (exception_state.is_exception_set()) { |
| return; |
| } |
| info.GetReturnValue().Set(result_value); |
| } |
| |
| {% if not indexed_property_getter %} |
| void IndexedPropertyGetterCallback( |
| uint32_t index, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| v8::Local<v8::String> as_string = v8::Integer::New(info.GetIsolate(), index)->ToString(); |
| NamedPropertyGetterCallback(as_string, info); |
| } |
| {% endif %} |
| |
| void NamedPropertyQueryCallback( |
| v8::Local<v8::Name> property, |
| const v8::PropertyCallbackInfo<v8::Integer>& info) { |
| v8::Isolate* isolate = info.GetIsolate(); |
| v8::Local<v8::Object> object = info.Holder(); |
| {{ get_impl_class_instance(impl_class) }} |
| std::string property_name = *v8::String::Utf8Value(isolate, property); |
| bool result = impl->CanQueryNamedProperty(property_name); |
| if (!result) { |
| return; |
| } |
| |
| // https://heycam.github.io/webidl/#LegacyPlatformObjectGetOwnProperty |
| int properties = v8::None; |
| // 2.7. If |O| implements an interface with a named property setter, then set |
| // desc.[[Writable]] to true, otherwise set it to false. |
| {% if named_property_setter %} |
| {% else %} |
| properties |= v8::ReadOnly; |
| {% endif %} |
| // 2.8. If |O| implements an interface with the |
| // [LegacyUnenumerableNamedProperties] extended attribute, then set |
| // desc.[[Enumerable]] to false, otherwise set it to true. |
| {% if has_legacy_unenumerable_named_properties %} |
| // TODO: Note that this is never true at the moment, as Cobalt's IDLs and |
| // IDL compiler do not support this. |
| properties |= v8::DontEnum; |
| {% endif %} |
| |
| info.GetReturnValue().Set(properties); |
| } |
| |
| {% if not indexed_property_getter %} |
| void IndexedPropertyDescriptorCallback( |
| uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) { |
| // TODO: Figure out under what conditions this gets called. It's not |
| // getting called in our tests. |
| NOTIMPLEMENTED(); |
| } |
| {% endif %} |
| |
| void NamedPropertyEnumeratorCallback( |
| const v8::PropertyCallbackInfo<v8::Array>& info) { |
| v8::Isolate* isolate = info.GetIsolate(); |
| v8::Local<v8::Object> object = info.Holder(); |
| {{ get_impl_class_instance(impl_class) }} |
| v8::Local<v8::Array> array = v8::Array::New(isolate); |
| V8cPropertyEnumerator property_enumerator(isolate, &array); |
| impl->EnumerateNamedProperties(&property_enumerator); |
| info.GetReturnValue().Set(array); |
| } |
| |
| {% endif %} |
| |
| {% if named_property_setter %} |
| void NamedPropertySetterCallback( |
| v8::Local<v8::Name> property, |
| v8::Local<v8::Value> value, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| v8::Isolate* isolate = info.GetIsolate(); |
| v8::Local<v8::Object> object = info.Holder(); |
| {{ nonstatic_function_prologue(impl_class) }} |
| std::string property_name = *v8::String::Utf8Value(isolate, property); |
| TypeTraits<{{named_property_setter.type}}>::ConversionType native_value; |
| FromJSValue(isolate, value, {{named_property_setter.conversion_flags}}, |
| &exception_state, &native_value); |
| if (exception_state.is_exception_set()) { |
| return; |
| } |
| {{ call_cobalt_function(impl_class, "void", |
| named_property_setter.name, ["property_name", "native_value"], |
| named_property_setter.raises_exception, |
| named_property_setter.call_with) }} |
| if (exception_state.is_exception_set()) { |
| return; |
| } |
| info.GetReturnValue().Set(value); |
| } |
| |
| {% if not indexed_property_setter %} |
| void IndexedPropertySetterCallback( |
| uint32_t index, |
| v8::Local<v8::Value> value, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| v8::Local<v8::String> as_string = v8::Integer::New(info.GetIsolate(), index)->ToString(); |
| NamedPropertySetterCallback(as_string, value, info); |
| } |
| {% endif %} |
| |
| {% endif %} |
| |
| {% if named_property_deleter %} |
| void NamedPropertyDeleterCallback( |
| v8::Local<v8::Name> property, |
| const v8::PropertyCallbackInfo<v8::Boolean>& info) { |
| v8::Isolate* isolate = info.GetIsolate(); |
| v8::Local<v8::Object> object = info.Holder(); |
| {{ nonstatic_function_prologue(impl_class) }} |
| std::string property_name = *v8::String::Utf8Value(isolate, property); |
| if (!impl->CanQueryNamedProperty(property_name)) { |
| return; |
| } |
| {{ call_cobalt_function(impl_class, "void", |
| named_property_deleter.name, ["property_name"], |
| named_property_deleter.raises_exception, |
| named_property_deleter.call_with) }} |
| if (exception_state.is_exception_set()) { |
| return; |
| } |
| info.GetReturnValue().Set(true); |
| } |
| |
| {% if not indexed_property_deleter %} |
| void IndexedPropertyDeleterCallback( |
| uint32_t index, |
| const v8::PropertyCallbackInfo<v8::Boolean>& info) { |
| v8::Isolate* isolate = info.GetIsolate(); |
| v8::Local<v8::String> as_string = v8::Integer::New(info.GetIsolate(), index)->ToString(); |
| NamedPropertyDeleterCallback(as_string, info); |
| } |
| {% endif %} |
| |
| {% endif %} |
| |
| {% if indexed_property_getter %} |
| |
| void IndexedPropertyGetterCallback( |
| uint32_t index, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| v8::Isolate* isolate = info.GetIsolate(); |
| v8::Local<v8::Object> object = info.Holder(); |
| {{ nonstatic_function_prologue(impl_class) }} |
| if (index >= impl->length()) { |
| // |index| is out of bounds, so return undefined. |
| return; |
| } |
| {{ call_cobalt_function(impl_class, indexed_property_getter.type, |
| indexed_property_getter.name, ["index"], |
| indexed_property_getter.raises_exception, |
| indexed_property_getter.call_with) }} |
| info.GetReturnValue().Set(result_value); |
| } |
| |
| void IndexedPropertyDescriptorCallback( |
| uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) { |
| // TODO: Figure out under what conditions this gets called. It's not |
| // getting called in our tests. |
| NOTIMPLEMENTED(); |
| } |
| |
| void IndexedPropertyEnumeratorCallback( |
| const v8::PropertyCallbackInfo<v8::Array>& info) { |
| v8::Isolate* isolate = info.GetIsolate(); |
| v8::Local<v8::Object> object = info.Holder(); |
| {{ get_impl_class_instance(impl_class) }} |
| const uint32_t length = impl->length(); |
| v8::Local<v8::Array> array = v8::Array::New(isolate, length); |
| for (uint32_t i = 0; i < length; ++i) { |
| array->Set(i, v8::Integer::New(isolate, i)); |
| } |
| info.GetReturnValue().Set(array); |
| } |
| |
| void IndexedPropertyDefinerCallback( |
| uint32_t index, const v8::PropertyDescriptor& desc, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| // TODO: Figure out under what conditions this gets called. It's not |
| // getting called in our tests. |
| NOTIMPLEMENTED(); |
| } |
| |
| {% endif %} |
| |
| {% if indexed_property_setter %} |
| void IndexedPropertySetterCallback( |
| uint32_t index, |
| v8::Local<v8::Value> value, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| v8::Isolate* isolate = info.GetIsolate(); |
| v8::Local<v8::Object> object = info.Holder(); |
| {{ nonstatic_function_prologue(impl_class) }} |
| if (index >= impl->length()) { |
| return; |
| } |
| TypeTraits<{{indexed_property_setter.type}}>::ConversionType native_value; |
| FromJSValue(isolate, value, {{indexed_property_setter.conversion_flags}}, |
| &exception_state, &native_value); |
| if (exception_state.is_exception_set()) { |
| return; |
| } |
| {{ call_cobalt_function(impl_class, "void", |
| indexed_property_setter.name, ["index", "native_value"], |
| indexed_property_setter.raises_exception, |
| indexed_property_setter.call_with) }} |
| if (exception_state.is_exception_set()) { |
| return; |
| } |
| info.GetReturnValue().Set(value); |
| } |
| {% endif %} |
| |
| {% if indexed_property_deleter %} |
| void IndexedPropertyDeleterCallback( |
| uint32_t index, |
| const v8::PropertyCallbackInfo<v8::Boolean>& info) { |
| v8::Isolate* isolate = info.GetIsolate(); |
| v8::Local<v8::Object> object = info.Holder(); |
| {{ nonstatic_function_prologue(impl_class) }} |
| if (index >= impl->length()) { |
| return; |
| } |
| {{ call_cobalt_function(impl_class, "void", |
| indexed_property_deleter.name, ["index"], |
| indexed_property_deleter.raises_exception, |
| indexed_property_deleter.call_with) }} |
| if (exception_state.is_exception_set()) { |
| return; |
| } |
| info.GetReturnValue().Set(v8::Boolean::New(isolate, true)); |
| } |
| {% endif %} |
| |
| {% if constructor %} |
| |
| {% for overload in constructor.overloads if constructor.overloads|length > 1 %} |
| void Constructor{{overload.overload_index}}( |
| const v8::FunctionCallbackInfo<v8::Value>& info) { |
| v8::Isolate* isolate = info.GetIsolate(); |
| {{ constructor_implementation(overload) -}} |
| } |
| {% endfor %} |
| |
| void Constructor(const v8::FunctionCallbackInfo<v8::Value>& info) { |
| {% if constructor.overloads|length == 1 %} |
| v8::Isolate* isolate = info.GetIsolate(); |
| {{ constructor_implementation(constructor.overloads[0]) -}} |
| {% else %} |
| {{ overload_resolution_implementation(constructor, "Constructor")}} |
| {% endif %} |
| } |
| |
| {% else %} |
| |
| void DummyConstructor(const v8::FunctionCallbackInfo<v8::Value>& info) { |
| V8cExceptionState exception(info.GetIsolate()); |
| exception.SetSimpleException( |
| script::kTypeError, "{{interface_name}} is not constructible."); |
| } |
| |
| {% endif %} |
| |
| {% for attribute in attributes + static_attributes %} |
| {% if attribute.conditional %} |
| #if defined({{attribute.conditional}}) |
| {% endif %} |
| |
| {% if attribute.is_constructor_attribute %} |
| // Nothing for {{attribute}}. We will just give them the v8::FunctionTemplate. |
| {% else %} |
| void {{attribute.idl_name}}AttributeGetter( |
| const v8::FunctionCallbackInfo<v8::Value>& info) { |
| v8::Isolate* isolate = info.GetIsolate(); |
| v8::Local<v8::Object> object = info.Holder(); |
| |
| {% if attribute.is_static %} |
| {{ static_function_prologue() -}} |
| {% endif %} |
| |
| {% if not attribute.is_static %} |
| {{ check_if_object_implements_interface() }} |
| {{ nonstatic_function_prologue(impl_class) }} |
| {% endif %} |
| |
| {{ call_cobalt_function(impl_class, attribute.type, |
| attribute.getter_function_name, [], |
| attribute.raises_exception, attribute.call_with, |
| attribute.is_static) }} |
| if (exception_state.is_exception_set()) { |
| return; |
| } |
| info.GetReturnValue().Set(result_value); |
| } |
| |
| {% if attribute.has_setter %} |
| void {{attribute.idl_name}}AttributeSetter( |
| const v8::FunctionCallbackInfo<v8::Value>& info) { |
| v8::Isolate* isolate = info.GetIsolate(); |
| v8::Local<v8::Object> object = info.Holder(); |
| v8::Local<v8::Value> v8_value = info[0]; |
| |
| {% if attribute.is_static %} |
| {{ static_function_prologue() }} |
| {% else %} |
| {{ check_if_object_implements_interface() }} |
| {{ nonstatic_function_prologue(impl_class)}} |
| {% endif %} {#- attribute.is_static #} |
| {{ set_attribute_implementation(attribute, impl_class) -}} |
| } |
| {% endif %} {#- attribute.has_setter #} |
| |
| {% endif %} |
| {% if attribute.conditional %} |
| #endif // {{attribute.conditional}} |
| {% endif %} {#- attribute.is_constructor_attribute #} |
| {% endfor %} {#- for attribute in attributes + static_attributes #} |
| |
| {%- for operation in operations + static_operations %} |
| {% if operation.conditional %} |
| #if defined({{operation.conditional}}) |
| {% endif %} |
| {% set boundFunctionSuffix = "StaticMethod" if operation.is_static else "Method" %} |
| |
| {% for overload in operation.overloads if operation.overloads|length > 1 %} |
| void {{operation.idl_name}}{{boundFunctionSuffix}}{{overload.overload_index}}( |
| const v8::FunctionCallbackInfo<v8::Value>& info) { |
| {{ function_implementation(overload) -}} |
| } |
| {% endfor %} |
| |
| void {{operation.idl_name}}{{boundFunctionSuffix}}(const v8::FunctionCallbackInfo<v8::Value>& info) { |
| {% if operation.overloads|length == 1 %} |
| {{ function_implementation(operation.overloads[0]) -}} |
| {% else %} |
| {{ overload_resolution_implementation(operation, operation.idl_name + boundFunctionSuffix) }} |
| {% endif %} |
| } |
| |
| {% if operation.conditional %} |
| #endif // {{operation.conditional}} |
| {% endif %} |
| {% endfor %} |
| |
| {% if stringifier %} |
| void Stringifier(const v8::FunctionCallbackInfo<v8::Value>& info) { |
| v8::Isolate* isolate = info.GetIsolate(); |
| v8::Local<v8::Object> object = info.Holder(); |
| V8cExceptionState exception_state(isolate); |
| |
| {{ check_if_object_implements_interface() }} |
| |
| WrapperPrivate* wrapper_private = |
| WrapperPrivate::GetFromWrapperObject(object); |
| |
| // |WrapperPrivate::GetFromObject| can fail if |object| is not a |Wrapper| |
| // object. |
| if (!wrapper_private) { |
| exception_state.SetSimpleException(cobalt::script::kStringifierProblem); |
| return; |
| } |
| |
| {{impl_class}}* impl = |
| wrapper_private->wrappable<{{impl_class}}>().get(); |
| if (!impl) { |
| exception_state.SetSimpleException(cobalt::script::kStringifierProblem); |
| NOTREACHED(); |
| return; |
| } |
| std::string stringified = impl->{{stringifier.name}}(); |
| |
| v8::Local<v8::Value> v8_stringified; |
| ToJSValue(isolate, stringified, &v8_stringified); |
| |
| info.GetReturnValue().Set(v8_stringified); |
| } |
| {% endif %} |
| |
| void InitializeTemplate(v8::Isolate* isolate) { |
| // https://heycam.github.io/webidl/#interface-object |
| // 3.6.1. Interface object |
| // |
| // The interface object for a given interface is a built-in function object. |
| // It has properties that correspond to the constants and static operations |
| // defined on that interface, as described in sections 3.6.6 Constants and |
| // 3.6.8 Operations. |
| // |
| // If the interface is declared with a [Constructor] extended attribute, |
| // then the interface object can be called as a constructor to create an |
| // object that implements that interface. Calling that interface as a |
| // function will throw an exception. |
| // |
| // Interface objects whose interfaces are not declared with a [Constructor] |
| // extended attribute will throw when called, both as a function and as a |
| // constructor. |
| // |
| // An interface object for a non-callback interface has an associated object |
| // called the interface prototype object. This object has properties that |
| // correspond to the regular attributes and regular operations defined on |
| // the interface, and is described in more detail in 3.6.3 Interface |
| // prototype object. |
| {% if constructor %} |
| v8::Local<v8::FunctionTemplate> function_template = |
| v8::FunctionTemplate::New( |
| isolate, |
| Constructor, |
| v8::Local<v8::Value>(), |
| v8::Local<v8::Signature>(), |
| {{constructor.length}}); |
| function_template->SetLength({{constructor.length}}); |
| {% else %} |
| v8::Local<v8::FunctionTemplate> function_template = |
| v8::FunctionTemplate::New( |
| isolate, |
| DummyConstructor, |
| v8::Local<v8::Value>(), |
| v8::Local<v8::Signature>(), |
| 0); |
| {% endif %} |
| function_template->SetClassName(NewInternalString(isolate, "{{interface_name}}")); |
| function_template->ReadOnlyPrototype(); |
| |
| v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate(); |
| v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate(); |
| instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount); |
| |
| V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); |
| global_environment->AddInterfaceData(kInterfaceUniqueId, function_template); |
| |
| {% if parent_interface %} |
| { |
| // An interface can be defined to inherit from another interface. If the |
| // identifier of the interface is followed by a U+003A COLON (":") character |
| // and an identifier, then that identifier identifies the inherited |
| // interface. An object that implements an interface that inherits from |
| // another also implements that inherited interface. The object therefore |
| // will also have members that correspond to the interface members from the |
| // inherited interface. |
| v8::Local<v8::FunctionTemplate> parent_template = {{parent_interface}}::GetTemplate(isolate); |
| function_template->Inherit(parent_template); |
| static_assert( |
| std::is_base_of<{{parent_interface_name}}, {{interface_name}}>::value, |
| "Expected {{interface_name}} to have C++ parent class " |
| "{{parent_interface_name}}, because that is its WebIDL parent."); |
| } |
| {% elif is_exception_interface %} |
| { |
| // A spicy hack from Chromium in order to achieve |
| // https://heycam.github.io/webidl/#es-DOMException-specialness |
| // See https://cs.chromium.org/chromium/src/third_party/WebKit/Source/bindings/templates/interface_base.cpp.tmpl?l=630&rcl=0f7c2c752bb24ad08c17017e4e68401093fe76a0 |
| v8::Local<v8::FunctionTemplate> intrinsic_error_prototype_interface_template = |
| v8::FunctionTemplate::New(isolate); |
| intrinsic_error_prototype_interface_template->RemovePrototype(); |
| intrinsic_error_prototype_interface_template->SetIntrinsicDataProperty( |
| NewInternalString(isolate, "prototype"), v8::kErrorPrototype); |
| function_template->Inherit(intrinsic_error_prototype_interface_template); |
| } |
| {% endif %} |
| |
| // https://heycam.github.io/webidl/#es-constants |
| // 3.6.6. Constants |
| // |
| // For each exposed constant defined on an interface A, there must be a |
| // corresponding property. The property has the following characteristics: |
| {% for constant in constants %} |
| { |
| // The name of the property is the identifier of the constant. |
| v8::Local<v8::String> name = NewInternalString( |
| isolate, |
| "{{constant.idl_name}}"); |
| |
| // The value of the property is that which is obtained by converting the |
| // constant's IDL value to an ECMAScript value. |
| v8::Local<v8::Value> constant_value; |
| ToJSValue(isolate, {{constant.value}}, &constant_value); |
| |
| // The property has attributes { [[Writable]]: false, [[Enumerable]]: true, |
| // [[Configurable]]: false }. |
| v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( |
| v8::ReadOnly | v8::DontDelete); |
| |
| // The location of the property is determined as follows: |
| {% if is_global_interface %} |
| // If the interface was declared with the [Global] extended attribute, then |
| // the property exists on the single object that implements the interface. |
| instance_template->Set(name, constant_value, attributes); |
| |
| {% else %} |
| // Otherwise, if the interface has an interface prototype object, then the |
| // property exists on it. |
| prototype_template->Set(name, constant_value, attributes); |
| {% endif %} |
| |
| // In addition, a property with the same characteristics must exist on the |
| // interface object or the legacy callback interface object, if either of |
| // those objects exists. |
| function_template->Set(name, constant_value, attributes); |
| } |
| {% endfor %} |
| |
| // https://heycam.github.io/webidl/#es-attributes |
| // 3.6.7. Attributes |
| // |
| // For each exposed attribute of the interface there must exist a |
| // corresponding property. The characteristics of this property are as |
| // follows: |
| {% for attribute in all_attributes_v8_order_quirk %} |
| {% if attribute.conditional %} |
| #if defined({{attribute.conditional}}) |
| {% endif %} |
| { |
| // The name of the property is the identifier of the attribute. |
| v8::Local<v8::String> name = NewInternalString( |
| isolate, |
| "{{attribute.idl_name}}"); |
| |
| // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]: |
| // true, [[Configurable]]: configurable }, where: configurable is false if |
| // the attribute was declared with the [Unforgeable] extended attribute and |
| // true otherwise; |
| bool configurable = {{ "false" if attribute.is_unforgeable else "true"}}; |
| v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( |
| configurable ? v8::None : v8::DontDelete); |
| |
| // G is the attribute getter created given the attribute, the interface, and |
| // the relevant Realm of the object that is the location of the property; |
| // and |
| // |
| // S is the attribute setter created given the attribute, the interface, and |
| // the relevant Realm of the object that is the location of the property. |
| {% if not attribute.is_constructor_attribute %} |
| v8::Local<v8::FunctionTemplate> getter = |
| v8::FunctionTemplate::New(isolate, {{attribute.idl_name}}AttributeGetter); |
| {% if attribute.has_setter %} |
| v8::Local<v8::FunctionTemplate> setter = |
| v8::FunctionTemplate::New(isolate, {{attribute.idl_name}}AttributeSetter); |
| {% else %} |
| v8::Local<v8::FunctionTemplate> setter; |
| {% endif %} |
| |
| // The location of the property is determined as follows: |
| {% if attribute.is_static %} |
| // Operations installed on the interface object must be static methods, so |
| // no need to specify a signature, i.e. no need to do type check against a |
| // holder. |
| |
| // If the attribute is a static attribute, then there is a single |
| // corresponding property and it exists on the interface's interface object. |
| function_template-> |
| {% elif attribute.is_unforgeable or is_global_interface %} |
| // Otherwise, if the attribute is unforgeable on the interface or if the |
| // interface was declared with the [Global] extended attribute, then the |
| // property exists on every object that implements the interface. |
| instance_template-> |
| {% else %} |
| // Otherwise, the property exists solely on the interface's interface |
| // prototype object. |
| prototype_template-> |
| {% endif %} |
| SetAccessorProperty( |
| name, |
| getter, |
| setter, |
| attributes); |
| |
| {% else %} {#- not attribute.is_constructor_attribute #} |
| { |
| v8::Local<v8::String> name = NewInternalString( |
| isolate, |
| "{{attribute.idl_name}}"); |
| instance_template->Set( |
| name, |
| {% if attribute.interface_name == interface_name %} |
| function_template |
| {% else %} |
| // Note that we use "attribute.interface_name", and not |
| // "attribute.idl_name", because of named constructors. |
| V8c{{attribute.interface_name}}::GetTemplate(isolate) |
| {% endif %} |
| ); |
| } |
| |
| {% endif %} {#- not attribute.is_constructor_attribute #} |
| } |
| {% if attribute.conditional %} |
| #endif // {{attribute.conditional}} |
| {% endif %} |
| {% endfor %} |
| |
| // https://heycam.github.io/webidl/#es-operations |
| // 3.6.8. Operations |
| // |
| // For each unique identifier of an exposed operation defined on the |
| // interface, there must exist a corresponding property, unless the effective |
| // overload set for that identifier and operation and with an argument count |
| // of 0 has no entries. |
| // |
| // The characteristics of this property are as follows: |
| {% for operation in operations + static_operations %} |
| {% if operation.conditional %} |
| #if defined({{operation.conditional}}) |
| {% endif %} |
| { |
| // The name of the property is the identifier. |
| v8::Local<v8::String> name = NewInternalString( |
| isolate, |
| "{{operation.idl_name}}"); |
| |
| // The property has attributes { [[Writable]]: B, [[Enumerable]]: true, |
| // [[Configurable]]: B }, where B is false if the operation is unforgeable |
| // on the interface, and true otherwise. |
| bool B = {{ "false" if operation.is_unforgeable else "true"}}; |
| v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( |
| B ? v8::None : (v8::ReadOnly | v8::DontDelete)); |
| |
| v8::Local<v8::FunctionTemplate> method_template = |
| v8::FunctionTemplate::New(isolate, {{operation.idl_name}}{{"Static" if operation.is_static else ""}}Method); |
| method_template->RemovePrototype(); |
| method_template->SetLength({{operation.length}}); |
| |
| // The location of the property is determined as follows: |
| {% if operation.is_static %} |
| // If the operation is static, then the property exists on the interface |
| // object. |
| function_template-> |
| {% elif operation.is_unforgeable or is_global_interface %} |
| // Otherwise, if the operation is unforgeable on the interface or if the |
| // interface was declared with the [Global] extended attribute, then the |
| // property exists on every object that implements the interface. |
| instance_template-> |
| {% else %} |
| // Otherwise, the property exists solely on the interface's interface |
| // prototype object. |
| prototype_template-> |
| {% endif %} |
| Set(name, method_template); |
| |
| // The value of the property is the result of creating an operation function |
| // given the operation, the interface, and the relevant Realm of the object |
| // that is the location of the property. |
| |
| // Note: that is, even if an includes statement was used to make an |
| // operation available on the interface, we pass in the interface which |
| // includes the interface mixin, and not the interface mixin on which the |
| // operation was originally declared. |
| } |
| {% if operation.conditional %} |
| #endif // {{operation.conditional}} |
| {% endif %} |
| {% endfor %} |
| |
| // https://heycam.github.io/webidl/#es-stringifier |
| // 3.6.8.2. Stringifiers |
| prototype_template->Set( |
| v8::Symbol::GetToStringTag(isolate), |
| NewInternalString(isolate, "{{interface_name}}"), |
| static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum)); |
| |
| {% if stringifier %} |
| { |
| v8::Local<v8::String> name = NewInternalString(isolate, "toString"); |
| v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, Stringifier); |
| prototype_template->Set( |
| NewInternalString(isolate, "toString"), |
| method_template); |
| } |
| {% endif %} |
| |
| {% if named_property_getter %} |
| { |
| v8::NamedPropertyHandlerConfiguration named_property_handler_configuration = { |
| NamedPropertyGetterCallback, |
| {{ "NamedPropertySetterCallback" if named_property_setter else "nullptr" }}, |
| NamedPropertyQueryCallback, |
| {{ "NamedPropertyDeleterCallback" if named_property_deleter else "nullptr" }}, |
| NamedPropertyEnumeratorCallback, |
| v8::Local<v8::Value>(), |
| static_cast<v8::PropertyHandlerFlags>(int(v8::PropertyHandlerFlags::kNonMasking) | int(v8::PropertyHandlerFlags::kOnlyInterceptStrings)) |
| }; |
| instance_template->SetHandler(named_property_handler_configuration); |
| } |
| {% endif %} |
| |
| {% if named_property_getter and not indexed_property_getter %} |
| { |
| v8::IndexedPropertyHandlerConfiguration indexed_property_handler_configuration = { |
| IndexedPropertyGetterCallback, |
| {{ "IndexedPropertySetterCallback" if named_property_setter else "nullptr" }}, |
| IndexedPropertyDescriptorCallback, |
| {{ "IndexedPropertyDeleterCallback" if named_property_deleter else "nullptr" }}, |
| nullptr, |
| nullptr |
| }; |
| instance_template->SetHandler(indexed_property_handler_configuration); |
| } |
| {% endif %} |
| |
| {% if indexed_property_getter %} |
| { |
| v8::IndexedPropertyHandlerConfiguration indexed_property_handler_configuration = { |
| IndexedPropertyGetterCallback, |
| {{ "IndexedPropertySetterCallback" if indexed_property_setter else "nullptr" }}, |
| IndexedPropertyDescriptorCallback, |
| {{ "IndexedPropertyDeleterCallback" if indexed_property_deleter else "nullptr" }}, |
| IndexedPropertyEnumeratorCallback, |
| IndexedPropertyDefinerCallback |
| }; |
| instance_template->SetHandler(indexed_property_handler_configuration); |
| } |
| {% endif %} |
| |
| } |
| |
| } // namespace |
| |
| {% if is_global_interface %} |
| |
| // The global interface is special. Just give them the global object proxy. |
| v8::Local<v8::Object> {{binding_class}}::CreateWrapper( |
| v8::Isolate* isolate, const scoped_refptr<Wrappable>&) { |
| return isolate->GetCurrentContext()->Global(); |
| } |
| |
| {% else %} |
| |
| v8::Local<v8::Object> {{binding_class}}::CreateWrapper( |
| v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) { |
| EscapableEntryScope entry_scope(isolate); |
| v8::Local<v8::Context> context = isolate->GetCurrentContext(); |
| |
| V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); |
| if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) { |
| InitializeTemplate(isolate); |
| } |
| v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId); |
| |
| DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount); |
| v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked(); |
| DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount); |
| |
| // This |WrapperPrivate|'s lifetime will be managed by V8. |
| new WrapperPrivate(isolate, wrappable, object); |
| return entry_scope.Escape(object); |
| } |
| |
| {% endif %} |
| |
| v8::Local<v8::FunctionTemplate> {{binding_class}}::GetTemplate(v8::Isolate* isolate) { |
| V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); |
| if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) { |
| InitializeTemplate(isolate); |
| } |
| return global_environment->GetInterfaceData(kInterfaceUniqueId); |
| } |
| |
| {% endblock implementation %} |
| |
| {% block create_global_object_impl %} |
| |
| namespace cobalt { |
| namespace script { |
| namespace v8c { |
| |
| template <typename GlobalInterface> |
| void V8cGlobalEnvironment::CreateGlobalObject( |
| const scoped_refptr<GlobalInterface>& global_interface, |
| EnvironmentSettings* environment_settings) { |
| TRACE_EVENT0("cobalt::script", "V8cGlobalEnvironment::CreateGlobalObject()"); |
| // Intentionally not an |EntryScope|, since the context doesn't exist yet. |
| v8::Isolate::Scope isolate_scope(isolate_); |
| v8::HandleScope handle_scope(isolate_); |
| v8::Local<v8::ObjectTemplate> global_object_template = {{binding_class}}::GetTemplate(isolate_)->InstanceTemplate(); |
| |
| v8::Local<v8::Context> context = |
| v8::Context::New(isolate_, nullptr, global_object_template); |
| context_.Reset(isolate_, context); |
| v8::Context::Scope context_scope(context); |
| |
| global_wrappable_ = global_interface; |
| |
| DCHECK(!environment_settings_); |
| DCHECK(environment_settings); |
| environment_settings_ = environment_settings; |
| EvaluateAutomatics(); |
| |
| v8::Local<v8::Object> global_object = context->Global(); |
| new WrapperPrivate(isolate_, global_interface, global_object); |
| |
| auto actual_global_object = global_object->GetPrototype()->ToObject(); |
| new WrapperPrivate(isolate_, global_interface, actual_global_object); |
| |
| {% for interface in all_interfaces %} |
| {% if interface.conditional %} |
| #if defined({{interface.conditional}}) |
| {% endif %} |
| wrapper_factory_->RegisterWrappableType( |
| {{interface.name}}::{{interface.name}}WrappableType(), |
| base::Bind(V8c{{interface.name}}::CreateWrapper), |
| base::Bind(V8c{{interface.name}}::GetTemplate)); |
| {% if interface.conditional %} |
| #endif // defined({{interface.conditional}}) |
| {% endif %} |
| {% endfor %} |
| |
| } |
| |
| } // namespace v8c |
| |
| template<> |
| void GlobalEnvironment::CreateGlobalObject<{{impl_class}}>( |
| const scoped_refptr<{{impl_class}}>& global_interface, |
| EnvironmentSettings* environment_settings) { |
| base::polymorphic_downcast<v8c::V8cGlobalEnvironment*>(this)->CreateGlobalObject(global_interface, environment_settings); |
| } |
| |
| } // namespace script |
| } // namespace cobalt |
| |
| {% endblock create_global_object_impl %} |
| |
| {% block enumeration_definitions %} |
| // enum block |
| {% for enumeration in enumerations %} |
| |
| inline void ToJSValue( |
| v8::Isolate* isolate, |
| {{impl_class}}::{{enumeration.name}} in_enum, |
| v8::Local<v8::Value>* out_value) { |
| switch (in_enum) { |
| {% for value, idl_value in enumeration.value_pairs %} |
| case {{impl_class}}::{{value}}: |
| ToJSValue(isolate, std::string("{{idl_value}}"), out_value); |
| return; |
| {% endfor %} |
| default: |
| NOTREACHED(); |
| *out_value = v8::Undefined(isolate); |
| } |
| } |
| |
| inline void FromJSValue( |
| v8::Isolate* isolate, v8::Local<v8::Value> value, |
| int conversion_flags, ExceptionState* exception_state, |
| {{impl_class}}::{{enumeration.name}}* out_enum) { |
| DCHECK_EQ(0, conversion_flags) << "Unexpected conversion flags."; |
| // Value -> IDL enum algorithm described here: |
| // http://heycam.github.io/webidl/#es-enumeration |
| // 1. Let S be the result of calling ToString(V). |
| v8::Local<v8::Context> context = isolate->GetCurrentContext(); |
| v8::MaybeLocal<v8::String> maybe_string = value->ToString(context); |
| v8::Local<v8::String> string; |
| if (!maybe_string.ToLocal(&string)) { |
| exception_state->SetSimpleException(cobalt::script::kConvertToEnumFailed); |
| return; |
| } |
| |
| bool match = false; |
| // 3. Return the enumeration value of type E that is equal to S. |
| {% for value, idl_value in enumeration.value_pairs %} |
| {{-" else " if not loop.first}} |
| if (string == NewInternalString(isolate, "{{id_value}}")) { |
| *out_enum = {{impl_class}}::{{value}}; |
| } |
| {% endfor %} |
| else { |
| // 2. If S is not one of E's enumeration values, then throw a TypeError. |
| exception_state->SetSimpleException(cobalt::script::kConvertToEnumFailed); |
| return; |
| } |
| } |
| |
| {% endfor %} |
| |
| {% endblock enumeration_definitions %} |