Rewrite handling of pointers to boxes in the DOM tree

This commit is contained in:
Simon Sapin 2020-05-07 08:31:45 +02:00
parent c43ab0c267
commit 3e8623332b
3 changed files with 33 additions and 73 deletions

View file

@ -110,25 +110,21 @@ fn traverse_element<'dom, Node>(
let replaced = ReplacedContent::for_element(element);
let style = element.style(context);
match Display::from(style.get_box().display) {
Display::None => element.unset_boxes_in_subtree(),
Display::None => element.unset_all_boxes(),
Display::Contents => {
if replaced.is_some() {
// `display: content` on a replaced element computes to `display: none`
// <https://drafts.csswg.org/css-display-3/#valdef-display-contents>
element.unset_boxes_in_subtree()
element.unset_all_boxes()
} else {
*element.layout_data_mut().self_box.borrow_mut() = Some(LayoutBox::DisplayContents);
element.element_box_slot().set(LayoutBox::DisplayContents);
traverse_children_of(element, context, handler)
}
},
Display::GeneratingBox(display) => {
handler.handle_element(
element,
&style,
display,
replaced.map_or(Contents::OfElement, Contents::Replaced),
element.element_box_slot(),
);
let contents = replaced.map_or(Contents::OfElement, Contents::Replaced);
let box_slot = element.element_box_slot();
handler.handle_element(element, &style, display, contents, box_slot);
},
}
}
@ -145,17 +141,20 @@ fn traverse_pseudo_element<'dom, Node>(
match Display::from(style.get_box().display) {
Display::None => element.unset_pseudo_element_box(which),
Display::Contents => {
element.unset_pseudo_element_box(which);
let items = generate_pseudo_element_content(&style, element, context);
let box_slot = element.pseudo_element_box_slot(which);
box_slot.set(LayoutBox::DisplayContents);
traverse_pseudo_element_contents(element, &style, context, handler, items);
},
Display::GeneratingBox(display) => {
let items = generate_pseudo_element_content(&style, element, context);
let contents = Contents::OfPseudoElement(items);
let box_slot = element.pseudo_element_box_slot(which);
let contents = Contents::OfPseudoElement(items);
handler.handle_element(element, &style, display, contents, box_slot);
},
}
} else {
element.unset_pseudo_element_box(which)
}
}
@ -373,7 +372,9 @@ pub(crate) trait NodeExt<'dom>: 'dom + Copy + LayoutNode<'dom> + Send + Sync {
fn element_box_slot(&self) -> BoxSlot<'dom>;
fn pseudo_element_box_slot(&self, which: WhichPseudoElement) -> BoxSlot<'dom>;
fn unset_pseudo_element_box(self, which: WhichPseudoElement);
fn unset_boxes_in_subtree(self);
/// Remove boxes for the element itself, and its `:before` and `:after` if any.
fn unset_all_boxes(self);
}
impl<'dom, T> NodeExt<'dom> for T
@ -458,65 +459,29 @@ where
}
fn pseudo_element_box_slot(&self, which: WhichPseudoElement) -> BoxSlot<'dom> {
let mut data = self.layout_data_mut();
let pseudos = data.pseudo_elements.get_or_insert_with(Default::default);
let data = self.layout_data_mut();
let cell = match which {
WhichPseudoElement::Before => &mut pseudos.before,
WhichPseudoElement::After => &mut pseudos.after,
WhichPseudoElement::Before => &data.pseudo_before_box,
WhichPseudoElement::After => &data.pseudo_after_box,
};
BoxSlot::new(cell.clone())
}
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.borrow_mut() = None,
WhichPseudoElement::After => *pseudos.after.borrow_mut() = None,
}
}
}
fn unset_boxes_in_subtree(self) {
assert!(self.is_element());
assert!(self.parent_node().is_some());
let mut node = self;
loop {
if node.is_element() {
let traverse_children = {
let mut layout_data = node.layout_data_mut();
layout_data.pseudo_elements = None;
let self_box = layout_data.self_box.borrow_mut().take();
self_box.is_some()
let data = self.layout_data_mut();
let cell = match which {
WhichPseudoElement::Before => &data.pseudo_before_box,
WhichPseudoElement::After => &data.pseudo_after_box,
};
if traverse_children {
// Only descend into children if we removed a box.
// If there wasnt one, then descendants dont have boxes either.
if let Some(child) = node.first_child() {
node = child;
continue;
}
} else if node == self {
// If this is the root of the subtree and we aren't descending
// into our children return now.
return;
}
*cell.borrow_mut() = None;
}
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_node()
.expect("reached the root while traversing only a subtree");
}
};
if next_is_a_sibling_of == self {
// Dont go outside the subtree.
return;
}
}
fn unset_all_boxes(self) {
let mut data = self.layout_data_mut();
*data.self_box.borrow_mut() = None;
*data.pseudo_before_box.borrow_mut() = None;
*data.pseudo_after_box.borrow_mut() = None;
// Stylo already takes care of removing all layout data
// for DOM descendants of elements with `display: none`.
}
}

View file

@ -9,13 +9,8 @@ use crate::flow::BlockLevelBox;
#[derive(Default)]
pub struct LayoutDataForElement {
pub(super) self_box: ArcRefCell<Option<LayoutBox>>,
pub(super) pseudo_elements: Option<Box<PseudoElementBoxes>>,
}
#[derive(Default)]
pub(super) struct PseudoElementBoxes {
pub before: ArcRefCell<Option<LayoutBox>>,
pub after: ArcRefCell<Option<LayoutBox>>,
pub(super) pseudo_before_box: ArcRefCell<Option<LayoutBox>>,
pub(super) pseudo_after_box: ArcRefCell<Option<LayoutBox>>,
}
pub(super) enum LayoutBox {

View file

@ -88,7 +88,7 @@ fn construct_for_root_element<'dom>(
let display_inside = match Display::from(box_style.display) {
Display::None => {
root_element.unset_boxes_in_subtree();
root_element.unset_all_boxes();
return (ContainsFloats::No, Vec::new());
},
Display::Contents => {