Files
ladybird/Libraries/LibWeb/HTML/ElementInternals.cpp
Shannon Booth fd44da6829 LibWeb/Bindings: Emit one bindings header and cpp per IDL
Previously, the LibWeb bindings generator would output multiple per
interface files like Prototype/Constructor/Namespace/GlobalMixin
depending on the contents of that IDL file.

This complicates the build system as it means that it does not know
what files will be generated without knowledge of the contents of that
IDL file.

Instead, for each IDL file only generate a single Bindings/<IDLFile>.h
and Bindings/<IDLFile>.cpp.
2026-04-21 07:36:13 +02:00

263 lines
12 KiB
C++

/*
* Copyright (c) 2024, the Ladybird developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWeb/Bindings/ElementInternals.h>
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/DOM/ShadowRoot.h>
#include <LibWeb/FileAPI/File.h>
#include <LibWeb/HTML/ElementInternals.h>
#include <LibWeb/HTML/HTMLElement.h>
#include <LibWeb/XHR/FormData.h>
namespace Web::HTML {
GC_DEFINE_ALLOCATOR(ElementInternals);
GC::Ref<ElementInternals> ElementInternals::create(JS::Realm& realm, HTMLElement& target_element)
{
return realm.create<ElementInternals>(realm, target_element);
}
ElementInternals::ElementInternals(JS::Realm& realm, HTMLElement& target_element)
: Bindings::PlatformObject(realm)
, m_target_element(target_element)
{
}
// https://html.spec.whatwg.org/multipage/custom-elements.html#dom-elementinternals-shadowroot
GC::Ptr<DOM::ShadowRoot> ElementInternals::shadow_root() const
{
// 1. Let target be this's target element.
auto target = m_target_element;
// 2. If target is not a shadow host, then return null.
if (!target->is_shadow_host())
return nullptr;
// 3. Let shadow be target's shadow root.
auto shadow = target->shadow_root();
// 4. If shadow's available to element internals is false, then return null.
if (!shadow->available_to_element_internals())
return nullptr;
// 5. Return shadow.
return shadow;
}
// https://html.spec.whatwg.org/multipage/custom-elements.html#dom-elementinternals-setformvalue
WebIDL::ExceptionOr<void> ElementInternals::set_form_value(ElementInternalsFormValue value, Optional<ElementInternalsFormValue> state)
{
// 1. Let element be this's target element.
auto element = m_target_element;
// 2. If element is not a form-associated custom element, then throw a "NotSupportedError" DOMException.
if (!element->is_form_associated_custom_element())
return WebIDL::NotSupportedError::create(realm(), "Element is not a form-associated custom element"_utf16);
// 3. Set target element's submission value to value if value is not a FormData object, or to a clone of value's entry list otherwise.
auto submission_value = value.visit(
[](GC::Root<FileAPI::File> const& file) -> FormAssociatedElement::FACESubmissionValue {
return GC::Ref { *file };
},
[](String const& string) -> FormAssociatedElement::FACESubmissionValue {
return string;
},
[](GC::Root<XHR::FormData> const& form_data) -> FormAssociatedElement::FACESubmissionValue {
return form_data->entry_list();
},
[](Empty const& empty) -> FormAssociatedElement::FACESubmissionValue {
return empty;
});
element->set_face_submission_value({}, submission_value);
// 4. If the state argument of the function is omitted, set element's state to its submission value.
if (!state.has_value()) {
element->set_face_state({}, submission_value);
}
// 5. Otherwise, if state is a FormData object, set element's state to a clone of state's entry list.
// 6. Otherwise, set element's state to state.
else {
auto state_value = state.value().visit(
[](GC::Root<FileAPI::File> const& file) -> FormAssociatedElement::FACESubmissionValue {
return GC::Ref { *file };
},
[](String const& string) -> FormAssociatedElement::FACESubmissionValue {
return string;
},
[](GC::Root<XHR::FormData> const& form_data) -> FormAssociatedElement::FACESubmissionValue {
return form_data->entry_list();
},
[](Empty const& empty) -> FormAssociatedElement::FACESubmissionValue {
return empty;
});
element->set_face_state({}, state_value);
}
return {};
}
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-elementinternals-form
WebIDL::ExceptionOr<GC::Ptr<HTMLFormElement>> ElementInternals::form() const
{
// Form-associated custom elements don't have form IDL attribute. Instead, their ElementInternals object has a form IDL attribute.
// On getting, it must throw a "NotSupportedError" DOMException if the target element is not a form-associated custom element.
// Otherwise, it must return the element's form owner, or null if there isn't one.
if (!m_target_element->is_form_associated_custom_element())
return WebIDL::NotSupportedError::create(realm(), "Element is not a form-associated custom element"_utf16);
return m_target_element->form();
}
// https://html.spec.whatwg.org/multipage/custom-elements.html#dom-elementinternals-setvalidity
WebIDL::ExceptionOr<void> ElementInternals::set_validity(ValidityStateFlags const& flags, Optional<String> message, GC::Ptr<HTMLElement> anchor)
{
// 1. Let element be this's target element.
auto element = m_target_element;
// 2. If element is not a form-associated custom element, then throw a "NotSupportedError" DOMException.
if (!element->is_form_associated_custom_element())
return WebIDL::NotSupportedError::create(realm(), "Element is not a form-associated custom element"_utf16);
// 3. If flags contains one or more true values and message is not given or is the empty string, then throw a TypeError.
if (flags.has_one_or_more_true_values() && (!message.has_value() || message->is_empty())) {
return WebIDL::SimpleException {
WebIDL::SimpleExceptionType::TypeError,
"Invalid flag(s) and empty message"sv
};
}
// 4. For each entry flag → value of flags, set element's validity flag with the name flag to value.
element->set_face_validity_flags({}, flags);
// 5. Set element's validation message to the empty string if message is not given or all of element's validity flags are false, or to message otherwise.
String validation_message;
if (message.has_value() && flags.has_one_or_more_true_values())
validation_message = message.release_value();
element->set_face_validation_message({}, validation_message);
// 6. If element's customError validity flag is true, then set element's custom validity error message to element's
// validation message. Otherwise, set element's custom validity error message to the empty string.
element->set_custom_validity_error_message({}, flags.custom_error ? validation_message : ""_string);
// 7. If anchor is not given, then set it to element.
if (!anchor) {
anchor = element;
}
// 8. Otherwise, if anchor is not a shadow-including inclusive descendant of element, then throw a "NotFoundError" DOMException.
else if (!anchor->is_shadow_including_inclusive_descendant_of(element)) {
return WebIDL::NotFoundError::create(realm(), "Anchor is not a shadow-including descendant of element"_utf16);
}
// 9. Set element's validation anchor to anchor.
element->set_face_validation_anchor({}, anchor);
return {};
}
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-elementinternals-willvalidate
WebIDL::ExceptionOr<bool> ElementInternals::will_validate() const
{
// The willValidate attribute of ElementInternals interface, on getting, must throw a "NotSupportedError" DOMException if
// the target element is not a form-associated custom element. Otherwise, it must return true if the target element is a
// candidate for constraint validation, and false otherwise.
if (!m_target_element->is_form_associated_custom_element())
return WebIDL::NotSupportedError::create(realm(), "Element is not a form-associated custom element"_utf16);
return m_target_element->is_candidate_for_constraint_validation();
}
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-elementinternals-validity
WebIDL::ExceptionOr<GC::Ref<ValidityState const>> ElementInternals::validity() const
{
// The validity attribute of ElementInternals interface, on getting, must throw a "NotSupportedError" DOMException if
// the target element is not a form-associated custom element. Otherwise, it must return a ValidityState object that
// represents the validity states of the target element. This object is live.
if (!m_target_element->is_form_associated_custom_element())
return WebIDL::NotSupportedError::create(realm(), "Element is not a form-associated custom element"_utf16);
return ValidityState::create(realm(), m_target_element);
}
// https://html.spec.whatwg.org/multipage/custom-elements.html#dom-elementinternals-validationmessage
WebIDL::ExceptionOr<String> ElementInternals::validation_message() const
{
// 1. Let element be this's target element.
auto element = m_target_element;
// 2. If element is not a form-associated custom element, then throw a "NotSupportedError" DOMException.
if (!element->is_form_associated_custom_element())
return WebIDL::NotSupportedError::create(realm(), "Element is not a form-associated custom element"_utf16);
// 3. Return element's validation message.
return element->face_validation_message();
}
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-elementinternals-checkvalidity
WebIDL::ExceptionOr<bool> ElementInternals::check_validity() const
{
// 1. Let element be this ElementInternals's target element.
auto element = m_target_element;
// 2. If element is not a form-associated custom element, then throw a "NotSupportedError" DOMException.
if (!element->is_form_associated_custom_element())
return WebIDL::NotSupportedError::create(realm(), "Element is not a form-associated custom element"_utf16);
// 3. Run the check validity steps on element.
return element->check_validity_steps();
}
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-elementinternals-reportvalidity
WebIDL::ExceptionOr<bool> ElementInternals::report_validity() const
{
// 1. Let element be this ElementInternals's target element
auto element = m_target_element;
// 2. If element is not a form-associated custom element, then throw a "NotSupportedError" DOMException.
if (!element->is_form_associated_custom_element())
return WebIDL::NotSupportedError::create(realm(), "Element is not a form-associated custom element"_utf16);
// 3. Run the report validity steps on element.
return element->report_validity_steps();
}
// https://html.spec.whatwg.org/multipage/forms.html#dom-elementinternals-labels
WebIDL::ExceptionOr<GC::Ptr<DOM::NodeList>> ElementInternals::labels()
{
// Form-associated custom elements don't have a labels IDL attribute. Instead, their ElementInternals object has a labels IDL attribute.
// On getting, it must throw a "NotSupportedError" DOMException if the target element is not a form-associated custom element.
// Otherwise, it must return that NodeList object, and that same value must always be returned.
if (!m_target_element->is_form_associated_custom_element())
return WebIDL::NotSupportedError::create(realm(), "Element is not a form-associated custom element"_utf16);
return m_target_element->labels();
}
// https://html.spec.whatwg.org/multipage/custom-elements.html#dom-elementinternals-states
GC::Ptr<CustomStateSet> ElementInternals::states()
{
// The states getter steps are to return this's target element's states set.
return m_target_element->ensure_custom_state_set();
}
void ElementInternals::initialize(JS::Realm& realm)
{
WEB_SET_PROTOTYPE_FOR_INTERFACE(ElementInternals);
Base::initialize(realm);
}
void ElementInternals::visit_edges(JS::Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_target_element);
}
}