LibWeb: Implement stub for ElementInternals

This implements a stub ElementInternals object which implements the
shadowRoot getter only.

Also implement attachInternals function.
This commit is contained in:
Luke Warlow 2024-06-24 21:54:42 +01:00 committed by Andreas Kling
parent ce8d3d17c4
commit a65f1ecc37
Notes: sideshowbarker 2024-07-17 18:49:10 +09:00
13 changed files with 193 additions and 1 deletions

View file

@ -48,6 +48,7 @@ static bool is_platform_object(Type const& type)
"DocumentType"sv,
"DOMRectReadOnly"sv,
"DynamicsCompressorNode"sv,
"ElementInternals"sv,
"EventTarget"sv,
"FileList"sv,
"FontFace"sv,

View file

@ -30,6 +30,7 @@ source_set("HTML") {
"Dates.cpp",
"DecodedImageData.cpp",
"DocumentState.cpp",
"ElementInternals.cpp",
"ErrorEvent.cpp",
"EventHandler.cpp",
"EventNames.cpp",

View file

@ -119,6 +119,7 @@ standard_idl_files = [
"//Userland/Libraries/LibWeb/HTML/DataTransfer.idl",
"//Userland/Libraries/LibWeb/HTML/DOMParser.idl",
"//Userland/Libraries/LibWeb/HTML/DOMStringMap.idl",
"//Userland/Libraries/LibWeb/HTML/ElementInternals.idl",
"//Userland/Libraries/LibWeb/HTML/ErrorEvent.idl",
"//Userland/Libraries/LibWeb/HTML/EventSource.idl",
"//Userland/Libraries/LibWeb/HTML/FormDataEvent.idl",

View file

@ -273,6 +273,7 @@ set(SOURCES
HTML/DOMParser.cpp
HTML/DOMStringMap.cpp
HTML/DataTransfer.cpp
HTML/ElementInternals.cpp
HTML/ErrorEvent.cpp
HTML/EventHandler.cpp
HTML/EventSource.cpp

View file

@ -405,6 +405,8 @@ protected:
virtual bool id_reference_exists(String const&) const override;
CustomElementState custom_element_state() const { return m_custom_element_state; }
private:
void make_html_uppercased_qualified_name();

View file

@ -352,6 +352,7 @@ class DecodedImageData;
class DocumentState;
class DOMParser;
class DOMStringMap;
class ElementInternals;
class ErrorEvent;
class EventHandler;
class EventLoop;

View file

@ -0,0 +1,61 @@
/*
* Copyright (c) 2024, the Ladybird developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWeb/Bindings/ElementInternalsPrototype.h>
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/DOM/ShadowRoot.h>
#include <LibWeb/HTML/ElementInternals.h>
#include <LibWeb/HTML/HTMLElement.h>
namespace Web::HTML {
JS_DEFINE_ALLOCATOR(ElementInternals);
JS::NonnullGCPtr<ElementInternals> ElementInternals::create(JS::Realm& realm, HTMLElement& target_element)
{
return realm.heap().allocate<ElementInternals>(realm, realm, target_element);
}
ElementInternals::ElementInternals(JS::Realm& realm, HTMLElement& target_element)
: Bindings::PlatformObject(realm)
, m_target_element(target_element)
{
}
// https://html.spec.whatwg.org/#dom-elementinternals-shadowroot
JS::GCPtr<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;
}
void ElementInternals::initialize(JS::Realm& realm)
{
Base::initialize(realm);
WEB_SET_PROTOTYPE_FOR_INTERFACE(ElementInternals);
}
void ElementInternals::visit_edges(JS::Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_target_element);
}
}

View file

@ -0,0 +1,36 @@
/*
* Copyright (c) 2024, the Ladybird developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/WeakPtr.h>
#include <LibJS/Heap/GCPtr.h>
#include <LibWeb/Bindings/PlatformObject.h>
#include <LibWeb/Forward.h>
namespace Web::HTML {
// https://html.spec.whatwg.org/multipage/custom-elements.html#elementinternals
class ElementInternals final : public Bindings::PlatformObject {
WEB_PLATFORM_OBJECT(ElementInternals, Bindings::PlatformObject);
JS_DECLARE_ALLOCATOR(ElementInternals);
public:
static JS::NonnullGCPtr<ElementInternals> create(JS::Realm&, HTMLElement& target_element);
JS::GCPtr<DOM::ShadowRoot> shadow_root() const;
private:
explicit ElementInternals(JS::Realm&, HTMLElement& target_element);
virtual void initialize(JS::Realm&) override;
virtual void visit_edges(JS::Cell::Visitor& visitor) override;
// https://html.spec.whatwg.org/multipage/custom-elements.html#internals-target
JS::NonnullGCPtr<HTMLElement> m_target_element;
};
}

View file

@ -0,0 +1,44 @@
#import <DOM/ShadowRoot.idl>
// https://html.spec.whatwg.org/multipage/custom-elements.html#elementinternals
[Exposed=Window]
interface ElementInternals {
// Shadow root access
readonly attribute ShadowRoot? shadowRoot;
// Form-associated custom elements
[FIXME] undefined setFormValue((File or USVString or FormData)? value,
optional (File or USVString or FormData)? state);
[FIXME] readonly attribute HTMLFormElement? form;
[FIXME] undefined setValidity(optional ValidityStateFlags flags = {},
optional DOMString message,
optional HTMLElement anchor);
[FIXME] readonly attribute boolean willValidate;
[FIXME] readonly attribute ValidityState validity;
[FIXME] readonly attribute DOMString validationMessage;
[FIXME] boolean checkValidity();
[FIXME] boolean reportValidity();
[FIXME] readonly attribute NodeList labels;
// Custom state pseudo-class
[FIXME, SameObject] readonly attribute CustomStateSet states;
};
// Accessibility semantics
// ElementInternals includes ARIAMixin;
dictionary ValidityStateFlags {
boolean valueMissing = false;
boolean typeMismatch = false;
boolean patternMismatch = false;
boolean tooLong = false;
boolean tooShort = false;
boolean rangeUnderflow = false;
boolean rangeOverflow = false;
boolean stepMismatch = false;
boolean badInput = false;
boolean customError = false;
};

View file

@ -13,7 +13,9 @@
#include <LibWeb/DOM/LiveNodeList.h>
#include <LibWeb/DOM/ShadowRoot.h>
#include <LibWeb/HTML/BrowsingContext.h>
#include <LibWeb/HTML/CustomElements/CustomElementDefinition.h>
#include <LibWeb/HTML/DOMStringMap.h>
#include <LibWeb/HTML/ElementInternals.h>
#include <LibWeb/HTML/EventHandler.h>
#include <LibWeb/HTML/Focus.h>
#include <LibWeb/HTML/HTMLAnchorElement.h>
@ -60,6 +62,7 @@ void HTMLElement::visit_edges(Cell::Visitor& visitor)
Base::visit_edges(visitor);
visitor.visit(m_dataset);
visitor.visit(m_labels);
visitor.visit(m_attached_internals);
}
JS::NonnullGCPtr<DOMStringMap> HTMLElement::dataset()
@ -608,6 +611,40 @@ TokenizedFeature::NoOpener HTMLElement::get_an_elements_noopener(StringView targ
return TokenizedFeature::NoOpener::No;
}
WebIDL::ExceptionOr<JS::NonnullGCPtr<ElementInternals>> HTMLElement::attach_internals()
{
// 1. If this's is value is not null, then throw a "NotSupportedError" DOMException.
if (is_value().has_value())
return WebIDL::NotSupportedError::create(realm(), "ElementInternals cannot be attached to a customized build-in element"_fly_string);
// 2. Let definition be the result of looking up a custom element definition given this's node document, its namespace, its local name, and null as the is value.
auto definition = document().lookup_custom_element_definition(namespace_uri(), local_name(), is_value());
// 3. If definition is null, then throw an "NotSupportedError" DOMException.
if (!definition)
return WebIDL::NotSupportedError::create(realm(), "ElementInternals cannot be attached to an element that is not a custom element"_fly_string);
// 4. If definition's disable internals is true, then throw a "NotSupportedError" DOMException.
if (definition->disable_internals())
return WebIDL::NotSupportedError::create(realm(), "ElementInternals are disabled for this custom element"_fly_string);
// 5. If this's attached internals is non-null, then throw an "NotSupportedError" DOMException.
if (m_attached_internals)
return WebIDL::NotSupportedError::create(realm(), "ElementInternals already attached"_fly_string);
// 6. If this's custom element state is not "precustomized" or "custom", then throw a "NotSupportedError" DOMException.
if (!first_is_one_of(custom_element_state(), DOM::CustomElementState::Precustomized, DOM::CustomElementState::Custom))
return WebIDL::NotSupportedError::create(realm(), "Custom element is in an invalid state to attach ElementInternals"_fly_string);
// 7. Set this's attached internals to a new ElementInternals instance whose target element is this.
auto internals = ElementInternals::create(realm(), *this);
m_attached_internals = internals;
// 8. Return this's attached internals.
return { internals };
}
void HTMLElement::did_receive_focus()
{
if (m_content_editable_state != ContentEditableState::True)

View file

@ -75,6 +75,8 @@ public:
String get_an_elements_target() const;
TokenizedFeature::NoOpener get_an_elements_noopener(StringView target) const;
WebIDL::ExceptionOr<JS::NonnullGCPtr<ElementInternals>> attach_internals();
protected:
HTMLElement(DOM::Document&, DOM::QualifiedName);
@ -97,6 +99,9 @@ private:
JS::GCPtr<DOM::NodeList> m_labels;
// https://html.spec.whatwg.org/multipage/custom-elements.html#attached-internals
JS::GCPtr<ElementInternals> m_attached_internals;
enum class ContentEditableState {
True,
False,

View file

@ -1,5 +1,6 @@
#import <CSS/ElementCSSInlineStyle.idl>
#import <HTML/DOMStringMap.idl>
#import <HTML/ElementInternals.idl>
#import <DOM/Element.idl>
#import <DOM/EventHandler.idl>
@ -28,7 +29,7 @@ interface HTMLElement : Element {
[LegacyNullToEmptyString, CEReactions] attribute DOMString innerText;
[LegacyNullToEmptyString, CEReactions] attribute DOMString outerText;
[FIXME] ElementInternals attachInternals();
ElementInternals attachInternals();
// The popover API
[FIXME] undefined showPopover();

View file

@ -103,6 +103,7 @@ libweb_js_bindings(HTML/CustomElements/CustomElementRegistry)
libweb_js_bindings(HTML/DOMParser)
libweb_js_bindings(HTML/DOMStringMap)
libweb_js_bindings(HTML/DataTransfer)
libweb_js_bindings(HTML/ElementInternals)
libweb_js_bindings(HTML/ErrorEvent)
libweb_js_bindings(HTML/EventSource)
libweb_js_bindings(HTML/FormDataEvent)