LibWeb/DOM: Implement DocumentOrShadowRoot.customElementRegistry

Step towards scoped custom element registries.
This commit is contained in:
Sam Atkins 2025-04-23 13:02:04 +01:00 committed by Tim Ledbetter
parent fe3c6a1f9f
commit ecbd846272
11 changed files with 66 additions and 14 deletions

View file

@ -404,7 +404,9 @@ WebIDL::ExceptionOr<GC::Ref<Document>> Document::create_and_initialize(Type type
// current document readiness: "loading"
// about base URL: navigationParams's about base URL
// allow declarative shadow roots: true
auto document = HTML::HTMLDocument::create(window->realm());
// custom element registry: A new CustomElementRegistry object.
auto& realm = window->realm();
auto document = HTML::HTMLDocument::create(realm);
document->m_type = type;
document->m_content_type = move(content_type);
document->set_origin(navigation_params.origin);
@ -417,6 +419,7 @@ WebIDL::ExceptionOr<GC::Ref<Document>> Document::create_and_initialize(Type type
document->m_readiness = HTML::DocumentReadyState::Loading;
document->m_about_base_url = navigation_params.about_base_url;
document->set_allow_declarative_shadow_roots(true);
document->set_custom_element_registry(realm.create<HTML::CustomElementRegistry>(realm));
document->m_window = window;
@ -679,6 +682,7 @@ void Document::visit_edges(Cell::Visitor& visitor)
visitor.visit(m_render_blocking_elements);
visitor.visit(m_policy_container);
visitor.visit(m_style_invalidator);
visitor.visit(m_custom_element_registry);
}
// https://w3c.github.io/selection-api/#dom-document-getselection
@ -7586,6 +7590,17 @@ void Document::view_transition_page_visibility_change_steps()
}));
}
// https://dom.spec.whatwg.org/#dom-documentorshadowroot-customelementregistry
GC::Ptr<HTML::CustomElementRegistry> Document::custom_element_registry() const
{
// 1. If this is a document, then return thiss custom element registry.
// NB: Always true.
return m_custom_element_registry;
// 2. Assert: this is a ShadowRoot node.
// 3. Return thiss custom element registry.
}
ElementByIdMap& Document::element_by_id() const
{
if (!m_element_by_id)

View file

@ -1035,6 +1035,9 @@ public:
Optional<CSS::SelectorList> const* cached_query_selector_result(String const& selector_text) const;
void cache_query_selector_result(String selector_text, Optional<CSS::SelectorList>);
GC::Ptr<HTML::CustomElementRegistry> custom_element_registry() const;
void set_custom_element_registry(GC::Ptr<HTML::CustomElementRegistry> custom_element_registry) { m_custom_element_registry = custom_element_registry; }
protected:
virtual void initialize(JS::Realm&) override;
virtual void visit_edges(Cell::Visitor&) override;
@ -1464,6 +1467,9 @@ private:
// https://fullscreen.spec.whatwg.org/#list-of-pending-fullscreen-events
Vector<PendingFullscreenEvent> m_pending_fullscreen_events;
// https://dom.spec.whatwg.org/#document-custom-element-registry
GC::Ptr<HTML::CustomElementRegistry> m_custom_element_registry;
};
template<>

View file

