| 
									
										
										
										
											2020-08-01 03:04:26 +01:00
										 |  |  | /*
 | 
					
						
							| 
									
										
										
										
											2021-04-28 22:46:44 +02:00
										 |  |  |  * Copyright (c) 2020, the SerenityOS developers. | 
					
						
							| 
									
										
										
										
											2025-01-15 14:39:05 -05:00
										 |  |  |  * Copyright (c) 2023-2025, Tim Flynn <trflynn89@ladybird.org> | 
					
						
							| 
									
										
										
										
											2020-08-01 03:04:26 +01:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2021-04-22 01:24:48 -07:00
										 |  |  |  * SPDX-License-Identifier: BSD-2-Clause | 
					
						
							| 
									
										
										
										
											2020-08-01 03:04:26 +01:00
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-27 12:09:58 +12:00
										 |  |  | #include <LibWeb/Bindings/HTMLDetailsElementPrototype.h>
 | 
					
						
							| 
									
										
										
										
											2022-09-30 17:16:16 -06:00
										 |  |  | #include <LibWeb/Bindings/Intrinsics.h>
 | 
					
						
							| 
									
										
										
										
											2023-09-05 15:24:20 -04:00
										 |  |  | #include <LibWeb/DOM/ElementFactory.h>
 | 
					
						
							| 
									
										
										
										
											2023-06-02 21:44:03 +02:00
										 |  |  | #include <LibWeb/DOM/Event.h>
 | 
					
						
							| 
									
										
										
										
											2023-09-05 15:24:20 -04:00
										 |  |  | #include <LibWeb/DOM/ShadowRoot.h>
 | 
					
						
							|  |  |  | #include <LibWeb/DOM/Text.h>
 | 
					
						
							| 
									
										
										
										
											2023-08-31 13:53:17 -04:00
										 |  |  | #include <LibWeb/HTML/EventLoop/TaskQueue.h>
 | 
					
						
							| 
									
										
										
										
											2020-08-01 03:04:26 +01:00
										 |  |  | #include <LibWeb/HTML/HTMLDetailsElement.h>
 | 
					
						
							| 
									
										
										
										
											2023-09-05 15:24:20 -04:00
										 |  |  | #include <LibWeb/HTML/HTMLSlotElement.h>
 | 
					
						
							| 
									
										
										
										
											2023-06-02 21:44:03 +02:00
										 |  |  | #include <LibWeb/HTML/HTMLSummaryElement.h>
 | 
					
						
							| 
									
										
										
										
											2023-08-31 13:53:17 -04:00
										 |  |  | #include <LibWeb/HTML/ToggleEvent.h>
 | 
					
						
							| 
									
										
										
										
											2025-05-09 10:35:07 -04:00
										 |  |  | #include <LibWeb/Layout/Node.h>
 | 
					
						
							| 
									
										
										
										
											2023-09-05 15:24:20 -04:00
										 |  |  | #include <LibWeb/Namespace.h>
 | 
					
						
							| 
									
										
										
										
											2020-08-01 03:04:26 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | namespace Web::HTML { | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-15 04:01:23 +13:00
										 |  |  | GC_DEFINE_ALLOCATOR(HTMLDetailsElement); | 
					
						
							| 
									
										
										
										
											2023-11-19 19:47:52 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-18 21:00:52 +01:00
										 |  |  | HTMLDetailsElement::HTMLDetailsElement(DOM::Document& document, DOM::QualifiedName qualified_name) | 
					
						
							| 
									
										
										
										
											2021-02-07 11:20:15 +01:00
										 |  |  |     : HTMLElement(document, move(qualified_name)) | 
					
						
							| 
									
										
										
										
											2020-08-01 03:04:26 +01:00
										 |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-14 13:21:51 -06:00
										 |  |  | HTMLDetailsElement::~HTMLDetailsElement() = default; | 
					
						
							| 
									
										
										
										
											2023-01-10 06:28:20 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-05 15:24:20 -04:00
										 |  |  | void HTMLDetailsElement::visit_edges(Cell::Visitor& visitor) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     Base::visit_edges(visitor); | 
					
						
							|  |  |  |     visitor.visit(m_summary_slot); | 
					
						
							|  |  |  |     visitor.visit(m_descendants_slot); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-31 13:53:17 -04:00
										 |  |  | void HTMLDetailsElement::initialize(JS::Realm& realm) | 
					
						
							| 
									
										
										
										
											2023-06-02 21:44:03 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2024-03-16 13:13:08 +01:00
										 |  |  |     WEB_SET_PROTOTYPE_FOR_INTERFACE(HTMLDetailsElement); | 
					
						
							| 
									
										
										
										
											2025-04-20 16:22:57 +02:00
										 |  |  |     Base::initialize(realm); | 
					
						
							| 
									
										
										
										
											2023-11-29 18:22:16 -05:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-15 14:39:05 -05:00
										 |  |  | // https://html.spec.whatwg.org/multipage/interactive-elements.html#the-details-element:html-element-insertion-steps
 | 
					
						
							| 
									
										
										
										
											2023-11-29 18:22:16 -05:00
										 |  |  | void HTMLDetailsElement::inserted() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2025-01-23 17:38:24 +01:00
										 |  |  |     Base::inserted(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-15 14:39:05 -05:00
										 |  |  |     // 1. Ensure details exclusivity by closing the given element if needed given insertedNode.
 | 
					
						
							|  |  |  |     ensure_details_exclusivity_by_closing_the_given_element_if_needed(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-29 18:22:16 -05:00
										 |  |  |     create_shadow_tree_if_needed().release_value_but_fixme_should_propagate_errors(); | 
					
						
							|  |  |  |     update_shadow_tree_slots(); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2023-09-05 15:24:20 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-15 11:19:12 -05:00
										 |  |  | // https://html.spec.whatwg.org/multipage/interactive-elements.html#the-details-element:concept-element-attributes-change-ext
 | 
					
						
							|  |  |  | void HTMLDetailsElement::attribute_changed(FlyString const& local_name, Optional<String> const& old_value, Optional<String> const& value, Optional<FlyString> const& namespace_) | 
					
						
							| 
									
										
										
										
											2023-06-02 21:44:03 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2025-01-15 11:19:12 -05:00
										 |  |  |     Base::attribute_changed(local_name, old_value, value, namespace_); | 
					
						
							| 
									
										
										
										
											2023-06-02 21:44:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-15 11:19:12 -05:00
										 |  |  |     // 1. If namespace is not null, then return.
 | 
					
						
							|  |  |  |     if (namespace_.has_value()) | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // 2. If localName is name, then ensure details exclusivity by closing the given element if needed given element.
 | 
					
						
							|  |  |  |     if (local_name == HTML::AttributeNames::name) { | 
					
						
							| 
									
										
										
										
											2025-01-15 14:39:05 -05:00
										 |  |  |         ensure_details_exclusivity_by_closing_the_given_element_if_needed(); | 
					
						
							|  |  |  |         update_shadow_tree_style(); | 
					
						
							| 
									
										
										
										
											2025-01-15 11:19:12 -05:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // 3. If localName is open, then:
 | 
					
						
							|  |  |  |     else if (local_name == HTML::AttributeNames::open) { | 
					
						
							|  |  |  |         // 1. If one of oldValue or value is null and the other is not null, run the following steps, which are known as
 | 
					
						
							|  |  |  |         //    the details notification task steps, for this details element:
 | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             // 1. If oldValue is null, queue a details toggle event task given the details element, "closed", and "open".
 | 
					
						
							|  |  |  |             if (!old_value.has_value()) { | 
					
						
							|  |  |  |                 queue_a_details_toggle_event_task("closed"_string, "open"_string); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             // 2. Otherwise, queue a details toggle event task given the details element, "open", and "closed".
 | 
					
						
							|  |  |  |             else { | 
					
						
							|  |  |  |                 queue_a_details_toggle_event_task("open"_string, "closed"_string); | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2023-08-31 13:53:17 -04:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-01-15 11:19:12 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |         // 2. If oldValue is null and value is not null, then ensure details exclusivity by closing other elements if
 | 
					
						
							|  |  |  |         //    needed given element.
 | 
					
						
							|  |  |  |         if (!old_value.has_value() && value.has_value()) { | 
					
						
							| 
									
										
										
										
											2025-01-15 14:39:05 -05:00
										 |  |  |             ensure_details_exclusivity_by_closing_other_elements_if_needed(); | 
					
						
							| 
									
										
										
										
											2023-08-31 13:53:17 -04:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-09-05 15:24:20 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |         update_shadow_tree_style(); | 
					
						
							| 
									
										
										
										
											2023-08-31 13:53:17 -04:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-06-02 21:44:03 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-27 01:16:33 +13:00
										 |  |  | void HTMLDetailsElement::children_changed(ChildrenChangedMetadata const* metadata) | 
					
						
							| 
									
										
										
										
											2023-09-05 15:24:20 -04:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2025-01-27 01:16:33 +13:00
										 |  |  |     Base::children_changed(metadata); | 
					
						
							| 
									
										
										
										
											2023-09-05 15:24:20 -04:00
										 |  |  |     update_shadow_tree_slots(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-31 13:53:17 -04:00
										 |  |  | // https://html.spec.whatwg.org/multipage/interactive-elements.html#queue-a-details-toggle-event-task
 | 
					
						
							|  |  |  | void HTMLDetailsElement::queue_a_details_toggle_event_task(String old_state, String new_state) | 
					
						
							| 
									
										
										
										
											2023-06-02 21:44:03 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2023-08-31 13:53:17 -04:00
										 |  |  |     // 1. If element's details toggle task tracker is not null, then:
 | 
					
						
							|  |  |  |     if (m_details_toggle_task_tracker.has_value()) { | 
					
						
							|  |  |  |         // 1. Set oldState to element's details toggle task tracker's old state.
 | 
					
						
							|  |  |  |         old_state = move(m_details_toggle_task_tracker->old_state); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // 2. Remove element's details toggle task tracker's task from its task queue.
 | 
					
						
							|  |  |  |         HTML::main_thread_event_loop().task_queue().remove_tasks_matching([&](auto const& task) { | 
					
						
							|  |  |  |             return task.id() == m_details_toggle_task_tracker->task_id; | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // 3. Set element's details toggle task tracker to null.
 | 
					
						
							|  |  |  |         m_details_toggle_task_tracker->task_id = {}; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // 2. Queue an element task given the DOM manipulation task source and element to run the following steps:
 | 
					
						
							|  |  |  |     auto task_id = queue_an_element_task(HTML::Task::Source::DOMManipulation, [this, old_state, new_state = move(new_state)]() mutable { | 
					
						
							|  |  |  |         // 1. Fire an event named toggle at element, using ToggleEvent, with the oldState attribute initialized to
 | 
					
						
							|  |  |  |         //    oldState and the newState attribute initialized to newState.
 | 
					
						
							|  |  |  |         ToggleEventInit event_init {}; | 
					
						
							|  |  |  |         event_init.old_state = move(old_state); | 
					
						
							|  |  |  |         event_init.new_state = move(new_state); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         dispatch_event(ToggleEvent::create(realm(), HTML::EventNames::toggle, move(event_init))); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // 2. Set element's details toggle task tracker to null.
 | 
					
						
							|  |  |  |         m_details_toggle_task_tracker = {}; | 
					
						
							| 
									
										
										
										
											2023-06-02 21:44:03 +02:00
										 |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-31 13:53:17 -04:00
										 |  |  |     // 3. Set element's details toggle task tracker to a struct with task set to the just-queued task and old state set to oldState.
 | 
					
						
							|  |  |  |     m_details_toggle_task_tracker = ToggleTaskTracker { | 
					
						
							|  |  |  |         .task_id = task_id, | 
					
						
							|  |  |  |         .old_state = move(old_state), | 
					
						
							|  |  |  |     }; | 
					
						
							| 
									
										
										
										
											2023-01-10 06:28:20 -05:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-15 14:39:05 -05:00
										 |  |  | // https://html.spec.whatwg.org/multipage/interactive-elements.html#details-name-group
 | 
					
						
							|  |  |  | template<typename Callback> | 
					
						
							|  |  |  | void for_each_element_in_details_name_group(HTMLDetailsElement& details, FlyString const& name, Callback&& callback) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // The details name group that contains a details element a also contains all the other details elements b that
 | 
					
						
							|  |  |  |     // fulfill all of the following conditions:
 | 
					
						
							|  |  |  |     auto name_group_contains_element = [&](auto const& element) { | 
					
						
							|  |  |  |         // 1. Both a and b are in the same tree.
 | 
					
						
							|  |  |  |         // NOTE: This is true due to the way we iterate the tree below.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // 2. They both have a name attribute, their name attributes are not the empty string, and the value of a's name
 | 
					
						
							|  |  |  |         //    attribute equals the value of b's name attribute.
 | 
					
						
							|  |  |  |         return element.attribute(HTML::AttributeNames::name) == name; | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     details.root().for_each_in_inclusive_subtree_of_type<HTMLDetailsElement>([&](HTMLDetailsElement& candidate) { | 
					
						
							|  |  |  |         if (&details != &candidate && name_group_contains_element(candidate)) | 
					
						
							|  |  |  |             return callback(candidate); | 
					
						
							|  |  |  |         return TraversalDecision::Continue; | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // https://html.spec.whatwg.org/multipage/interactive-elements.html#ensure-details-exclusivity-by-closing-other-elements-if-needed
 | 
					
						
							|  |  |  | void HTMLDetailsElement::ensure_details_exclusivity_by_closing_other_elements_if_needed() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // 1. Assert: element has an open attribute.
 | 
					
						
							|  |  |  |     VERIFY(has_attribute(HTML::AttributeNames::open)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // 2. If element does not have a name attribute, or its name attribute is the empty string, then return.
 | 
					
						
							|  |  |  |     auto name = attribute(HTML::AttributeNames::name); | 
					
						
							|  |  |  |     if (!name.has_value() || name->is_empty()) | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // 3. Let groupMembers be a list of elements, containing all elements in element's details name group except for
 | 
					
						
							|  |  |  |     //    element, in tree order.
 | 
					
						
							|  |  |  |     // 4. For each element otherElement of groupMembers:
 | 
					
						
							|  |  |  |     for_each_element_in_details_name_group(*this, *name, [&](HTMLDetailsElement& other_element) { | 
					
						
							|  |  |  |         // 1. If the open attribute is set on otherElement, then:
 | 
					
						
							|  |  |  |         if (other_element.has_attribute(HTML::AttributeNames::open)) { | 
					
						
							|  |  |  |             // 1. Assert: otherElement is the only element in groupMembers that has the open attribute set.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // 2. Remove the open attribute on otherElement.
 | 
					
						
							|  |  |  |             other_element.remove_attribute(HTML::AttributeNames::open); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // 3. Break.
 | 
					
						
							|  |  |  |             return TraversalDecision::Break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return TraversalDecision::Continue; | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // https://html.spec.whatwg.org/multipage/interactive-elements.html#ensure-details-exclusivity-by-closing-the-given-element-if-needed
 | 
					
						
							|  |  |  | void HTMLDetailsElement::ensure_details_exclusivity_by_closing_the_given_element_if_needed() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // 1. If element does not have an open attribute, then return.
 | 
					
						
							|  |  |  |     if (!has_attribute(HTML::AttributeNames::open)) | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // 2. If element does not have a name attribute, or its name attribute is the empty string, then return.
 | 
					
						
							|  |  |  |     auto name = attribute(HTML::AttributeNames::name); | 
					
						
							|  |  |  |     if (!name.has_value() || name->is_empty()) | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // 3. Let groupMembers be a list of elements, containing all elements in element's details name group except for
 | 
					
						
							|  |  |  |     //    element, in tree order.
 | 
					
						
							|  |  |  |     // 4. For each element otherElement of groupMembers:
 | 
					
						
							|  |  |  |     for_each_element_in_details_name_group(*this, *name, [&](HTMLDetailsElement const& other_element) { | 
					
						
							|  |  |  |         // 1. If the open attribute is set on otherElement, then:
 | 
					
						
							|  |  |  |         if (other_element.has_attribute(HTML::AttributeNames::open)) { | 
					
						
							|  |  |  |             // 1. Remove the open attribute on element.
 | 
					
						
							|  |  |  |             remove_attribute(HTML::AttributeNames::open); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // 2. Break.
 | 
					
						
							|  |  |  |             return TraversalDecision::Break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return TraversalDecision::Continue; | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-04 13:01:46 +01:00
										 |  |  | // https://html.spec.whatwg.org/multipage/rendering.html#the-details-and-summary-elements
 | 
					
						
							| 
									
										
										
										
											2023-11-29 18:22:16 -05:00
										 |  |  | WebIDL::ExceptionOr<void> HTMLDetailsElement::create_shadow_tree_if_needed() | 
					
						
							| 
									
										
										
										
											2023-09-05 15:24:20 -04:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2024-06-25 11:28:58 +02:00
										 |  |  |     if (shadow_root()) | 
					
						
							| 
									
										
										
										
											2023-11-29 18:22:16 -05:00
										 |  |  |         return {}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     auto& realm = this->realm(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-30 23:46:26 +00:00
										 |  |  |     // The details element is expected to have an internal shadow tree with three child elements:
 | 
					
						
							| 
									
										
										
										
											2024-11-14 05:50:17 +13:00
										 |  |  |     auto shadow_root = realm.create<DOM::ShadowRoot>(document(), *this, Bindings::ShadowRootMode::Closed); | 
					
						
							| 
									
										
										
										
											2023-09-05 15:24:20 -04:00
										 |  |  |     shadow_root->set_slot_assignment(Bindings::SlotAssignmentMode::Manual); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-30 23:46:26 +00:00
										 |  |  |     // The first child element is a slot that is expected to take the details element's first summary element child, if any.
 | 
					
						
							| 
									
										
										
										
											2023-11-04 18:42:04 +01:00
										 |  |  |     auto summary_slot = TRY(DOM::create_element(document(), HTML::TagNames::slot, Namespace::HTML)); | 
					
						
							| 
									
										
										
										
											2023-09-05 15:24:20 -04:00
										 |  |  |     MUST(shadow_root->append_child(summary_slot)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-30 23:46:26 +00:00
										 |  |  |     // The second child element is a slot that is expected to take the details element's remaining descendants, if any.
 | 
					
						
							| 
									
										
										
										
											2023-11-04 18:42:04 +01:00
										 |  |  |     auto descendants_slot = TRY(DOM::create_element(document(), HTML::TagNames::slot, Namespace::HTML)); | 
					
						
							| 
									
										
										
										
											2025-03-20 16:56:46 +00:00
										 |  |  |     descendants_slot->set_use_pseudo_element(CSS::PseudoElement::DetailsContent); | 
					
						
							| 
									
										
										
										
											2023-09-05 15:24:20 -04:00
										 |  |  |     MUST(shadow_root->append_child(descendants_slot)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-30 23:46:26 +00:00
										 |  |  |     // The third child element is either a link or style element with the following styles for the default summary:
 | 
					
						
							|  |  |  |     auto style = TRY(DOM::create_element(document(), HTML::TagNames::style, Namespace::HTML)); | 
					
						
							|  |  |  |     style->set_text_content(R"~~~( | 
					
						
							|  |  |  |         :host summary { | 
					
						
							|  |  |  |             display: list-item; | 
					
						
							|  |  |  |             counter-increment: list-item 0; | 
					
						
							|  |  |  |             list-style: disclosure-closed inside; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         :host([open]) summary { | 
					
						
							|  |  |  |             list-style-type: disclosure-open; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     )~~~"_string); | 
					
						
							|  |  |  |     MUST(shadow_root->append_child(style)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-05 15:24:20 -04:00
										 |  |  |     m_summary_slot = static_cast<HTML::HTMLSlotElement&>(*summary_slot); | 
					
						
							|  |  |  |     m_descendants_slot = static_cast<HTML::HTMLSlotElement&>(*descendants_slot); | 
					
						
							|  |  |  |     set_shadow_root(shadow_root); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return {}; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void HTMLDetailsElement::update_shadow_tree_slots() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2024-06-25 11:28:58 +02:00
										 |  |  |     if (!shadow_root()) | 
					
						
							| 
									
										
										
										
											2023-11-29 18:22:16 -05:00
										 |  |  |         return; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-05 15:24:20 -04:00
										 |  |  |     Vector<HTMLSlotElement::SlottableHandle> summary_assignment; | 
					
						
							|  |  |  |     Vector<HTMLSlotElement::SlottableHandle> descendants_assignment; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     auto* summary = first_child_of_type<HTMLSummaryElement>(); | 
					
						
							|  |  |  |     if (summary != nullptr) | 
					
						
							| 
									
										
										
										
											2024-11-15 04:01:23 +13:00
										 |  |  |         summary_assignment.append(GC::make_root(static_cast<DOM::Element&>(*summary))); | 
					
						
							| 
									
										
										
										
											2023-09-05 15:24:20 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     for_each_in_subtree([&](auto& child) { | 
					
						
							|  |  |  |         if (&child == summary) | 
					
						
							| 
									
										
										
										
											2024-05-04 14:47:04 +01:00
										 |  |  |             return TraversalDecision::Continue; | 
					
						
							| 
									
										
										
										
											2023-09-05 15:24:20 -04:00
										 |  |  |         if (!child.is_slottable()) | 
					
						
							| 
									
										
										
										
											2024-05-04 14:47:04 +01:00
										 |  |  |             return TraversalDecision::Continue; | 
					
						
							| 
									
										
										
										
											2023-09-05 15:24:20 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |         child.as_slottable().visit([&](auto& node) { | 
					
						
							| 
									
										
										
										
											2024-11-15 04:01:23 +13:00
										 |  |  |             descendants_assignment.append(GC::make_root(node)); | 
					
						
							| 
									
										
										
										
											2023-09-05 15:24:20 -04:00
										 |  |  |         }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-04 14:47:04 +01:00
										 |  |  |         return TraversalDecision::Continue; | 
					
						
							| 
									
										
										
										
											2023-09-05 15:24:20 -04:00
										 |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     m_summary_slot->assign(move(summary_assignment)); | 
					
						
							|  |  |  |     m_descendants_slot->assign(move(descendants_assignment)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     update_shadow_tree_style(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-04 13:01:46 +01:00
										 |  |  | // https://html.spec.whatwg.org/multipage/rendering.html#the-details-and-summary-elements%3Athe-details-element-6
 | 
					
						
							| 
									
										
										
										
											2023-09-05 15:24:20 -04:00
										 |  |  | void HTMLDetailsElement::update_shadow_tree_style() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2024-06-25 11:28:58 +02:00
										 |  |  |     if (!shadow_root()) | 
					
						
							| 
									
										
										
										
											2023-11-29 18:22:16 -05:00
										 |  |  |         return; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-05 15:24:20 -04:00
										 |  |  |     if (has_attribute(HTML::AttributeNames::open)) { | 
					
						
							|  |  |  |         MUST(m_descendants_slot->set_attribute(HTML::AttributeNames::style, R"~~~( | 
					
						
							|  |  |  |             display: block; | 
					
						
							| 
									
										
										
										
											2023-10-08 11:42:00 +13:00
										 |  |  |         )~~~"_string)); | 
					
						
							| 
									
										
										
										
											2023-09-05 15:24:20 -04:00
										 |  |  |     } else { | 
					
						
							|  |  |  |         MUST(m_descendants_slot->set_attribute(HTML::AttributeNames::style, R"~~~( | 
					
						
							| 
									
										
										
										
											2024-06-24 12:11:02 +02:00
										 |  |  |             display: block; | 
					
						
							| 
									
										
										
										
											2023-09-05 15:24:20 -04:00
										 |  |  |             content-visibility: hidden; | 
					
						
							| 
									
										
										
										
											2023-10-08 11:42:00 +13:00
										 |  |  |         )~~~"_string)); | 
					
						
							| 
									
										
										
										
											2023-09-05 15:24:20 -04:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-05-09 10:35:07 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     shadow_root()->set_needs_layout_tree_update(true, DOM::SetNeedsLayoutTreeUpdateReason::DetailsElementOpenedOrClosed); | 
					
						
							| 
									
										
										
										
											2023-09-05 15:24:20 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-01 03:04:26 +01:00
										 |  |  | } |