mirror of
https://github.com/servo/servo.git
synced 2025-08-11 00:15:32 +01:00
layout: Store Fragment
results in LayoutBoxBase
and start using them for queries (#36583)
Start storing a link to laid-out `Fragment`s in `LayoutBoxBase`, so that these are accessible for queries and eventually for incremental layout. Some box tree data structures lacked a `LayoutBoxBase`, such as table tracks and table track groups[^1]. In addition, start using these `Fragment`s for queries instead of walking the entire `Fragment` tree. Currently, this isn't possible for most queries as `Fragment`s do not cache their absolute offsets (which are often necessary). This change uses the new box tree `Fragment`s for most resolved style queries. [^1]: Note that only rows and row groups store `Fragment`s as columsn and colgroups do not produce any. Testing: This is covered by existing tests. Fixes: This is part of #36525. Signed-off-by: Martin Robinson <mrobinson@igalia.com> Co-authored-by: Oriol Brufau <obrufau@igalia.com>
This commit is contained in:
parent
fc201927ae
commit
2ee8427665
15 changed files with 387 additions and 222 deletions
|
@ -73,6 +73,10 @@ impl InlineBoxes {
|
|||
self.inline_boxes.len()
|
||||
}
|
||||
|
||||
pub(super) fn iter(&self) -> impl Iterator<Item = &ArcRefCell<InlineBox>> {
|
||||
self.inline_boxes.iter()
|
||||
}
|
||||
|
||||
pub(super) fn get(&self, identifier: &InlineBoxIdentifier) -> ArcRefCell<InlineBox> {
|
||||
self.inline_boxes[identifier.index_in_inline_boxes as usize].clone()
|
||||
}
|
||||
|
|
|
@ -492,9 +492,11 @@ impl LineItemLayout<'_, '_> {
|
|||
}
|
||||
|
||||
self.current_state.inline_advance += inner_state.inline_advance + pbm_sums.inline_sum();
|
||||
self.current_state
|
||||
.fragments
|
||||
.push((Fragment::Box(ArcRefCell::new(fragment)), content_rect));
|
||||
|
||||
let fragment = Fragment::Box(ArcRefCell::new(fragment));
|
||||
inline_box.base.add_fragment(fragment.clone());
|
||||
|
||||
self.current_state.fragments.push((fragment, content_rect));
|
||||
}
|
||||
|
||||
fn calculate_inline_box_block_start(
|
||||
|
@ -587,32 +589,34 @@ impl LineItemLayout<'_, '_> {
|
|||
// This needs to be added to the calculated block and inline positions.
|
||||
// Make the final result relative to the parent box.
|
||||
let ifc_writing_mode = self.layout.containing_block.style.writing_mode;
|
||||
let padding_border_margin_sides = atomic
|
||||
.fragment
|
||||
.padding_border_margin()
|
||||
.to_logical(ifc_writing_mode);
|
||||
let content_rect = {
|
||||
let block_start = atomic.calculate_block_start(&self.line_metrics);
|
||||
let atomic_fragment = atomic.fragment.borrow_mut();
|
||||
let padding_border_margin_sides = atomic_fragment
|
||||
.padding_border_margin()
|
||||
.to_logical(ifc_writing_mode);
|
||||
|
||||
let mut atomic_offset = LogicalVec2 {
|
||||
inline: self.current_state.inline_advance + padding_border_margin_sides.inline_start,
|
||||
block: atomic.calculate_block_start(&self.line_metrics) -
|
||||
self.current_state.parent_offset.block +
|
||||
padding_border_margin_sides.block_start,
|
||||
};
|
||||
let mut atomic_offset = LogicalVec2 {
|
||||
inline: self.current_state.inline_advance +
|
||||
padding_border_margin_sides.inline_start,
|
||||
block: block_start - self.current_state.parent_offset.block +
|
||||
padding_border_margin_sides.block_start,
|
||||
};
|
||||
|
||||
if atomic.fragment.style.get_box().position == Position::Relative {
|
||||
atomic_offset +=
|
||||
relative_adjustement(&atomic.fragment.style, self.layout.containing_block);
|
||||
}
|
||||
if atomic_fragment.style.get_box().position == Position::Relative {
|
||||
atomic_offset +=
|
||||
relative_adjustement(&atomic_fragment.style, self.layout.containing_block);
|
||||
}
|
||||
|
||||
// Reconstruct a logical rectangle relative to the inline box container that will be used
|
||||
// after the inline box is procesed to find a final physical rectangle.
|
||||
let content_rect = LogicalRect {
|
||||
start_corner: atomic_offset,
|
||||
size: atomic
|
||||
.fragment
|
||||
.content_rect
|
||||
.size
|
||||
.to_logical(ifc_writing_mode),
|
||||
// Reconstruct a logical rectangle relative to the inline box container that will be used
|
||||
// after the inline box is procesed to find a final physical rectangle.
|
||||
LogicalRect {
|
||||
start_corner: atomic_offset,
|
||||
size: atomic_fragment
|
||||
.content_rect
|
||||
.size
|
||||
.to_logical(ifc_writing_mode),
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(mut positioning_context) = atomic.positioning_context {
|
||||
|
@ -628,10 +632,10 @@ impl LineItemLayout<'_, '_> {
|
|||
}
|
||||
|
||||
self.current_state.inline_advance += atomic.size.inline;
|
||||
self.current_state.fragments.push((
|
||||
Fragment::Box(ArcRefCell::new(atomic.fragment)),
|
||||
content_rect,
|
||||
));
|
||||
|
||||
self.current_state
|
||||
.fragments
|
||||
.push((Fragment::Box(atomic.fragment), content_rect));
|
||||
}
|
||||
|
||||
fn layout_absolute(&mut self, absolute: AbsolutelyPositionedLineItem) {
|
||||
|
@ -691,7 +695,7 @@ impl LineItemLayout<'_, '_> {
|
|||
));
|
||||
}
|
||||
|
||||
fn layout_float(&mut self, mut float: FloatLineItem) {
|
||||
fn layout_float(&mut self, float: FloatLineItem) {
|
||||
self.current_state
|
||||
.flags
|
||||
.insert(LineLayoutInlineContainerFlags::HAD_ANY_FLOATS);
|
||||
|
@ -705,13 +709,12 @@ impl LineItemLayout<'_, '_> {
|
|||
inline: self.current_state.parent_offset.inline,
|
||||
block: self.line_metrics.block_offset + self.current_state.parent_offset.block,
|
||||
};
|
||||
float.fragment.content_rect.origin -= distance_from_parent_to_ifc
|
||||
float.fragment.borrow_mut().content_rect.origin -= distance_from_parent_to_ifc
|
||||
.to_physical_size(self.layout.containing_block.style.writing_mode);
|
||||
|
||||
self.current_state.fragments.push((
|
||||
Fragment::Float(ArcRefCell::new(float.fragment)),
|
||||
LogicalRect::zero(),
|
||||
));
|
||||
self.current_state
|
||||
.fragments
|
||||
.push((Fragment::Float(float.fragment), LogicalRect::zero()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -829,7 +832,7 @@ impl TextRunLineItem {
|
|||
}
|
||||
|
||||
pub(super) struct AtomicLineItem {
|
||||
pub fragment: BoxFragment,
|
||||
pub fragment: ArcRefCell<BoxFragment>,
|
||||
pub size: LogicalVec2<Au>,
|
||||
pub positioning_context: Option<PositioningContext>,
|
||||
|
||||
|
@ -849,7 +852,7 @@ impl AtomicLineItem {
|
|||
/// Given the metrics for a line, our vertical alignment, and our block size, find a block start
|
||||
/// position relative to the top of the line.
|
||||
fn calculate_block_start(&self, line_metrics: &LineMetrics) -> Au {
|
||||
match self.fragment.style.clone_vertical_align() {
|
||||
match self.fragment.borrow().style.clone_vertical_align() {
|
||||
GenericVerticalAlign::Keyword(VerticalAlignKeyword::Top) => Au::zero(),
|
||||
GenericVerticalAlign::Keyword(VerticalAlignKeyword::Bottom) => {
|
||||
line_metrics.block_size - self.size.block
|
||||
|
@ -869,7 +872,7 @@ pub(super) struct AbsolutelyPositionedLineItem {
|
|||
}
|
||||
|
||||
pub(super) struct FloatLineItem {
|
||||
pub fragment: BoxFragment,
|
||||
pub fragment: ArcRefCell<BoxFragment>,
|
||||
/// Whether or not this float Fragment has been placed yet. Fragments that
|
||||
/// do not fit on a line need to be placed after the hypothetical block start
|
||||
/// of the next line.
|
||||
|
|
|
@ -221,6 +221,22 @@ impl InlineItem {
|
|||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn fragments(&self) -> Vec<Fragment> {
|
||||
match self {
|
||||
InlineItem::StartInlineBox(inline_box) => inline_box.borrow().base.fragments(),
|
||||
InlineItem::EndInlineBox | InlineItem::TextRun(..) => {
|
||||
unreachable!("Should never have these kind of fragments attached to a DOM node")
|
||||
},
|
||||
InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => {
|
||||
positioned_box.borrow().context.base.fragments()
|
||||
},
|
||||
InlineItem::OutOfFlowFloatBox(float_box) => float_box.contents.base.fragments(),
|
||||
InlineItem::Atomic(independent_formatting_context, ..) => {
|
||||
independent_formatting_context.base.fragments()
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Information about the current line under construction for a particular
|
||||
|
@ -1081,8 +1097,8 @@ impl InlineFormattingContextLayout<'_> {
|
|||
float_item: &mut FloatLineItem,
|
||||
line_inline_size_without_trailing_whitespace: Au,
|
||||
) {
|
||||
let logical_margin_rect_size = float_item
|
||||
.fragment
|
||||
let mut float_fragment = float_item.fragment.borrow_mut();
|
||||
let logical_margin_rect_size = float_fragment
|
||||
.margin_rect()
|
||||
.size
|
||||
.to_logical(self.containing_block.style.writing_mode);
|
||||
|
@ -1106,7 +1122,7 @@ impl InlineFormattingContextLayout<'_> {
|
|||
if needs_placement_later {
|
||||
self.current_line.has_floats_waiting_to_be_placed = true;
|
||||
} else {
|
||||
self.place_float_fragment(&mut float_item.fragment);
|
||||
self.place_float_fragment(&mut float_fragment);
|
||||
float_item.needs_placement = false;
|
||||
}
|
||||
|
||||
|
@ -1657,6 +1673,11 @@ impl InlineFormattingContext {
|
|||
Au::zero()
|
||||
};
|
||||
|
||||
// Clear any cached inline fragments from previous layouts.
|
||||
for inline_box in self.inline_boxes.iter() {
|
||||
inline_box.borrow().base.clear_fragments();
|
||||
}
|
||||
|
||||
let style = containing_block.style;
|
||||
|
||||
// It's unfortunate that it isn't possible to get this during IFC text processing, but in
|
||||
|
@ -2055,6 +2076,10 @@ impl IndependentFormattingContext {
|
|||
size.inline,
|
||||
SegmentContentFlags::empty(),
|
||||
);
|
||||
|
||||
let fragment = ArcRefCell::new(fragment);
|
||||
self.base.set_fragment(Fragment::Box(fragment.clone()));
|
||||
|
||||
layout.push_line_item_to_unbreakable_segment(LineItem::Atomic(
|
||||
layout.current_inline_box_identifier(),
|
||||
AtomicLineItem {
|
||||
|
@ -2131,11 +2156,15 @@ impl IndependentFormattingContext {
|
|||
|
||||
impl FloatBox {
|
||||
fn layout_into_line_items(&self, layout: &mut InlineFormattingContextLayout) {
|
||||
let fragment = self.layout(
|
||||
let fragment = ArcRefCell::new(self.layout(
|
||||
layout.layout_context,
|
||||
layout.positioning_context,
|
||||
layout.containing_block,
|
||||
);
|
||||
));
|
||||
|
||||
self.contents
|
||||
.base
|
||||
.set_fragment(Fragment::Box(fragment.clone()));
|
||||
layout.push_line_item_to_unbreakable_segment(LineItem::Float(
|
||||
layout.current_inline_box_identifier(),
|
||||
FloatLineItem {
|
||||
|
@ -2150,7 +2179,7 @@ fn place_pending_floats(ifc: &mut InlineFormattingContextLayout, line_items: &mu
|
|||
for item in line_items.iter_mut() {
|
||||
if let LineItem::Float(_, float_line_item) = item {
|
||||
if float_line_item.needs_placement {
|
||||
ifc.place_float_fragment(&mut float_line_item.fragment);
|
||||
ifc.place_float_fragment(&mut float_line_item.fragment.borrow_mut());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -91,23 +91,25 @@ pub(crate) enum BlockLevelBox {
|
|||
|
||||
impl BlockLevelBox {
|
||||
pub(crate) fn invalidate_cached_fragment(&self) {
|
||||
self.with_base(LayoutBoxBase::invalidate_cached_fragment);
|
||||
}
|
||||
|
||||
pub(crate) fn fragments(&self) -> Vec<Fragment> {
|
||||
self.with_base(LayoutBoxBase::fragments)
|
||||
}
|
||||
|
||||
pub(crate) fn with_base<T>(&self, callback: impl Fn(&LayoutBoxBase) -> T) -> T {
|
||||
match self {
|
||||
BlockLevelBox::Independent(independent_formatting_context) => {
|
||||
&independent_formatting_context.base
|
||||
callback(&independent_formatting_context.base)
|
||||
},
|
||||
BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(positioned_box) => {
|
||||
positioned_box
|
||||
.borrow()
|
||||
.context
|
||||
.base
|
||||
.invalidate_cached_fragment();
|
||||
return;
|
||||
callback(&positioned_box.borrow().context.base)
|
||||
},
|
||||
BlockLevelBox::OutOfFlowFloatBox(float_box) => &float_box.contents.base,
|
||||
BlockLevelBox::OutsideMarker(outside_marker) => &outside_marker.base,
|
||||
BlockLevelBox::SameFormattingContextBlock { base, .. } => base,
|
||||
BlockLevelBox::OutOfFlowFloatBox(float_box) => callback(&float_box.contents.base),
|
||||
BlockLevelBox::OutsideMarker(outside_marker) => callback(&outside_marker.base),
|
||||
BlockLevelBox::SameFormattingContextBlock { base, .. } => callback(base),
|
||||
}
|
||||
.invalidate_cached_fragment();
|
||||
}
|
||||
|
||||
fn contains_floats(&self) -> bool {
|
||||
|
@ -770,7 +772,7 @@ impl BlockLevelBox {
|
|||
collapsible_with_parent_start_margin: Option<CollapsibleWithParentStartMargin>,
|
||||
ignore_block_margins_for_stretch: LogicalSides1D<bool>,
|
||||
) -> Fragment {
|
||||
match self {
|
||||
let fragment = match self {
|
||||
BlockLevelBox::SameFormattingContextBlock { base, contents, .. } => Fragment::Box(
|
||||
ArcRefCell::new(positioning_context.layout_maybe_position_relative_fragment(
|
||||
layout_context,
|
||||
|
@ -836,7 +838,11 @@ impl BlockLevelBox {
|
|||
sequential_layout_state,
|
||||
collapsible_with_parent_start_margin,
|
||||
),
|
||||
}
|
||||
};
|
||||
|
||||
self.with_base(|base| base.set_fragment(fragment.clone()));
|
||||
|
||||
fragment
|
||||
}
|
||||
|
||||
fn inline_content_sizes(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue