mirror of
https://github.com/servo/servo.git
synced 2025-08-06 14:10:11 +01:00
Simplify layout of absolutes with static insets
Absolutes with static insets need to be laid out at their ancestor containing blocks, but their position is dependent on their parent's layout. The static layout position is passed up the tree during hoisting and ancestors each add their own offset to the position until it is relative to the containing block that contains the absolute. This is currently done with a closure and a fairly tricky "tree rank" numbering system that needs to be threaded through the entire layout. This change replaces that system. Every time a child is laid out we create a positioning context to hold any absolute children (this can be optimized away at a later time). At each of these moments, we call a method to aggregate offsets to the static insets of hoisted absolutes. This makes the logic easier to follow and will also allow implementing this behavior for inline-blocks, which was impossible with the old system.
This commit is contained in:
parent
836ae5fa48
commit
459a7d26aa
7 changed files with 174 additions and 235 deletions
|
@ -39,11 +39,6 @@ pub(crate) struct PositioningContext {
|
|||
pub(crate) struct HoistedAbsolutelyPositionedBox {
|
||||
absolutely_positioned_box: ArcRefCell<AbsolutelyPositionedBox>,
|
||||
|
||||
/// The rank of the child from which this absolutely positioned fragment
|
||||
/// came from, when doing the layout of a block container. Used to compute
|
||||
/// static positions when going up the tree.
|
||||
pub(crate) tree_rank: usize,
|
||||
|
||||
/// A reference to a Fragment which is shared between this `HoistedAbsolutelyPositionedBox`
|
||||
/// and its placeholder `AbsoluteOrFixedPositionedFragment` in the original tree position.
|
||||
/// This will be used later in order to paint this hoisted box in tree order.
|
||||
|
@ -72,7 +67,6 @@ impl AbsolutelyPositionedBox {
|
|||
pub(crate) fn to_hoisted(
|
||||
self_: ArcRefCell<Self>,
|
||||
initial_start_corner: Vec2<Length>,
|
||||
tree_rank: usize,
|
||||
containing_block: &ContainingBlock,
|
||||
) -> HoistedAbsolutelyPositionedBox {
|
||||
fn absolute_box_offsets(
|
||||
|
@ -112,7 +106,6 @@ impl AbsolutelyPositionedBox {
|
|||
}
|
||||
};
|
||||
HoistedAbsolutelyPositionedBox {
|
||||
tree_rank,
|
||||
fragment: ArcRefCell::new(HoistedSharedFragment::new(box_offsets)),
|
||||
absolutely_positioned_box: self_,
|
||||
}
|
||||
|
@ -127,7 +120,11 @@ impl PositioningContext {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn new_for_rayon(collects_for_nearest_positioned_ancestor: bool) -> Self {
|
||||
/// Create a [PositioninContext] to use for laying out a subtree. The idea is that
|
||||
/// when subtree layout is finished, the newly hoisted boxes can be processed
|
||||
/// (normally adjusting their static insets) and then appended to the parent
|
||||
/// [PositioningContext].
|
||||
pub(crate) fn new_for_subtree(collects_for_nearest_positioned_ancestor: bool) -> Self {
|
||||
Self {
|
||||
for_nearest_positioned_ancestor: if collects_for_nearest_positioned_ancestor {
|
||||
Some(Vec::new())
|
||||
|
@ -155,6 +152,47 @@ impl PositioningContext {
|
|||
}
|
||||
}
|
||||
|
||||
/// Absolute and fixed position fragments are hoisted up to their containing blocks
|
||||
/// from their tree position. When these fragments have static inset start positions,
|
||||
/// that position (relative to the ancestor containing block) needs to be included
|
||||
/// with the hoisted fragment so that it can be laid out properly at the containing
|
||||
/// block.
|
||||
///
|
||||
/// This function is used to update that static position at every level of the
|
||||
/// fragment tree as the hoisted fragments move back up to their containing blocks.
|
||||
/// Once an ancestor fragment is laid out, this function can be used to aggregate its
|
||||
/// offset on the way back up.
|
||||
pub(crate) fn adjust_static_position_of_hoisted_fragments(
|
||||
&mut self,
|
||||
parent_fragment: &Fragment,
|
||||
) {
|
||||
let fragment_rect = match &parent_fragment {
|
||||
Fragment::Box(b) => &b.content_rect,
|
||||
Fragment::AbsoluteOrFixedPositioned(_) | Fragment::Float(_) => return,
|
||||
Fragment::Anonymous(a) => &a.rect,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let update_fragment_if_needed = |hoisted_fragment: &mut HoistedAbsolutelyPositionedBox| {
|
||||
let mut fragment = hoisted_fragment.fragment.borrow_mut();
|
||||
if let AbsoluteBoxOffsets::StaticStart { start } = &mut fragment.box_offsets.inline {
|
||||
*start += fragment_rect.start_corner.inline;
|
||||
}
|
||||
if let AbsoluteBoxOffsets::StaticStart { start } = &mut fragment.box_offsets.block {
|
||||
*start += fragment_rect.start_corner.block;
|
||||
}
|
||||
};
|
||||
|
||||
self.for_nearest_positioned_ancestor
|
||||
.as_mut()
|
||||
.map(|hoisted_boxes| {
|
||||
hoisted_boxes.iter_mut().for_each(update_fragment_if_needed);
|
||||
});
|
||||
self.for_nearest_containing_block_for_all_descendants
|
||||
.iter_mut()
|
||||
.for_each(update_fragment_if_needed);
|
||||
}
|
||||
|
||||
/// Given `fragment_layout_fn`, a closure which lays out a fragment in a provided
|
||||
/// `PositioningContext`, create a new positioning context if necessary for the fragment and
|
||||
/// lay out the fragment and all its children. Returns the newly created `BoxFragment`.
|
||||
|
@ -267,36 +305,6 @@ impl PositioningContext {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn adjust_static_positions(
|
||||
&mut self,
|
||||
tree_rank_in_parent: usize,
|
||||
f: impl FnOnce(&mut Self) -> Vec<Fragment>,
|
||||
) -> Vec<Fragment> {
|
||||
let for_containing_block_for_all_descendants =
|
||||
self.for_nearest_containing_block_for_all_descendants.len();
|
||||
let for_nearest_so_far = self
|
||||
.for_nearest_positioned_ancestor
|
||||
.as_ref()
|
||||
.map(|v| v.len());
|
||||
|
||||
let fragments = f(self);
|
||||
|
||||
adjust_static_positions(
|
||||
&mut self.for_nearest_containing_block_for_all_descendants
|
||||
[for_containing_block_for_all_descendants..],
|
||||
&fragments,
|
||||
tree_rank_in_parent,
|
||||
);
|
||||
if let Some(nearest) = &mut self.for_nearest_positioned_ancestor {
|
||||
adjust_static_positions(
|
||||
&mut nearest[for_nearest_so_far.unwrap()..],
|
||||
&fragments,
|
||||
tree_rank_in_parent,
|
||||
);
|
||||
}
|
||||
fragments
|
||||
}
|
||||
|
||||
pub(crate) fn layout_initial_containing_block_children(
|
||||
&mut self,
|
||||
layout_context: &LayoutContext,
|
||||
|
@ -512,7 +520,6 @@ impl HoistedAbsolutelyPositionedBox {
|
|||
containing_block_for_children.style.writing_mode,
|
||||
"Mixed writing modes are not supported yet"
|
||||
);
|
||||
let dummy_tree_rank = 0;
|
||||
|
||||
// Clear the context since we will lay out the same descendants
|
||||
// more than once. Otherwise, absolute descendants will create
|
||||
|
@ -524,7 +531,6 @@ impl HoistedAbsolutelyPositionedBox {
|
|||
layout_context,
|
||||
&mut positioning_context,
|
||||
&containing_block_for_children,
|
||||
dummy_tree_rank,
|
||||
);
|
||||
let block_size = size.auto_is(|| independent_layout.content_block_size);
|
||||
Result {
|
||||
|
@ -739,34 +745,6 @@ impl<'a> AbsoluteAxisSolver<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn adjust_static_positions(
|
||||
absolutely_positioned_fragments: &mut [HoistedAbsolutelyPositionedBox],
|
||||
child_fragments: &[Fragment],
|
||||
tree_rank_in_parent: usize,
|
||||
) {
|
||||
for abspos_fragment in absolutely_positioned_fragments {
|
||||
let original_tree_rank = abspos_fragment.tree_rank;
|
||||
abspos_fragment.tree_rank = tree_rank_in_parent;
|
||||
|
||||
let child_fragment_rect = match &child_fragments[original_tree_rank] {
|
||||
Fragment::Box(b) => &b.content_rect,
|
||||
Fragment::AbsoluteOrFixedPositioned(_) | Fragment::Float(_) => continue,
|
||||
Fragment::Anonymous(a) => &a.rect,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let mut shared_fragment = abspos_fragment.fragment.borrow_mut();
|
||||
|
||||
if let AbsoluteBoxOffsets::StaticStart { start } = &mut shared_fragment.box_offsets.inline {
|
||||
*start += child_fragment_rect.start_corner.inline;
|
||||
}
|
||||
|
||||
if let AbsoluteBoxOffsets::StaticStart { start } = &mut shared_fragment.box_offsets.block {
|
||||
*start += child_fragment_rect.start_corner.block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn vec_append_owned<T>(a: &mut Vec<T>, mut b: Vec<T>) {
|
||||
if a.is_empty() {
|
||||
*a = b
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue