mirror of
https://github.com/servo/servo.git
synced 2025-07-22 14:53:49 +01:00
Allow the <details>
element to be opened and closed (#35261)
* Implement the <summary> element Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> * Implement UA shadow root for <details> Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> * Invalidate style when display is opened or closed Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> * Fix /_mozilla/mozilla/duplicated_scroll_ids.html This test previously assumed that <details> elements would not be rendered. Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> * Implement implicit summary elements Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> * Update WPT expectations Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> * Remove test for duplicated scroll IDs See https://github.com/servo/servo/pull/35261#discussion_r1969328725 for reasoning. Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> * Use Iterator::find to find implicit summary element Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> --------- Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
This commit is contained in:
parent
cceff77928
commit
754b117011
17 changed files with 251 additions and 94 deletions
|
@ -23,6 +23,7 @@ use crate::dom::bindings::codegen::Bindings::EventHandlerBinding::{
|
|||
use crate::dom::bindings::codegen::Bindings::HTMLElementBinding::HTMLElementMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::HTMLLabelElementBinding::HTMLLabelElementMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::NodeBinding::Node_Binding::NodeMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::ShadowRoot_Binding::ShadowRootMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
|
||||
use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
|
||||
use crate::dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId};
|
||||
|
@ -48,6 +49,7 @@ use crate::dom::htmlinputelement::{HTMLInputElement, InputType};
|
|||
use crate::dom::htmllabelelement::HTMLLabelElement;
|
||||
use crate::dom::htmltextareaelement::HTMLTextAreaElement;
|
||||
use crate::dom::node::{BindContext, Node, NodeTraits, ShadowIncluding, UnbindContext};
|
||||
use crate::dom::shadowroot::ShadowRoot;
|
||||
use crate::dom::text::Text;
|
||||
use crate::dom::virtualmethods::VirtualMethods;
|
||||
use crate::script_runtime::CanGc;
|
||||
|
@ -914,45 +916,64 @@ impl HTMLElement {
|
|||
|
||||
// https://html.spec.whatwg.org/multipage/#the-summary-element:activation-behaviour
|
||||
pub(crate) fn summary_activation_behavior(&self) {
|
||||
// Step 1
|
||||
if !self.is_summary_for_its_parent_details() {
|
||||
debug_assert!(self.as_element().local_name() == &local_name!("summary"));
|
||||
|
||||
// Step 1. If this summary element is not the summary for its parent details, then return.
|
||||
if !self.is_a_summary_for_its_parent_details() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 2
|
||||
let parent_details = self.upcast::<Node>().GetParentNode().unwrap();
|
||||
// Step 2. Let parent be this summary element's parent.
|
||||
let parent = if self.is_implicit_summary_element() {
|
||||
DomRoot::downcast::<HTMLDetailsElement>(self.containing_shadow_root().unwrap().Host())
|
||||
.unwrap()
|
||||
} else {
|
||||
self.upcast::<Node>()
|
||||
.GetParentNode()
|
||||
.and_then(DomRoot::downcast::<HTMLDetailsElement>)
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
// Step 3
|
||||
parent_details
|
||||
.downcast::<HTMLDetailsElement>()
|
||||
.unwrap()
|
||||
.toggle();
|
||||
// Step 3. If the open attribute is present on parent, then remove it.
|
||||
// Otherwise, set parent's open attribute to the empty string.
|
||||
parent.toggle();
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#summary-for-its-parent-details
|
||||
fn is_summary_for_its_parent_details(&self) -> bool {
|
||||
// Step 1
|
||||
let summary_node = self.upcast::<Node>();
|
||||
if !summary_node.has_parent() {
|
||||
/// <https://html.spec.whatwg.org/multipage/#summary-for-its-parent-details>
|
||||
fn is_a_summary_for_its_parent_details(&self) -> bool {
|
||||
if self.is_implicit_summary_element() {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Step 1. If this summary element has no parent, then return false.
|
||||
// Step 2. Let parent be this summary element's parent.
|
||||
let Some(parent) = self.upcast::<Node>().GetParentNode() else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// Step 2
|
||||
let parent = &summary_node.GetParentNode().unwrap();
|
||||
|
||||
// Step 3
|
||||
if !parent.is::<HTMLDetailsElement>() {
|
||||
// Step 3. If parent is not a details element, then return false.
|
||||
let Some(details) = parent.downcast::<HTMLDetailsElement>() else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// Step 4 & 5
|
||||
let first_summary_element = parent
|
||||
.child_elements()
|
||||
.find(|el| el.local_name() == &local_name!("summary"));
|
||||
match first_summary_element {
|
||||
Some(first_summary) => &*first_summary == self.as_element(),
|
||||
None => false,
|
||||
}
|
||||
// Step 4. If parent's first summary element child is not this summary
|
||||
// element, then return false.
|
||||
// Step 5. Return true.
|
||||
details
|
||||
.find_corresponding_summary_element()
|
||||
.is_some_and(|summary| &*summary == self.upcast())
|
||||
}
|
||||
|
||||
/// Whether or not this is an implicitly generated `<summary>`
|
||||
/// element for a UA `<details>` shadow tree
|
||||
fn is_implicit_summary_element(&self) -> bool {
|
||||
// Note that non-implicit summary elements are not actually inside
|
||||
// the UA shadow tree, they're only assigned to a slot inside it.
|
||||
// Therefore they don't cause false positives here
|
||||
self.containing_shadow_root()
|
||||
.as_deref()
|
||||
.map(ShadowRoot::Host)
|
||||
.is_some_and(|host| host.is::<HTMLDetailsElement>())
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#rendered-text-fragment>
|
||||
|
@ -1173,6 +1194,7 @@ impl Activatable for HTMLElement {
|
|||
self.summary_activation_behavior();
|
||||
}
|
||||
}
|
||||
|
||||
// Form-associated custom elements are the same interface type as
|
||||
// normal HTMLElements, so HTMLElement needs to have the FormControl trait
|
||||
// even though it's usually more specific trait implementations, like the
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue