blob: 83e94a35ced700706cec1ca9dbd4f33991478abc [file] [log] [blame]
// Copyright 2016 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/bindings/core/v8/script_custom_element_definition_builder.h"
#include "third_party/blink/renderer/bindings/core/v8/idl_types.h"
#include "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h"
#include "third_party/blink/renderer/bindings/core/v8/script_custom_element_definition.h"
#include "third_party/blink/renderer/bindings/core/v8/script_value.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_custom_element_adopted_callback.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_custom_element_attribute_changed_callback.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_custom_element_constructor.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_custom_element_disabled_state_changed_callback.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_custom_element_form_associated_callback.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_custom_element_restore_value_callback.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_void_function.h"
#include "third_party/blink/renderer/platform/bindings/callback_method_retriever.h"
#include "third_party/blink/renderer/platform/bindings/dom_wrapper_world.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/bindings/script_state.h"
#include "third_party/blink/renderer/platform/bindings/v8_binding_macros.h"
namespace blink {
ScriptCustomElementDefinitionBuilder::ScriptCustomElementDefinitionBuilder(
ScriptState* script_state,
CustomElementRegistry* registry,
V8CustomElementConstructor* constructor,
ExceptionState& exception_state)
: exception_state_(exception_state) {
data_.script_state_ = script_state;
data_.registry_ = registry;
data_.constructor_ = constructor;
}
bool ScriptCustomElementDefinitionBuilder::CheckConstructorIntrinsics() {
DCHECK(GetScriptState()->World().IsMainWorld());
if (!Constructor()->IsConstructor()) {
exception_state_.ThrowTypeError(
"constructor argument is not a constructor");
return false;
}
return true;
}
bool ScriptCustomElementDefinitionBuilder::CheckConstructorNotRegistered() {
if (!ScriptCustomElementDefinition::ForConstructor(
GetScriptState(), data_.registry_, Constructor()->CallbackObject()))
return true;
// Constructor is already registered.
exception_state_.ThrowDOMException(
DOMExceptionCode::kNotSupportedError,
"this constructor has already been used with this registry");
return false;
}
bool ScriptCustomElementDefinitionBuilder::RememberOriginalProperties() {
// https://html.spec.whatwg.org/C/custom-elements.html#element-definition
// step 10. Run the following substeps while catching any exceptions:
CallbackMethodRetriever retriever(Constructor());
retriever.GetPrototypeObject(exception_state_);
if (exception_state_.HadException())
return false;
v8_connected_callback_ =
retriever.GetMethodOrUndefined("connectedCallback", exception_state_);
if (exception_state_.HadException())
return false;
if (v8_connected_callback_->IsFunction()) {
data_.connected_callback_ =
V8VoidFunction::Create(v8_connected_callback_.As<v8::Function>());
}
v8_disconnected_callback_ =
retriever.GetMethodOrUndefined("disconnectedCallback", exception_state_);
if (exception_state_.HadException())
return false;
if (v8_disconnected_callback_->IsFunction()) {
data_.disconnected_callback_ =
V8VoidFunction::Create(v8_disconnected_callback_.As<v8::Function>());
}
v8_adopted_callback_ =
retriever.GetMethodOrUndefined("adoptedCallback", exception_state_);
if (exception_state_.HadException())
return false;
if (v8_adopted_callback_->IsFunction()) {
data_.adopted_callback_ = V8CustomElementAdoptedCallback::Create(
v8_adopted_callback_.As<v8::Function>());
}
v8_attribute_changed_callback_ = retriever.GetMethodOrUndefined(
"attributeChangedCallback", exception_state_);
if (exception_state_.HadException())
return false;
if (v8_attribute_changed_callback_->IsFunction()) {
data_.attribute_changed_callback_ =
V8CustomElementAttributeChangedCallback::Create(
v8_attribute_changed_callback_.As<v8::Function>());
}
// step 10.6. If the value of the entry in lifecycleCallbacks with key
// "attributeChangedCallback" is not null, then:
if (data_.attribute_changed_callback_) {
v8::Isolate* isolate = Isolate();
v8::Local<v8::Context> current_context = isolate->GetCurrentContext();
v8::TryCatch try_catch(isolate);
v8::Local<v8::Value> v8_observed_attributes;
if (!Constructor()
->CallbackObject()
->Get(current_context,
V8AtomicString(isolate, "observedAttributes"))
.ToLocal(&v8_observed_attributes)) {
exception_state_.RethrowV8Exception(try_catch.Exception());
return false;
}
if (!v8_observed_attributes->IsUndefined()) {
const Vector<String>& observed_attrs =
NativeValueTraits<IDLSequence<IDLString>>::NativeValue(
isolate, v8_observed_attributes, exception_state_);
if (exception_state_.HadException())
return false;
data_.observed_attributes_.ReserveCapacityForSize(observed_attrs.size());
for (const auto& attribute : observed_attrs)
data_.observed_attributes_.insert(AtomicString(attribute));
}
}
if (RuntimeEnabledFeatures::ElementInternalsEnabled()) {
auto* isolate = Isolate();
v8::Local<v8::Context> current_context = isolate->GetCurrentContext();
v8::TryCatch try_catch(isolate);
v8::Local<v8::Value> v8_disabled_features;
if (!Constructor()
->CallbackObject()
->Get(current_context, V8AtomicString(isolate, "disabledFeatures"))
.ToLocal(&v8_disabled_features)) {
exception_state_.RethrowV8Exception(try_catch.Exception());
return false;
}
if (!v8_disabled_features->IsUndefined()) {
data_.disabled_features_ =
NativeValueTraits<IDLSequence<IDLString>>::NativeValue(
isolate, v8_disabled_features, exception_state_);
if (exception_state_.HadException())
return false;
}
}
if (RuntimeEnabledFeatures::FormAssociatedCustomElementsEnabled()) {
auto* isolate = Isolate();
v8::Local<v8::Context> current_context = isolate->GetCurrentContext();
v8::TryCatch try_catch(isolate);
v8::Local<v8::Value> v8_form_associated;
if (!Constructor()
->CallbackObject()
->Get(current_context, V8AtomicString(isolate, "formAssociated"))
.ToLocal(&v8_form_associated)) {
exception_state_.RethrowV8Exception(try_catch.Exception());
return false;
}
if (!v8_form_associated->IsUndefined()) {
data_.is_form_associated_ = NativeValueTraits<IDLBoolean>::NativeValue(
isolate, v8_form_associated, exception_state_);
if (exception_state_.HadException())
return false;
}
}
if (data_.is_form_associated_) {
v8_form_associated_callback_ = retriever.GetMethodOrUndefined(
"formAssociatedCallback", exception_state_);
if (exception_state_.HadException())
return false;
if (v8_form_associated_callback_->IsFunction()) {
data_.form_associated_callback_ =
V8CustomElementFormAssociatedCallback::Create(
v8_form_associated_callback_.As<v8::Function>());
}
v8_form_reset_callback_ =
retriever.GetMethodOrUndefined("formResetCallback", exception_state_);
if (exception_state_.HadException())
return false;
if (v8_form_reset_callback_->IsFunction()) {
data_.form_reset_callback_ =
V8VoidFunction::Create(v8_form_reset_callback_.As<v8::Function>());
}
v8_disabled_state_changed_callback_ = retriever.GetMethodOrUndefined(
"disabledStateChangedCallback", exception_state_);
if (exception_state_.HadException())
return false;
if (v8_disabled_state_changed_callback_->IsFunction()) {
data_.disabled_state_changed_callback_ =
V8CustomElementDisabledStateChangedCallback::Create(
v8_disabled_state_changed_callback_.As<v8::Function>());
}
v8_restore_value_callback_ = retriever.GetMethodOrUndefined(
"restoreValueCallback", exception_state_);
if (exception_state_.HadException())
return false;
if (!v8_restore_value_callback_->IsFunction()) {
exception_state_.ThrowDOMException(
DOMExceptionCode::kTypeMismatchError,
"A class for form-associated custom elements must have a "
"'restoreValueCallback'.");
return false;
}
data_.restore_value_callback_ = V8CustomElementRestoreValueCallback::Create(
v8_restore_value_callback_.As<v8::Function>());
}
return true;
}
CustomElementDefinition* ScriptCustomElementDefinitionBuilder::Build(
const CustomElementDescriptor& descriptor,
CustomElementDefinition::Id id) {
return ScriptCustomElementDefinition::Create(data_, descriptor, id);
}
v8::Isolate* ScriptCustomElementDefinitionBuilder::Isolate() {
return data_.script_state_->GetIsolate();
}
} // namespace blink