From b602ee7dddb215a171aeae3f79ff872169073d28 Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Tue, 5 Sep 2023 15:08:35 -0400 Subject: [PATCH] LibWeb: Implement automatic slottable assignment This implements automatic slottable assignment by way of hooking into the element attribute change steps. When the `name` attribute of a slot or the `slot` attribute of a slottable changes, assignment is performed. --- Userland/Libraries/LibWeb/DOM/Element.cpp | 32 +++++++++++++++++++ Userland/Libraries/LibWeb/DOM/Slottable.cpp | 30 ++++++++++++++--- .../Libraries/LibWeb/HTML/HTMLSlotElement.cpp | 27 ++++++++++++++++ 3 files changed, 85 insertions(+), 4 deletions(-) diff --git a/Userland/Libraries/LibWeb/DOM/Element.cpp b/Userland/Libraries/LibWeb/DOM/Element.cpp index 5334e4afaa..f82716945a 100644 --- a/Userland/Libraries/LibWeb/DOM/Element.cpp +++ b/Userland/Libraries/LibWeb/DOM/Element.cpp @@ -71,6 +71,38 @@ Element::Element(Document& document, DOM::QualifiedName qualified_name) , m_qualified_name(move(qualified_name)) { make_html_uppercased_qualified_name(); + + // https://dom.spec.whatwg.org/#ref-for-concept-element-attributes-change-ext① + add_attribute_change_steps([this](auto const& local_name, auto const& old_value, auto const& value, auto const& namespace_) { + // 1. If localName is slot and namespace is null, then: + if (local_name == HTML::AttributeNames::slot && namespace_.is_null()) { + // 1. If value is oldValue, then return. + if (value == old_value) + return; + + // 2. If value is null and oldValue is the empty string, then return. + if (value.is_null() && old_value == DeprecatedString::empty()) + return; + + // 3. If value is the empty string and oldValue is null, then return. + if (value == DeprecatedString::empty() && old_value.is_null()) + return; + + // 4. If value is null or the empty string, then set element’s name to the empty string. + if (value.is_empty()) + set_slottable_name({}); + // 5. Otherwise, set element’s name to value. + else + set_slottable_name(MUST(String::from_deprecated_string(value))); + + // 6. If element is assigned, then run assign slottables for element’s assigned slot. + if (auto assigned_slot = assigned_slot_internal()) + assign_slottables(*assigned_slot); + + // 7. Run assign a slot for element. + assign_a_slot(JS::NonnullGCPtr { *this }); + } + }); } Element::~Element() = default; diff --git a/Userland/Libraries/LibWeb/DOM/Slottable.cpp b/Userland/Libraries/LibWeb/DOM/Slottable.cpp index 4429db0e40..50b4edcb88 100644 --- a/Userland/Libraries/LibWeb/DOM/Slottable.cpp +++ b/Userland/Libraries/LibWeb/DOM/Slottable.cpp @@ -84,8 +84,19 @@ JS::GCPtr find_a_slot(Slottable const& slottable, OpenFla return slot; } - // FIXME: 6. Return the first slot in tree order in shadow’s descendants whose name is slottable’s name, if any; otherwise null. - return nullptr; + // 6. Return the first slot in tree order in shadow’s descendants whose name is slottable’s name, if any; otherwise null. + auto const& slottable_name = slottable.visit([](auto const& node) { return node->slottable_name(); }); + JS::GCPtr slot; + + shadow->for_each_in_subtree_of_type([&](auto& child) { + if (child.slot_name() != slottable_name) + return IterationDecision::Continue; + + slot = child; + return IterationDecision::Break; + }); + + return slot; } // https://dom.spec.whatwg.org/#find-slotables @@ -118,8 +129,19 @@ Vector find_slottables(JS::NonnullGCPtr slot) } // 6. Otherwise, for each slottable child slottable of host, in tree order: else { - // FIXME: 1. Let foundSlot be the result of finding a slot given slottable. - // FIXME: 2. If foundSlot is slot, then append slottable to result. + host->for_each_child([&](auto& node) { + if (!node.is_slottable()) + return; + + auto slottable = node.as_slottable(); + + // 1. Let foundSlot be the result of finding a slot given slottable. + auto found_slot = find_a_slot(slottable); + + // 2. If foundSlot is slot, then append slottable to result. + if (found_slot == slot) + result.append(move(slottable)); + }); } // 7. Return result. diff --git a/Userland/Libraries/LibWeb/HTML/HTMLSlotElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLSlotElement.cpp index 7a6bc694ba..e357744bc0 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLSlotElement.cpp +++ b/Userland/Libraries/LibWeb/HTML/HTMLSlotElement.cpp @@ -15,6 +15,33 @@ namespace Web::HTML { HTMLSlotElement::HTMLSlotElement(DOM::Document& document, DOM::QualifiedName qualified_name) : HTMLElement(document, move(qualified_name)) { + // https://dom.spec.whatwg.org/#ref-for-concept-element-attributes-change-ext + add_attribute_change_steps([this](auto const& local_name, auto const& old_value, auto const& value, auto const& namespace_) { + // 1. If element is a slot, localName is name, and namespace is null, then: + if (local_name == AttributeNames::name && namespace_.is_null()) { + // 1. If value is oldValue, then return. + if (value == old_value) + return; + + // 2. If value is null and oldValue is the empty string, then return. + if (value.is_null() && old_value == DeprecatedString::empty()) + return; + + // 3. If value is the empty string and oldValue is null, then return. + if (value == DeprecatedString::empty() && old_value.is_null()) + return; + + // 4. If value is null or the empty string, then set element’s name to the empty string. + if (value.is_empty()) + set_slot_name({}); + // 5. Otherwise, set element’s name to value. + else + set_slot_name(MUST(String::from_deprecated_string(value))); + + // 6. Run assign slottables for a tree with element’s root. + DOM::assign_slottables_for_a_tree(root()); + } + }); } HTMLSlotElement::~HTMLSlotElement() = default;