aboutsummaryrefslogtreecommitdiffstats
path: root/components/layout_2020/dom_traversal.rs
diff options
context:
space:
mode:
Diffstat (limited to 'components/layout_2020/dom_traversal.rs')
-rw-r--r--components/layout_2020/dom_traversal.rs388
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;
}
}