Detect body elements during layout

During layout it is often useful, for various specification reasons, to
know if an element is the `<body>` element of an `<html>` element root. There
are a couple places where a brittle heuristic is used to detect `<body>`
elements. This information is going to be even more important to
properly handle `<html>` elements that inherit their overflow property from
their `<body>` children.

Implementing this properly requires updating the DOM wrapper interface.
This check does reach up to the parent of thread-safe nodes, but this is
essentially the same kind of operation that `parent_style()` does, so is
ostensibly safe.

This change should not change any behavior and is just a preparation
step for properly handle `<body>` overflow.
This commit is contained in:
Martin Robinson 2023-04-30 20:21:58 +02:00
parent 77a184a0e7
commit 72302e2dae
26 changed files with 487 additions and 277 deletions

View file

@ -428,6 +428,40 @@ impl<'le> fmt::Debug for ServoLayoutElement<'le> {
}
}
impl<'dom> ServoLayoutElement<'dom> {
/// Returns true if this element is the body child of an html element root element.
fn is_body_element_of_html_element_root(&self) -> bool {
if self.element.local_name() != &local_name!("body") {
return false;
}
self.parent_element()
.map(|element| {
element.is_root() && element.element.local_name() == &local_name!("html")
})
.unwrap_or(false)
}
/// Returns the parent element of this element, if it has one.
fn parent_element(&self) -> Option<Self> {
self.element
.upcast()
.composed_parent_node_ref()
.and_then(as_element)
}
// Returns true is this is the root element.
fn is_root(&self) -> bool {
match self.as_node().parent_node() {
None => false,
Some(node) => match node.script_type_id() {
NodeTypeId::Document(_) => true,
_ => false,
},
}
}
}
impl<'le> TElement for ServoLayoutElement<'le> {
type ConcreteNode = ServoLayoutNode<'le>;
type TraversalChildrenIterator = DomChildren<Self::ConcreteNode>;
@ -679,11 +713,7 @@ impl<'le> TElement for ServoLayoutElement<'le> {
}
fn is_html_document_body_element(&self) -> bool {
// This is only used for the "tables inherit from body" quirk, which we
// don't implement.
//
// FIXME(emilio): We should be able to give the right answer though!
false
self.is_body_element_of_html_element_root()
}
fn synthesize_presentational_hints_for_legacy_attributes<V>(
@ -839,13 +869,7 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> {
}
fn is_root(&self) -> bool {
match self.as_node().parent_node() {
None => false,
Some(node) => match node.script_type_id() {
NodeTypeId::Document(_) => true,
_ => false,
},
}
ServoLayoutElement::is_root(self)
}
fn is_empty(&self) -> bool {
@ -981,11 +1005,7 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> {
}
fn is_html_element_in_html_document(&self) -> bool {
if !self.element.is_html_element() {
return false;
}
self.as_node().owner_doc().is_html_document()
self.element.is_html_element() && self.as_node().owner_doc().is_html_document()
}
}
@ -1361,6 +1381,10 @@ impl<'le> ThreadSafeLayoutElement<'le> for ServoThreadSafeLayoutElement<'le> {
fn is_shadow_host(&self) -> bool {
self.element.shadow_root().is_some()
}
fn is_body_element_of_html_element_root(&self) -> bool {
self.element.is_body_element_of_html_element_root()
}
}
/// This implementation of `::selectors::Element` is used for implementing lazy