@ -3,6 +3,8 @@
// https://dom.spec.whatwg.org/#documentorshadowroot
interface mixin DocumentOrShadowRoot {
readonly attribute CustomElementRegistry? customElementRegistry;
// https://html.spec.whatwg.org/multipage/interaction.html#dom-documentorshadowroot-activeelement
readonly attribute Element? activeElement;

View file

@ -1499,7 +1499,7 @@ WebIDL::ExceptionOr<GC::Ref<Node>> Node::clone_single_node(Document& document) c
document_copy->set_document_type(document_.document_type());
document_copy->set_quirks_mode(document_.mode());
document_copy->set_allow_declarative_shadow_roots(document_.allow_declarative_shadow_roots());
// FIXME: Custom element registry.
document_copy->set_custom_element_registry(document_.custom_element_registry().ptr());
copy = move(document_copy);
} else if (is_document_type()) {
// -> DocumentType

View file

@ -14,6 +14,7 @@
#include <LibWeb/DOM/ShadowRoot.h>
#include <LibWeb/DOM/SlotRegistry.h>
#include <LibWeb/DOM/Utils.h>
#include <LibWeb/HTML/CustomElements/CustomElementRegistry.h>
#include <LibWeb/HTML/HTMLSlotElement.h>
#include <LibWeb/HTML/HTMLTemplateElement.h>
#include <LibWeb/HTML/Parser/HTMLParser.h>
@ -192,6 +193,7 @@ void ShadowRoot::visit_edges(Visitor& visitor)
for (auto const& element : elements)
element.visit(visitor);
}
visitor.visit(m_custom_element_registry);
}
GC::Ref<WebIDL::ObservableArray> ShadowRoot::adopted_style_sheets() const
@ -323,4 +325,15 @@ void ShadowRoot::calculate_part_element_map()
});
}
// https://dom.spec.whatwg.org/#dom-documentorshadowroot-customelementregistry
GC::Ptr<HTML::CustomElementRegistry> ShadowRoot::custom_element_registry() const
{
// 1. If this is a document, then return thiss custom element registry.
// NB: Impossible.
// 2. Assert: this is a ShadowRoot node.
// 3. Return thiss custom element registry.
return m_custom_element_registry;
}
}

View file

@ -97,6 +97,9 @@ public:
using PartElementMap = HashMap<FlyString, OrderedHashTable<AbstractElement>>;
PartElementMap const& part_element_map() const;
GC::Ptr<HTML::CustomElementRegistry> custom_element_registry() const;
void set_custom_element_registry(GC::Ptr<HTML::CustomElementRegistry> registry) { m_custom_element_registry = registry; }
virtual void finalize() override;
GC::Ptr<Element> fullscreen_element_for_bindings() const;
@ -145,6 +148,9 @@ private:
mutable PartElementMap m_part_element_map;
mutable u64 m_dom_tree_version_when_calculated_part_element_map { 0 };
// https://dom.spec.whatwg.org/#shadowroot-custom-element-registry
GC::Ptr<HTML::CustomElementRegistry> m_custom_element_registry;
public:
using DocumentShadowRootList = IntrusiveList<&ShadowRoot::m_list_node>;
};

View file

