mirror of
https://github.com/servo/servo.git
synced 2025-08-03 04:30:10 +01:00
layout: Store most anonymous pseudo-elements in box slots (#37941)
Previously, anonymous boxes, such for anonymous table parts were not associated with their non-pseudo ancestor DOM nodes. This presents a problem when it comes time to clear layout data during incremental layouts. This change reworks the way that pseudo-elements in general are stored in their non-pseudo ancestor DOM nodes, allowing for any number to be placed there. This trades a bit of performance for space, as just adding a vector to the node would add something like 24 bytes of storage to every node. This change should have a neutral runtime memory usage. Testing: This shouldn't change observable behavior and is thus covered by existing WPT tests. It will allow tests to pass in a subsequent PR. Signed-off-by: Martin Robinson <mrobinson@igalia.com> Co-authored-by: Oriol Brufau <obrufau@igalia.com>
This commit is contained in:
parent
4054f9a5a0
commit
51367c22a6
7 changed files with 106 additions and 95 deletions
|
@ -18,6 +18,7 @@ use malloc_size_of_derive::MallocSizeOf;
|
|||
use net_traits::image_cache::Image;
|
||||
use script::layout_dom::ServoLayoutNode;
|
||||
use servo_arc::Arc as ServoArc;
|
||||
use smallvec::SmallVec;
|
||||
use style::context::SharedStyleContext;
|
||||
use style::properties::ComputedValues;
|
||||
use style::selector_parser::{PseudoElement, RestyleDamage};
|
||||
|
@ -32,26 +33,35 @@ use crate::replaced::CanvasInfo;
|
|||
use crate::table::TableLevelBox;
|
||||
use crate::taffy::TaffyItemBox;
|
||||
|
||||
#[derive(MallocSizeOf)]
|
||||
pub struct PseudoLayoutData {
|
||||
pseudo: PseudoElement,
|
||||
box_slot: ArcRefCell<Option<LayoutBox>>,
|
||||
}
|
||||
|
||||
/// The data that is stored in each DOM node that is used by layout.
|
||||
#[derive(Default, MallocSizeOf)]
|
||||
pub struct InnerDOMLayoutData {
|
||||
pub(super) self_box: ArcRefCell<Option<LayoutBox>>,
|
||||
pub(super) pseudo_before_box: ArcRefCell<Option<LayoutBox>>,
|
||||
pub(super) pseudo_after_box: ArcRefCell<Option<LayoutBox>>,
|
||||
pub(super) pseudo_marker_box: ArcRefCell<Option<LayoutBox>>,
|
||||
pub(super) pseudo_boxes: SmallVec<[PseudoLayoutData; 2]>,
|
||||
}
|
||||
|
||||
impl InnerDOMLayoutData {
|
||||
pub(crate) fn for_pseudo(
|
||||
&self,
|
||||
pseudo_element: Option<PseudoElement>,
|
||||
) -> AtomicRef<Option<LayoutBox>> {
|
||||
match pseudo_element {
|
||||
Some(PseudoElement::Before) => self.pseudo_before_box.borrow(),
|
||||
Some(PseudoElement::After) => self.pseudo_after_box.borrow(),
|
||||
Some(PseudoElement::Marker) => self.pseudo_marker_box.borrow(),
|
||||
_ => self.self_box.borrow(),
|
||||
) -> Option<AtomicRef<Option<LayoutBox>>> {
|
||||
let Some(pseudo_element) = pseudo_element else {
|
||||
return Some(self.self_box.borrow());
|
||||
};
|
||||
|
||||
for pseudo_layout_data in self.pseudo_boxes.iter() {
|
||||
if pseudo_element == pseudo_layout_data.pseudo {
|
||||
return Some(pseudo_layout_data.box_slot.borrow());
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -171,16 +181,18 @@ pub struct BoxSlot<'dom> {
|
|||
pub(crate) marker: PhantomData<&'dom ()>,
|
||||
}
|
||||
|
||||
/// A mutable reference to a `LayoutBox` stored in a DOM element.
|
||||
impl BoxSlot<'_> {
|
||||
pub(crate) fn new(slot: ArcRefCell<Option<LayoutBox>>) -> Self {
|
||||
let slot = Some(slot);
|
||||
impl From<ArcRefCell<Option<LayoutBox>>> for BoxSlot<'_> {
|
||||
fn from(layout_box_slot: ArcRefCell<Option<LayoutBox>>) -> Self {
|
||||
let slot = Some(layout_box_slot);
|
||||
Self {
|
||||
slot,
|
||||
marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A mutable reference to a `LayoutBox` stored in a DOM element.
|
||||
impl BoxSlot<'_> {
|
||||
pub(crate) fn dummy() -> Self {
|
||||
let slot = None;
|
||||
Self {
|
||||
|
@ -226,12 +238,14 @@ pub(crate) trait NodeExt<'dom> {
|
|||
fn layout_data_mut(&self) -> AtomicRefMut<'dom, InnerDOMLayoutData>;
|
||||
fn layout_data(&self) -> Option<AtomicRef<'dom, InnerDOMLayoutData>>;
|
||||
fn element_box_slot(&self) -> BoxSlot<'dom>;
|
||||
fn pseudo_element_box_slot(&self, which: PseudoElement) -> BoxSlot<'dom>;
|
||||
fn unset_pseudo_element_box(&self, which: PseudoElement);
|
||||
fn pseudo_element_box_slot(&self, pseudo_element: PseudoElement) -> BoxSlot<'dom>;
|
||||
|
||||
/// Remove boxes for the element itself, and its `:before` and `:after` if any.
|
||||
/// Remove boxes for the element itself, and all of its pseudo-element boxes.
|
||||
fn unset_all_boxes(&self);
|
||||
|
||||
/// Remove all pseudo-element boxes for this element.
|
||||
fn unset_all_pseudo_boxes(&self);
|
||||
|
||||
fn fragments_for_pseudo(&self, pseudo_element: Option<PseudoElement>) -> Vec<Fragment>;
|
||||
fn invalidate_cached_fragment(&self);
|
||||
|
||||
|
@ -341,62 +355,55 @@ impl<'dom> NodeExt<'dom> for ServoLayoutNode<'dom> {
|
|||
}
|
||||
|
||||
fn element_box_slot(&self) -> BoxSlot<'dom> {
|
||||
BoxSlot::new(self.layout_data_mut().self_box.clone())
|
||||
self.layout_data_mut().self_box.clone().into()
|
||||
}
|
||||
|
||||
fn pseudo_element_box_slot(&self, pseudo_element_type: PseudoElement) -> BoxSlot<'dom> {
|
||||
let data = self.layout_data_mut();
|
||||
let cell = match pseudo_element_type {
|
||||
PseudoElement::Before => &data.pseudo_before_box,
|
||||
PseudoElement::After => &data.pseudo_after_box,
|
||||
PseudoElement::Marker => &data.pseudo_marker_box,
|
||||
_ => unreachable!(
|
||||
"Asked for box slot for unsupported pseudo-element: {:?}",
|
||||
pseudo_element_type
|
||||
),
|
||||
};
|
||||
BoxSlot::new(cell.clone())
|
||||
}
|
||||
|
||||
fn unset_pseudo_element_box(&self, pseudo_element_type: PseudoElement) {
|
||||
let data = self.layout_data_mut();
|
||||
let cell = match pseudo_element_type {
|
||||
PseudoElement::Before => &data.pseudo_before_box,
|
||||
PseudoElement::After => &data.pseudo_after_box,
|
||||
PseudoElement::Marker => &data.pseudo_marker_box,
|
||||
_ => unreachable!(
|
||||
"Asked for box slot for unsupported pseudo-element: {:?}",
|
||||
pseudo_element_type
|
||||
),
|
||||
};
|
||||
*cell.borrow_mut() = None;
|
||||
fn pseudo_element_box_slot(&self, pseudo_element: PseudoElement) -> BoxSlot<'dom> {
|
||||
let mut layout_data = self.layout_data_mut();
|
||||
let box_slot = ArcRefCell::new(None);
|
||||
layout_data.pseudo_boxes.push(PseudoLayoutData {
|
||||
pseudo: pseudo_element,
|
||||
box_slot: box_slot.clone(),
|
||||
});
|
||||
box_slot.into()
|
||||
}
|
||||
|
||||
fn unset_all_boxes(&self) {
|
||||
let 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;
|
||||
*data.pseudo_marker_box.borrow_mut() = None;
|
||||
let mut layout_data = self.layout_data_mut();
|
||||
*layout_data.self_box.borrow_mut() = None;
|
||||
layout_data.pseudo_boxes.clear();
|
||||
|
||||
// Stylo already takes care of removing all layout data
|
||||
// for DOM descendants of elements with `display: none`.
|
||||
}
|
||||
|
||||
fn unset_all_pseudo_boxes(&self) {
|
||||
self.layout_data_mut().pseudo_boxes.clear();
|
||||
}
|
||||
|
||||
fn invalidate_cached_fragment(&self) {
|
||||
let data = self.layout_data_mut();
|
||||
if let Some(data) = data.self_box.borrow_mut().as_mut() {
|
||||
if let Some(data) = data.self_box.borrow_mut().as_ref() {
|
||||
data.invalidate_cached_fragment();
|
||||
}
|
||||
|
||||
for pseudo_layout_data in data.pseudo_boxes.iter() {
|
||||
if let Some(layout_box) = pseudo_layout_data.box_slot.borrow().as_ref() {
|
||||
layout_box.invalidate_cached_fragment();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fragments_for_pseudo(&self, pseudo_element: Option<PseudoElement>) -> Vec<Fragment> {
|
||||
NodeExt::layout_data(self)
|
||||
.and_then(|layout_data| {
|
||||
layout_data
|
||||
.for_pseudo(pseudo_element)
|
||||
.as_ref()
|
||||
.map(LayoutBox::fragments)
|
||||
})
|
||||
let Some(layout_data) = NodeExt::layout_data(self) else {
|
||||
return vec![];
|
||||
};
|
||||
let Some(layout_data) = layout_data.for_pseudo(pseudo_element) else {
|
||||
return vec![];
|
||||
};
|
||||
layout_data
|
||||
.as_ref()
|
||||
.map(LayoutBox::fragments)
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
|
@ -407,21 +414,11 @@ impl<'dom> NodeExt<'dom> for ServoLayoutNode<'dom> {
|
|||
layout_object.repair_style(context, self, &style);
|
||||
}
|
||||
|
||||
if let Some(layout_object) = &*data.pseudo_before_box.borrow() {
|
||||
if let Some(node) = self.to_threadsafe().with_pseudo(PseudoElement::Before) {
|
||||
layout_object.repair_style(context, self, &node.style(context));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(layout_object) = &*data.pseudo_after_box.borrow() {
|
||||
if let Some(node) = self.to_threadsafe().with_pseudo(PseudoElement::After) {
|
||||
layout_object.repair_style(context, self, &node.style(context));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(layout_object) = &*data.pseudo_marker_box.borrow() {
|
||||
if let Some(node) = self.to_threadsafe().with_pseudo(PseudoElement::Marker) {
|
||||
layout_object.repair_style(context, self, &node.style(context));
|
||||
for pseudo_layout_data in data.pseudo_boxes.iter() {
|
||||
if let Some(layout_box) = pseudo_layout_data.box_slot.borrow().as_ref() {
|
||||
if let Some(node) = self.to_threadsafe().with_pseudo(pseudo_layout_data.pseudo) {
|
||||
layout_box.repair_style(context, self, &node.style(context));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue