diff options
Diffstat (limited to 'components/layout_2020/dom_traversal.rs')
-rw-r--r-- | components/layout_2020/dom_traversal.rs | 388 |
1 files changed, 213 insertions, 175 deletions
diff --git a/components/layout_2020/dom_traversal.rs b/components/layout_2020/dom_traversal.rs index 848e89ba564..0e9e18a739e 100644 --- a/components/layout_2020/dom_traversal.rs +++ b/components/layout_2020/dom_traversal.rs @@ -1,22 +1,28 @@ -use super::*; -use crate::dom::{Document, NodeData, NodeId}; -use crate::style::StyleSet; -use atomic_refcell::AtomicRefMut; +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -pub(super) struct Context<'a> { - pub document: &'a Document, - pub author_styles: &'a StyleSet, -} +use crate::element_data::{LayoutBox, LayoutDataForElement}; +use crate::replaced::ReplacedContent; +use crate::style_ext::{Display, DisplayGeneratingBox, DisplayInside, DisplayOutside}; +use crate::wrapper::GetRawData; +use atomic_refcell::AtomicRefMut; +use script_layout_interface::wrapper_traits::{LayoutNode, ThreadSafeLayoutNode}; +use servo_arc::Arc; +use std::convert::TryInto; +use style::context::SharedStyleContext; +use style::dom::TNode; +use style::properties::ComputedValues; -#[derive(Copy, Clone)] +#[derive(Clone, Copy)] pub(super) enum WhichPseudoElement { Before, After, } -pub(super) enum Contents { +pub(super) enum Contents<Node> { /// Refers to a DOM subtree, plus `::before` and `::after` pseudo-elements. - OfElement(NodeId), + OfElement(Node), /// Example: an `<img src=…>` element. /// <https://drafts.csswg.org/css2/conform.html#replaced-element> @@ -27,8 +33,8 @@ pub(super) enum Contents { OfPseudoElement(Vec<PseudoElementContentItem>), } -pub(super) enum NonReplacedContents { - OfElement(NodeId), +pub(super) enum NonReplacedContents<Node> { + OfElement(Node), OfPseudoElement(Vec<PseudoElementContentItem>), } @@ -37,206 +43,196 @@ pub(super) enum PseudoElementContentItem { Replaced(ReplacedContent), } -pub(super) trait TraversalHandler<'dom> { - fn handle_text(&mut self, text: &str, parent_style: &Arc<ComputedValues>); +pub(super) trait TraversalHandler<Node> { + fn handle_text(&mut self, text: String, parent_style: &Arc<ComputedValues>); /// Or pseudo-element fn handle_element( &mut self, style: &Arc<ComputedValues>, display: DisplayGeneratingBox, - contents: Contents, - box_slot: BoxSlot<'dom>, + contents: Contents<Node>, + box_slot: BoxSlot, ); } -fn traverse_children_of<'dom>( - parent_element: NodeId, - parent_element_style: &Arc<ComputedValues>, - context: &'dom Context, - handler: &mut impl TraversalHandler<'dom>, -) { - traverse_pseudo_element( - WhichPseudoElement::Before, - parent_element, - parent_element_style, - context, - handler, - ); +fn traverse_children_of<'dom, Node>( + parent_element: Node, + context: &SharedStyleContext, + handler: &mut impl TraversalHandler<Node>, +) where + Node: NodeExt<'dom>, +{ + traverse_pseudo_element(WhichPseudoElement::Before, parent_element, context, handler); - let mut next = context.document[parent_element].first_child; + let mut next = parent_element.first_child(); while let Some(child) = next { - match &context.document[child].data { - NodeData::Document - | NodeData::Doctype { .. } - | NodeData::Comment { .. } - | NodeData::ProcessingInstruction { .. } => {} - NodeData::Text { contents } => { - handler.handle_text(contents, parent_element_style); - } - NodeData::Element(_) => traverse_element(child, parent_element_style, context, handler), + if let Some(contents) = child.as_text() { + handler.handle_text(contents, &child.style(context)); + } else if child.is_element() { + traverse_element(child, context, handler); } - next = context.document[child].next_sibling + next = child.next_sibling(); } - traverse_pseudo_element( - WhichPseudoElement::After, - parent_element, - &parent_element_style, - context, - handler, - ); + traverse_pseudo_element(WhichPseudoElement::After, parent_element, context, handler); } -fn traverse_element<'dom>( - element_id: NodeId, - parent_element_style: &ComputedValues, - context: &'dom Context, - handler: &mut impl TraversalHandler<'dom>, -) { - let style = style_for_element( - context.author_styles, - context.document, - element_id, - Some(parent_element_style), - ); - match style.box_.display { - Display::None => context.unset_boxes_in_subtree(element_id), +fn traverse_element<'dom, Node>( + element: Node, + context: &SharedStyleContext, + handler: &mut impl TraversalHandler<Node>, +) where + Node: NodeExt<'dom>, +{ + let style = element.style(context); + match Display::from(style.get_box().display) { + Display::None => element.unset_boxes_in_subtree(), Display::Contents => { - if ReplacedContent::for_element(element_id, context).is_some() { + if ReplacedContent::for_element(element, context).is_some() { // `display: content` on a replaced element computes to `display: none` // <https://drafts.csswg.org/css-display-3/#valdef-display-contents> - context.unset_boxes_in_subtree(element_id) + element.unset_boxes_in_subtree() } else { - context.layout_data_mut(element_id).self_box = Some(LayoutBox::DisplayContents); - traverse_children_of(element_id, &style, context, handler) + element.layout_data_mut().self_box = Some(LayoutBox::DisplayContents); + traverse_children_of(element, context, handler) } - } - Display::GeneratingBox(display) => handler.handle_element( - &style, - display, - match ReplacedContent::for_element(element_id, context) { - Some(replaced) => Contents::Replaced(replaced), - None => Contents::OfElement(element_id), - }, - context.element_box_slot(element_id), - ), + }, + Display::GeneratingBox(display) => { + handler.handle_element( + &style, + display, + match ReplacedContent::for_element(element, context) { + Some(replaced) => Contents::Replaced(replaced), + None => Contents::OfElement(element), + }, + element.element_box_slot(), + ); + }, } } -fn traverse_pseudo_element<'dom>( +fn traverse_pseudo_element<'dom, Node>( which: WhichPseudoElement, - element: NodeId, - element_style: &ComputedValues, - context: &'dom Context, - handler: &mut impl TraversalHandler<'dom>, -) { - if let Some(style) = pseudo_element_style(which, element, element_style, context) { - match style.box_.display { - Display::None => context.unset_pseudo_element_box(element, which), + element: Node, + context: &SharedStyleContext, + handler: &mut impl TraversalHandler<Node>, +) where + Node: NodeExt<'dom>, +{ + if let Some(style) = pseudo_element_style(which, element, context) { + match Display::from(style.get_box().display) { + Display::None => element.unset_pseudo_element_box(which), Display::Contents => { - context.unset_pseudo_element_box(element, which); + element.unset_pseudo_element_box(which); let items = generate_pseudo_element_content(&style, element, context); traverse_pseudo_element_contents(&style, items, handler); - } + }, Display::GeneratingBox(display) => { let items = generate_pseudo_element_content(&style, element, context); let contents = Contents::OfPseudoElement(items); - let box_slot = context.pseudo_element_box_slot(element, which); + let box_slot = element.pseudo_element_box_slot(which); handler.handle_element(&style, display, contents, box_slot); - } + }, } } } -fn traverse_pseudo_element_contents<'dom>( +fn traverse_pseudo_element_contents<'dom, Node>( pseudo_element_style: &Arc<ComputedValues>, items: Vec<PseudoElementContentItem>, - handler: &mut impl TraversalHandler<'dom>, -) { - let mut anonymous_style = None; + handler: &mut impl TraversalHandler<Node>, +) where + Node: 'dom, +{ + // let mut anonymous_style = None; for item in items { match item { - PseudoElementContentItem::Text(text) => { - handler.handle_text(&text, pseudo_element_style) - } + PseudoElementContentItem::Text(text) => handler.handle_text(text, pseudo_element_style), PseudoElementContentItem::Replaced(contents) => { - let item_style = anonymous_style.get_or_insert_with(|| { - ComputedValues::anonymous_inheriting_from(Some(pseudo_element_style)) - }); - let display_inline = DisplayGeneratingBox::OutsideInside { - outside: DisplayOutside::Inline, - inside: DisplayInside::Flow, - }; - // `display` is not inherited, so we get the initial value - debug_assert!(item_style.box_.display == Display::GeneratingBox(display_inline)); - handler.handle_element( - item_style, - display_inline, - Contents::Replaced(contents), - // We don’t keep pointers to boxes generated by contents of pseudo-elements - BoxSlot::dummy(), - ) - } + // FIXME + // let item_style = anonymous_style.get_or_insert_with(|| { + // ComputedValues::anonymous_inheriting_from(Some(pseudo_element_style)) + // }); + // let display_inline = DisplayGeneratingBox::OutsideInside { + // outside: DisplayOutside::Inline, + // inside: DisplayInside::Flow, + // }; + // // `display` is not inherited, so we get the initial value + // debug_assert!(item_style.box_.display == Display::GeneratingBox(display_inline)); + // handler.handle_element( + // item_style, + // display_inline, + // Contents::Replaced(contents), + // // We don’t keep pointers to boxes generated by contents of pseudo-elements + // BoxSlot::dummy(), + // ) + }, } } } -impl std::convert::TryFrom<Contents> for NonReplacedContents { +impl<Node> std::convert::TryFrom<Contents<Node>> for NonReplacedContents<Node> { type Error = ReplacedContent; - fn try_from(contents: Contents) -> Result<Self, Self::Error> { + fn try_from(contents: Contents<Node>) -> Result<Self, Self::Error> { match contents { - Contents::OfElement(id) => Ok(NonReplacedContents::OfElement(id)), + Contents::OfElement(node) => Ok(NonReplacedContents::OfElement(node)), Contents::OfPseudoElement(items) => Ok(NonReplacedContents::OfPseudoElement(items)), Contents::Replaced(replaced) => Err(replaced), } } } -impl std::convert::From<NonReplacedContents> for Contents { - fn from(contents: NonReplacedContents) -> Self { +impl<Node> std::convert::From<NonReplacedContents<Node>> for Contents<Node> { + fn from(contents: NonReplacedContents<Node>) -> Self { match contents { - NonReplacedContents::OfElement(id) => Contents::OfElement(id), + NonReplacedContents::OfElement(node) => Contents::OfElement(node), NonReplacedContents::OfPseudoElement(items) => Contents::OfPseudoElement(items), } } } -impl NonReplacedContents { - pub fn traverse<'dom>( +impl<'dom, Node> NonReplacedContents<Node> +where + Node: NodeExt<'dom>, +{ + pub(crate) fn traverse( self, inherited_style: &Arc<ComputedValues>, - context: &'dom Context, - handler: &mut impl TraversalHandler<'dom>, + context: &SharedStyleContext, + handler: &mut impl TraversalHandler<Node>, ) { match self { - NonReplacedContents::OfElement(id) => { - traverse_children_of(id, inherited_style, context, handler) - } + NonReplacedContents::OfElement(node) => traverse_children_of(node, context, handler), NonReplacedContents::OfPseudoElement(items) => { traverse_pseudo_element_contents(inherited_style, items, handler) - } + }, } } } -fn pseudo_element_style( +fn pseudo_element_style<'dom, Node>( _which: WhichPseudoElement, - _element: NodeId, - _element_style: &ComputedValues, - _context: &Context, -) -> Option<Arc<ComputedValues>> { + _element: Node, + _context: &SharedStyleContext, +) -> Option<Arc<ComputedValues>> +where + Node: NodeExt<'dom>, +{ // FIXME: run the cascade, then return None for `content: normal` or `content: none` // https://drafts.csswg.org/css2/generate.html#content None } -fn generate_pseudo_element_content( +fn generate_pseudo_element_content<'dom, Node>( _pseudo_element_style: &ComputedValues, - _element: NodeId, - _context: &Context, -) -> Vec<PseudoElementContentItem> { + _element: Node, + _context: &SharedStyleContext, +) -> Vec<PseudoElementContentItem> +where + Node: NodeExt<'dom>, +{ let _ = PseudoElementContentItem::Text; let _ = PseudoElementContentItem::Replaced; unimplemented!() @@ -271,37 +267,77 @@ impl Drop for BoxSlot<'_> { } } -impl Context<'_> { - fn layout_data_mut(&self, element_id: NodeId) -> AtomicRefMut<LayoutDataForElement> { - self.document[element_id] - .as_element() +pub(crate) trait NodeExt<'dom>: 'dom + Copy + Send + Sync { + fn is_element(self) -> bool; + fn as_text(self) -> Option<String>; + fn first_child(self) -> Option<Self>; + fn next_sibling(self) -> Option<Self>; + fn parent_node(self) -> Option<Self>; + fn style(self, context: &SharedStyleContext) -> Arc<ComputedValues>; + + fn layout_data_mut(&self) -> AtomicRefMut<LayoutDataForElement>; + fn element_box_slot(&self) -> BoxSlot; + fn pseudo_element_box_slot(&self, which: WhichPseudoElement) -> BoxSlot; + fn unset_pseudo_element_box(self, which: WhichPseudoElement); + fn unset_boxes_in_subtree(self); +} + +impl<'dom, T> NodeExt<'dom> for T +where + T: 'dom + LayoutNode + Send + Sync, +{ + fn is_element(self) -> bool { + self.to_threadsafe().as_element().is_some() + } + + fn as_text(self) -> Option<String> { + if self.is_text_node() { + Some(self.to_threadsafe().node_text_content()) + } else { + None + } + } + + fn first_child(self) -> Option<Self> { + TNode::first_child(&self) + } + + fn next_sibling(self) -> Option<Self> { + TNode::next_sibling(&self) + } + + fn parent_node(self) -> Option<Self> { + TNode::next_sibling(&self) + } + + fn style(self, context: &SharedStyleContext) -> Arc<ComputedValues> { + self.to_threadsafe().style(context) + } + + fn layout_data_mut(&self) -> AtomicRefMut<LayoutDataForElement> { + self.get_raw_data() + .map(|d| d.layout_data.borrow_mut()) .unwrap() - .layout_data - .borrow_mut() } - fn element_box_slot(&self, element_id: NodeId) -> BoxSlot { - BoxSlot::new(AtomicRefMut::map( - self.layout_data_mut(element_id), - |data| &mut data.self_box, - )) + fn element_box_slot(&self) -> BoxSlot { + BoxSlot::new(AtomicRefMut::map(self.layout_data_mut(), |data| { + &mut data.self_box + })) } - fn pseudo_element_box_slot(&self, element_id: NodeId, which: WhichPseudoElement) -> BoxSlot { - BoxSlot::new(AtomicRefMut::map( - self.layout_data_mut(element_id), - |data| { - let pseudos = data.pseudo_elements.get_or_insert_with(Default::default); - match which { - WhichPseudoElement::Before => &mut pseudos.before, - WhichPseudoElement::After => &mut pseudos.after, - } - }, - )) + fn pseudo_element_box_slot(&self, which: WhichPseudoElement) -> BoxSlot { + BoxSlot::new(AtomicRefMut::map(self.layout_data_mut(), |data| { + let pseudos = data.pseudo_elements.get_or_insert_with(Default::default); + match which { + WhichPseudoElement::Before => &mut pseudos.before, + WhichPseudoElement::After => &mut pseudos.after, + } + })) } - fn unset_pseudo_element_box(&self, element_id: NodeId, which: WhichPseudoElement) { - if let Some(pseudos) = &mut self.layout_data_mut(element_id).pseudo_elements { + fn unset_pseudo_element_box(self, which: WhichPseudoElement) { + if let Some(pseudos) = &mut self.layout_data_mut().pseudo_elements { match which { WhichPseudoElement::Before => pseudos.before = None, WhichPseudoElement::After => pseudos.after = None, @@ -309,34 +345,36 @@ impl Context<'_> { } } - fn unset_boxes_in_subtree(&self, base_element: NodeId) { - let mut node_id = base_element; + fn unset_boxes_in_subtree(self) { + let mut node = self; loop { - let node = &self.document[node_id]; - if let Some(element_data) = node.as_element() { - let mut layout_data = element_data.layout_data.borrow_mut(); - layout_data.pseudo_elements = None; - if layout_data.self_box.take().is_some() { + if node.is_element() { + let traverse_children = { + let mut layout_data = node.layout_data_mut(); + layout_data.pseudo_elements = None; + layout_data.self_box.take().is_some() + }; + if traverse_children { // Only descend into children if we removed a box. // If there wasn’t one, then descendants don’t have boxes either. - if let Some(child) = node.first_child { - node_id = child; + if let Some(child) = node.first_child() { + node = child; continue; } } } - let mut next_is_a_sibling_of = node_id; - node_id = loop { - if let Some(sibling) = self.document[next_is_a_sibling_of].next_sibling { + let mut next_is_a_sibling_of = node; + node = loop { + if let Some(sibling) = next_is_a_sibling_of.next_sibling() { break sibling; } else { next_is_a_sibling_of = node - .parent + .parent_node() .expect("reached the root while traversing only a subtree"); } }; - if next_is_a_sibling_of == base_element { - // Don’t go outside the subtree + if next_is_a_sibling_of == self { + // Don’t go outside the subtree. return; } } |