| // Copyright 2020 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "third_party/blink/renderer/platform/bindings/idl_member_installer.h" |
| |
| #include "third_party/blink/renderer/platform/bindings/dom_wrapper_world.h" |
| #include "third_party/blink/renderer/platform/bindings/v8_binding.h" |
| #include "third_party/blink/renderer/platform/bindings/v8_private_property.h" |
| |
| namespace blink { |
| |
| namespace bindings { |
| |
| namespace { |
| |
| template <typename Config> |
| bool DoesWorldMatch(const Config& config, const DOMWrapperWorld& world) { |
| const unsigned world_bit = static_cast<unsigned>( |
| world.IsMainWorld() ? IDLMemberInstaller::FlagWorld::kMainWorld |
| : IDLMemberInstaller::FlagWorld::kNonMainWorlds); |
| return config.world & world_bit; |
| } |
| |
| enum class FunctionKind { |
| kAttributeGet, |
| kAttributeSet, |
| kOperation, |
| }; |
| |
| template <FunctionKind kind, typename Config> |
| v8::FunctionCallback GetConfigCallback(const Config& config); |
| template <> |
| v8::FunctionCallback GetConfigCallback<FunctionKind::kAttributeGet>( |
| const IDLMemberInstaller::AttributeConfig& config) { |
| return config.callback_for_get; |
| } |
| template <> |
| v8::FunctionCallback GetConfigCallback<FunctionKind::kAttributeSet>( |
| const IDLMemberInstaller::AttributeConfig& config) { |
| return config.callback_for_set; |
| } |
| template <> |
| v8::FunctionCallback GetConfigCallback<FunctionKind::kOperation>( |
| const IDLMemberInstaller::OperationConfig& config) { |
| return config.callback; |
| } |
| |
| template <FunctionKind kind, typename Config> |
| int GetConfigLength(const Config& config); |
| template <> |
| int GetConfigLength<FunctionKind::kAttributeGet>( |
| const IDLMemberInstaller::AttributeConfig& config) { |
| return 0; |
| } |
| template <> |
| int GetConfigLength<FunctionKind::kAttributeSet>( |
| const IDLMemberInstaller::AttributeConfig& config) { |
| return 1; |
| } |
| template <> |
| int GetConfigLength<FunctionKind::kOperation>( |
| const IDLMemberInstaller::OperationConfig& config) { |
| return config.length; |
| } |
| |
| template <FunctionKind kind, typename Config> |
| IDLMemberInstaller::FlagCrossOriginCheck GetConfigCrossOriginCheck( |
| const Config& config); |
| template <> |
| IDLMemberInstaller::FlagCrossOriginCheck |
| GetConfigCrossOriginCheck<FunctionKind::kAttributeGet>( |
| const IDLMemberInstaller::AttributeConfig& config) { |
| return static_cast<IDLMemberInstaller::FlagCrossOriginCheck>( |
| config.cross_origin_check_for_get); |
| } |
| template <> |
| IDLMemberInstaller::FlagCrossOriginCheck |
| GetConfigCrossOriginCheck<FunctionKind::kAttributeSet>( |
| const IDLMemberInstaller::AttributeConfig& config) { |
| return static_cast<IDLMemberInstaller::FlagCrossOriginCheck>( |
| config.cross_origin_check_for_set); |
| } |
| template <> |
| IDLMemberInstaller::FlagCrossOriginCheck |
| GetConfigCrossOriginCheck<FunctionKind::kOperation>( |
| const IDLMemberInstaller::OperationConfig& config) { |
| return static_cast<IDLMemberInstaller::FlagCrossOriginCheck>( |
| config.cross_origin_check); |
| } |
| |
| template <FunctionKind kind, typename Config> |
| v8::SideEffectType GetConfigSideEffect(const Config& config); |
| template <> |
| v8::SideEffectType GetConfigSideEffect<FunctionKind::kAttributeGet>( |
| const IDLMemberInstaller::AttributeConfig& config) { |
| return static_cast<v8::SideEffectType>(config.v8_side_effect); |
| } |
| template <> |
| v8::SideEffectType GetConfigSideEffect<FunctionKind::kAttributeSet>( |
| const IDLMemberInstaller::AttributeConfig& config) { |
| return v8::SideEffectType::kHasSideEffect; |
| } |
| template <> |
| v8::SideEffectType GetConfigSideEffect<FunctionKind::kOperation>( |
| const IDLMemberInstaller::OperationConfig& config) { |
| return static_cast<v8::SideEffectType>(config.v8_side_effect); |
| } |
| |
| template <FunctionKind kind, typename Config> |
| V8PrivateProperty::CachedAccessor GetConfigV8CachedAccessor( |
| const Config& config); |
| template <> |
| V8PrivateProperty::CachedAccessor |
| GetConfigV8CachedAccessor<FunctionKind::kAttributeGet>( |
| const IDLMemberInstaller::AttributeConfig& config) { |
| return static_cast<V8PrivateProperty::CachedAccessor>( |
| config.v8_cached_accessor); |
| } |
| template <> |
| V8PrivateProperty::CachedAccessor |
| GetConfigV8CachedAccessor<FunctionKind::kAttributeSet>( |
| const IDLMemberInstaller::AttributeConfig& config) { |
| return V8PrivateProperty::CachedAccessor::kNone; |
| } |
| template <> |
| V8PrivateProperty::CachedAccessor |
| GetConfigV8CachedAccessor<FunctionKind::kOperation>( |
| const IDLMemberInstaller::OperationConfig& config) { |
| return V8PrivateProperty::CachedAccessor::kNone; |
| } |
| |
| template <FunctionKind kind, typename Config> |
| v8::Local<v8::FunctionTemplate> CreateFunctionTemplate( |
| v8::Isolate* isolate, |
| const DOMWrapperWorld& world, |
| v8::Local<v8::Signature> signature, |
| v8::Local<v8::String> name, |
| const Config& config, |
| const v8::CFunction* v8_c_function = nullptr) { |
| v8::FunctionCallback callback = GetConfigCallback<kind>(config); |
| if (!callback) |
| return v8::Local<v8::FunctionTemplate>(); |
| |
| int length = GetConfigLength<kind>(config); |
| v8::SideEffectType v8_side_effect = GetConfigSideEffect<kind>(config); |
| V8PrivateProperty::CachedAccessor v8_cached_accessor = |
| GetConfigV8CachedAccessor<kind>(config); |
| |
| v8::Local<v8::FunctionTemplate> function_template; |
| if (v8_cached_accessor == V8PrivateProperty::CachedAccessor::kNone || |
| (v8_cached_accessor == |
| V8PrivateProperty::CachedAccessor::kWindowDocument && |
| !world.IsMainWorld())) { |
| function_template = v8::FunctionTemplate::New( |
| isolate, callback, v8::Local<v8::Value>(), signature, length, |
| v8::ConstructorBehavior::kThrow, v8_side_effect, v8_c_function); |
| } else { |
| function_template = v8::FunctionTemplate::NewWithCache( |
| isolate, callback, |
| V8PrivateProperty::GetCachedAccessor(isolate, v8_cached_accessor) |
| .GetPrivate(), |
| v8::Local<v8::Value>(), signature, length, v8_side_effect); |
| function_template->RemovePrototype(); |
| } |
| |
| function_template->SetClassName(name); |
| function_template->SetAcceptAnyReceiver( |
| GetConfigCrossOriginCheck<kind>(config) == |
| IDLMemberInstaller::FlagCrossOriginCheck::kDoNotCheck); |
| |
| return function_template; |
| } |
| |
| template <FunctionKind kind, typename Config> |
| v8::Local<v8::Function> CreateFunction(v8::Isolate* isolate, |
| v8::Local<v8::Context> context, |
| const DOMWrapperWorld& world, |
| v8::Local<v8::Signature> signature, |
| v8::Local<v8::String> name, |
| const Config& config) { |
| if (!GetConfigCallback<kind>(config)) |
| return v8::Local<v8::Function>(); |
| |
| return CreateFunctionTemplate<kind>(isolate, world, signature, name, config) |
| ->GetFunction(context) |
| .ToLocalChecked(); |
| } |
| |
| void InstallAttribute(v8::Isolate* isolate, |
| const DOMWrapperWorld& world, |
| v8::Local<v8::Template> instance_template, |
| v8::Local<v8::Template> prototype_template, |
| v8::Local<v8::Template> interface_template, |
| v8::Local<v8::Signature> signature, |
| const IDLMemberInstaller::AttributeConfig& config) { |
| if (!DoesWorldMatch(config, world)) |
| return; |
| |
| IDLMemberInstaller::FlagLocation location = |
| static_cast<IDLMemberInstaller::FlagLocation>(config.location); |
| if (static_cast<IDLMemberInstaller::FlagReceiverCheck>( |
| config.receiver_check) == |
| IDLMemberInstaller::FlagReceiverCheck::kDoNotCheck || |
| location == IDLMemberInstaller::FlagLocation::kInterface) |
| signature = v8::Local<v8::Signature>(); |
| |
| StringView name_as_view(config.name); |
| v8::Local<v8::String> name = V8AtomicString(isolate, name_as_view); |
| v8::Local<v8::String> get_name = V8AtomicString( |
| isolate, static_cast<String>(StringView("get ", 4) + name_as_view)); |
| v8::Local<v8::String> set_name = V8AtomicString( |
| isolate, static_cast<String>(StringView("set ", 4) + name_as_view)); |
| v8::Local<v8::FunctionTemplate> get_func = |
| CreateFunctionTemplate<FunctionKind::kAttributeGet>( |
| isolate, world, signature, get_name, config); |
| v8::Local<v8::FunctionTemplate> set_func = |
| CreateFunctionTemplate<FunctionKind::kAttributeSet>( |
| isolate, world, signature, set_name, config); |
| |
| v8::Local<v8::Template> target_template; |
| switch (location) { |
| case IDLMemberInstaller::FlagLocation::kInstance: |
| target_template = instance_template; |
| break; |
| case IDLMemberInstaller::FlagLocation::kPrototype: |
| target_template = prototype_template; |
| break; |
| case IDLMemberInstaller::FlagLocation::kInterface: |
| target_template = interface_template; |
| break; |
| default: |
| NOTREACHED(); |
| } |
| target_template->SetAccessorProperty( |
| name, get_func, set_func, |
| static_cast<v8::PropertyAttribute>(config.v8_property_attribute)); |
| } |
| |
| void InstallAttribute(v8::Isolate* isolate, |
| v8::Local<v8::Context> context, |
| const DOMWrapperWorld& world, |
| v8::Local<v8::Object> instance_object, |
| v8::Local<v8::Object> prototype_object, |
| v8::Local<v8::Object> interface_object, |
| v8::Local<v8::Signature> signature, |
| const IDLMemberInstaller::AttributeConfig& config) { |
| if (!DoesWorldMatch(config, world)) |
| return; |
| |
| IDLMemberInstaller::FlagLocation location = |
| static_cast<IDLMemberInstaller::FlagLocation>(config.location); |
| if (static_cast<IDLMemberInstaller::FlagReceiverCheck>( |
| config.receiver_check) == |
| IDLMemberInstaller::FlagReceiverCheck::kDoNotCheck || |
| location == IDLMemberInstaller::FlagLocation::kInterface) |
| signature = v8::Local<v8::Signature>(); |
| |
| StringView name_as_view(config.name); |
| v8::Local<v8::String> name = V8AtomicString(isolate, name_as_view); |
| v8::Local<v8::String> get_name = V8AtomicString( |
| isolate, static_cast<String>(StringView("get ", 4) + name_as_view)); |
| v8::Local<v8::String> set_name = V8AtomicString( |
| isolate, static_cast<String>(StringView("set ", 4) + name_as_view)); |
| v8::Local<v8::Function> get_func = |
| CreateFunction<FunctionKind::kAttributeGet>(isolate, context, world, |
| signature, get_name, config); |
| v8::Local<v8::Function> set_func = |
| CreateFunction<FunctionKind::kAttributeSet>(isolate, context, world, |
| signature, set_name, config); |
| |
| v8::Local<v8::Object> target_object; |
| switch (location) { |
| case IDLMemberInstaller::FlagLocation::kInstance: |
| target_object = instance_object; |
| break; |
| case IDLMemberInstaller::FlagLocation::kPrototype: |
| target_object = prototype_object; |
| break; |
| case IDLMemberInstaller::FlagLocation::kInterface: |
| target_object = interface_object; |
| break; |
| default: |
| NOTREACHED(); |
| } |
| target_object->SetAccessorProperty( |
| name, get_func, set_func, |
| static_cast<v8::PropertyAttribute>(config.v8_property_attribute)); |
| } |
| |
| void InstallOperation(v8::Isolate* isolate, |
| const DOMWrapperWorld& world, |
| v8::Local<v8::Template> instance_template, |
| v8::Local<v8::Template> prototype_template, |
| v8::Local<v8::Template> interface_template, |
| v8::Local<v8::Signature> signature, |
| const IDLMemberInstaller::OperationConfig& config, |
| const v8::CFunction* v8_c_function = nullptr) { |
| if (!DoesWorldMatch(config, world)) |
| return; |
| |
| IDLMemberInstaller::FlagLocation location = |
| static_cast<IDLMemberInstaller::FlagLocation>(config.location); |
| if (static_cast<IDLMemberInstaller::FlagReceiverCheck>( |
| config.receiver_check) == |
| IDLMemberInstaller::FlagReceiverCheck::kDoNotCheck || |
| location == IDLMemberInstaller::FlagLocation::kInterface) |
| signature = v8::Local<v8::Signature>(); |
| |
| v8::Local<v8::String> name = V8AtomicString(isolate, config.name); |
| v8::Local<v8::FunctionTemplate> func = |
| CreateFunctionTemplate<FunctionKind::kOperation>( |
| isolate, world, signature, name, config, v8_c_function); |
| |
| v8::Local<v8::Template> target_template; |
| switch (location) { |
| case IDLMemberInstaller::FlagLocation::kInstance: |
| target_template = instance_template; |
| break; |
| case IDLMemberInstaller::FlagLocation::kPrototype: |
| target_template = prototype_template; |
| break; |
| case IDLMemberInstaller::FlagLocation::kInterface: |
| target_template = interface_template; |
| break; |
| default: |
| NOTREACHED(); |
| } |
| target_template->Set( |
| name, func, |
| static_cast<v8::PropertyAttribute>(config.v8_property_attribute)); |
| } |
| |
| void InstallOperation(v8::Isolate* isolate, |
| v8::Local<v8::Context> context, |
| const DOMWrapperWorld& world, |
| v8::Local<v8::Object> instance_object, |
| v8::Local<v8::Object> prototype_object, |
| v8::Local<v8::Object> interface_object, |
| v8::Local<v8::Signature> signature, |
| const IDLMemberInstaller::OperationConfig& config) { |
| if (!DoesWorldMatch(config, world)) |
| return; |
| |
| IDLMemberInstaller::FlagLocation location = |
| static_cast<IDLMemberInstaller::FlagLocation>(config.location); |
| if (static_cast<IDLMemberInstaller::FlagReceiverCheck>( |
| config.receiver_check) == |
| IDLMemberInstaller::FlagReceiverCheck::kDoNotCheck || |
| location == IDLMemberInstaller::FlagLocation::kInterface) |
| signature = v8::Local<v8::Signature>(); |
| |
| v8::Local<v8::String> name = V8AtomicString(isolate, config.name); |
| v8::Local<v8::Function> func = CreateFunction<FunctionKind::kOperation>( |
| isolate, context, world, signature, name, config); |
| |
| v8::Local<v8::Object> target_object; |
| switch (location) { |
| case IDLMemberInstaller::FlagLocation::kInstance: |
| target_object = instance_object; |
| break; |
| case IDLMemberInstaller::FlagLocation::kPrototype: |
| target_object = prototype_object; |
| break; |
| case IDLMemberInstaller::FlagLocation::kInterface: |
| target_object = interface_object; |
| break; |
| default: |
| NOTREACHED(); |
| } |
| target_object |
| ->DefineOwnProperty( |
| context, name, func, |
| static_cast<v8::PropertyAttribute>(config.v8_property_attribute)) |
| .ToChecked(); |
| } |
| |
| } // namespace |
| |
| // static |
| void IDLMemberInstaller::InstallAttributes( |
| v8::Isolate* isolate, |
| const DOMWrapperWorld& world, |
| v8::Local<v8::Template> instance_template, |
| v8::Local<v8::Template> prototype_template, |
| v8::Local<v8::Template> interface_template, |
| v8::Local<v8::Signature> signature, |
| base::span<const AttributeConfig> configs) { |
| for (const auto& config : configs) { |
| InstallAttribute(isolate, world, instance_template, prototype_template, |
| interface_template, signature, config); |
| } |
| } |
| |
| // static |
| void IDLMemberInstaller::InstallAttributes( |
| v8::Isolate* isolate, |
| const DOMWrapperWorld& world, |
| v8::Local<v8::Object> instance_object, |
| v8::Local<v8::Object> prototype_object, |
| v8::Local<v8::Object> interface_object, |
| v8::Local<v8::Signature> signature, |
| base::span<const AttributeConfig> configs) { |
| v8::Local<v8::Context> context = isolate->GetCurrentContext(); |
| for (const auto& config : configs) { |
| InstallAttribute(isolate, context, world, instance_object, prototype_object, |
| interface_object, signature, config); |
| } |
| } |
| |
| // static |
| void IDLMemberInstaller::InstallConstants( |
| v8::Isolate* isolate, |
| const DOMWrapperWorld& world, |
| v8::Local<v8::Template> instance_template, |
| v8::Local<v8::Template> prototype_template, |
| v8::Local<v8::Template> interface_template, |
| v8::Local<v8::Signature> signature, |
| base::span<const ConstantCallbackConfig> configs) { |
| const bool has_prototype_template = !prototype_template.IsEmpty(); |
| const v8::PropertyAttribute v8_property_attribute = |
| static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete); |
| for (const auto& config : configs) { |
| v8::Local<v8::String> name = V8AtomicString(isolate, config.name); |
| if (has_prototype_template) { |
| prototype_template->SetLazyDataProperty( |
| name, config.callback, v8::Local<v8::Value>(), v8_property_attribute, |
| v8::SideEffectType::kHasNoSideEffect); |
| } |
| interface_template->SetLazyDataProperty( |
| name, config.callback, v8::Local<v8::Value>(), v8_property_attribute, |
| v8::SideEffectType::kHasNoSideEffect); |
| } |
| } |
| |
| // static |
| void IDLMemberInstaller::InstallConstants( |
| v8::Isolate* isolate, |
| const DOMWrapperWorld& world, |
| v8::Local<v8::Template> instance_template, |
| v8::Local<v8::Template> prototype_template, |
| v8::Local<v8::Template> interface_template, |
| v8::Local<v8::Signature> signature, |
| base::span<const ConstantValueConfig> configs) { |
| const bool has_prototype_template = !prototype_template.IsEmpty(); |
| const v8::PropertyAttribute v8_property_attribute = |
| static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete); |
| for (const auto& config : configs) { |
| v8::Local<v8::String> name = V8AtomicString(isolate, config.name); |
| v8::Local<v8::Integer> value; |
| if (config.value < 0) { |
| int32_t i32_value = static_cast<int32_t>(config.value); |
| DCHECK_EQ(static_cast<int64_t>(i32_value), config.value); |
| value = v8::Integer::New(isolate, i32_value); |
| } else { |
| uint32_t u32_value = static_cast<uint32_t>(config.value); |
| DCHECK_EQ(static_cast<int64_t>(u32_value), config.value); |
| value = v8::Integer::NewFromUnsigned(isolate, u32_value); |
| } |
| if (has_prototype_template) { |
| prototype_template->Set(name, value, v8_property_attribute); |
| } |
| interface_template->Set(name, value, v8_property_attribute); |
| } |
| } |
| |
| // static |
| void IDLMemberInstaller::InstallOperations( |
| v8::Isolate* isolate, |
| const DOMWrapperWorld& world, |
| v8::Local<v8::Template> instance_template, |
| v8::Local<v8::Template> prototype_template, |
| v8::Local<v8::Template> interface_template, |
| v8::Local<v8::Signature> signature, |
| base::span<const OperationConfig> configs) { |
| for (const auto& config : configs) { |
| InstallOperation(isolate, world, instance_template, prototype_template, |
| interface_template, signature, config); |
| } |
| } |
| |
| // static |
| void IDLMemberInstaller::InstallOperations( |
| v8::Isolate* isolate, |
| const DOMWrapperWorld& world, |
| v8::Local<v8::Object> instance_object, |
| v8::Local<v8::Object> prototype_object, |
| v8::Local<v8::Object> interface_object, |
| v8::Local<v8::Signature> signature, |
| base::span<const OperationConfig> configs) { |
| v8::Local<v8::Context> context = isolate->GetCurrentContext(); |
| for (const auto& config : configs) { |
| InstallOperation(isolate, context, world, instance_object, prototype_object, |
| interface_object, signature, config); |
| } |
| } |
| |
| // static |
| void IDLMemberInstaller::InstallOperations( |
| v8::Isolate* isolate, |
| const DOMWrapperWorld& world, |
| v8::Local<v8::Template> instance_template, |
| v8::Local<v8::Template> prototype_template, |
| v8::Local<v8::Template> interface_template, |
| v8::Local<v8::Signature> signature, |
| base::span<const NoAllocDirectCallOperationConfig> configs) { |
| for (const auto& config : configs) { |
| InstallOperation(isolate, world, instance_template, prototype_template, |
| interface_template, signature, config.operation_config, |
| &config.v8_c_function); |
| } |
| } |
| |
| // static |
| void IDLMemberInstaller::InstallExposedConstructs( |
| v8::Isolate* isolate, |
| const DOMWrapperWorld& world, |
| v8::Local<v8::Template> instance_template, |
| v8::Local<v8::Template> prototype_template, |
| v8::Local<v8::Template> interface_template, |
| v8::Local<v8::Signature> signature, |
| base::span<const ExposedConstructConfig> configs) { |
| for (const auto& config : configs) { |
| v8::Local<v8::String> name = V8AtomicString(isolate, config.name); |
| instance_template->SetLazyDataProperty( |
| name, config.callback, v8::Local<v8::Value>(), v8::DontEnum, |
| v8::SideEffectType::kHasNoSideEffect); |
| } |
| } |
| |
| // static |
| void IDLMemberInstaller::InstallExposedConstructs( |
| v8::Isolate* isolate, |
| const DOMWrapperWorld& world, |
| v8::Local<v8::Object> instance_object, |
| v8::Local<v8::Object> prototype_object, |
| v8::Local<v8::Object> interface_object, |
| v8::Local<v8::Signature> signature, |
| base::span<const ExposedConstructConfig> configs) { |
| v8::Local<v8::Context> context = isolate->GetCurrentContext(); |
| for (const auto& config : configs) { |
| instance_object |
| ->SetLazyDataProperty(context, V8AtomicString(isolate, config.name), |
| config.callback, v8::Local<v8::Value>(), |
| v8::DontEnum, |
| v8::SideEffectType::kHasNoSideEffect) |
| .ToChecked(); |
| } |
| } |
| |
| } // namespace bindings |
| |
| } // namespace blink |