@ -12,6 +12,7 @@
#include <LibWeb/DOM/Range.h>
#include <LibWeb/HTML/BrowsingContext.h>
#include <LibWeb/HTML/BrowsingContextGroup.h>
#include <LibWeb/HTML/CustomElements/CustomElementRegistry.h>
#include <LibWeb/HTML/HTMLDocument.h>
#include <LibWeb/HTML/HTMLIFrameElement.h>
#include <LibWeb/HTML/HTMLInputElement.h>
@ -184,6 +185,8 @@ WebIDL::ExceptionOr<BrowsingContext::BrowsingContextAndDocument> BrowsingContext
return browsing_context->window_proxy();
});
auto& realm = window->realm();
// 11. Let topLevelCreationURL be about:blank if embedder is null; otherwise embedder's relevant settings object's top-level creation URL.
auto top_level_creation_url = !embedder ? URL::about_blank() : relevant_settings_object(*embedder).top_level_creation_url.value();
@ -204,10 +207,10 @@ WebIDL::ExceptionOr<BrowsingContext::BrowsingContextAndDocument> BrowsingContext
auto load_timing_info = DOM::DocumentLoadTimingInfo();
load_timing_info.navigation_start_time = HighResolutionTime::coarsen_time(
unsafe_context_creation_time,
as<WindowEnvironmentSettingsObject>(Bindings::principal_host_defined_environment_settings_object(window->realm())).cross_origin_isolated_capability());
as<WindowEnvironmentSettingsObject>(Bindings::principal_host_defined_environment_settings_object(realm)).cross_origin_isolated_capability());
// 15. Let document be a new Document, with:
auto document = HTML::HTMLDocument::create(window->realm());
auto document = HTML::HTMLDocument::create(realm);
// Non-standard
window->set_associated_document(*document);
@ -246,6 +249,9 @@ WebIDL::ExceptionOr<BrowsingContext::BrowsingContextAndDocument> BrowsingContext
// allow declarative shadow roots: true
document->set_allow_declarative_shadow_roots(true);
// custom element registry: A new CustomElementRegistry object.
document->set_custom_element_registry(realm.create<CustomElementRegistry>(realm));
// 16. If creator is non-null, then:
if (creator) {
// 1. Set document's referrer to the serialization of creator's URL.

View file

@ -65,6 +65,7 @@ static bool is_platform_object(Type const& type)
"CSSUnitValue"sv,
"CSSUnparsedValue"sv,
"CSSVariableReferenceValue"sv,
"CustomElementRegistry"sv,
"CustomStateSet"sv,
"DataTransfer"sv,
"Document"sv,

View file

@ -2,8 +2,9 @@ Harness status: OK
Found 4 tests
4 Fail
1 Pass
3 Fail
Fail customElementRegistry on a document should return window.customElements by default
Fail customElementRegistry on a document without a browsing context should return null
Pass customElementRegistry on a document without a browsing context should return null
Fail customElementRegistry on a document of a connected iframe should return contentWindow.customElements
Fail customElementRegistry on a document of a disconnected iframe should return contentWindow.customElements

View file

@ -2,7 +2,8 @@ Harness status: OK
Found 12 tests
12 Fail
2 Pass
10 Fail
Fail Cloning with global registry
Fail Cloning with explicit global registry
Fail Cloning with scoped registry
@ -12,6 +13,6 @@ Fail Cloning including shadow tree with scoped registry
Fail Cloning with global registry (null registry target)
Fail Cloning with explicit global registry (null registry target)
Fail Cloning with scoped registry (null registry target)
Fail Cloning including shadow tree with global registry (null registry target)
Fail Cloning including shadow tree with explicit global registry (null registry target)
Pass Cloning including shadow tree with global registry (null registry target)
Pass Cloning including shadow tree with explicit global registry (null registry target)
Fail Cloning including shadow tree with scoped registry (null registry target)

View file

@ -2,14 +2,15 @@ Harness status: OK
Found 10 tests
10 Fail
4 Pass
6 Fail
Fail A newly attached disconnected ShadowRoot should use the global registry by default
Fail A newly attached connected ShadowRoot should use the global registry by default
Fail A newly attached disconnected ShadowRoot should use the scoped registry if explicitly specified in attachShadow
Fail A newly attached connected ShadowRoot should use the scoped registry if explicitly specified in attachShadow
Fail attachShadow() should use null registry when customElementRegistry is null (host uses global registry)
Pass attachShadow() should use null registry when customElementRegistry is null (host uses global registry)
Fail attachShadow() should use null registry when customElementRegistry is null (host uses custom registry)
Fail attchShadow on a builtin element with null customElementRegistry should create a ShadowRoot with null registry
Fail attchShadow on a custom elememnt candidate with null customElementRegistry should create a ShadowRoot with null registry
Fail attchShadow on a custom elememnt with null customElementRegistry should create a ShadowRoot with null registry
Pass attchShadow on a builtin element with null customElementRegistry should create a ShadowRoot with null registry
Pass attchShadow on a custom elememnt candidate with null customElementRegistry should create a ShadowRoot with null registry
Pass attchShadow on a custom elememnt with null customElementRegistry should create a ShadowRoot with null registry
Fail attachShadow() should use the null registry when the shadow host uses null registry and customElementRegistry is null