diff --git a/components/layout_2020/flexbox/layout.rs b/components/layout_2020/flexbox/layout.rs index 19619375b99..f4cc3e2cf4c 100644 --- a/components/layout_2020/flexbox/layout.rs +++ b/components/layout_2020/flexbox/layout.rs @@ -11,7 +11,7 @@ use crate::formatting_contexts::{IndependentFormattingContext, IndependentLayout use crate::fragment_tree::{BoxFragment, CollapsedBlockMargins, Fragment}; use crate::geom::flow_relative::{Rect, Sides, Vec2}; use crate::geom::LengthOrAuto; -use crate::positioned::{AbsolutelyPositionedBox, PositioningContext}; +use crate::positioned::{AbsolutelyPositionedBox, PositioningContext, PositioningContextLength}; use crate::sizing::ContentSizes; use crate::style_ext::ComputedValuesExt; use crate::ContainingBlock; @@ -194,8 +194,10 @@ impl FlexContainer { let (fragment, mut child_positioning_context) = flex_item_fragments.next().unwrap(); let fragment = Fragment::Box(fragment); - child_positioning_context - .adjust_static_position_of_hoisted_fragments(&fragment); + child_positioning_context.adjust_static_position_of_hoisted_fragments( + &fragment, + PositioningContextLength::zero(), + ); positioning_context.append(child_positioning_context); fragment }, diff --git a/components/layout_2020/flow/inline.rs b/components/layout_2020/flow/inline.rs index 4eae022cbd3..7dcb04b53f3 100644 --- a/components/layout_2020/flow/inline.rs +++ b/components/layout_2020/flow/inline.rs @@ -786,8 +786,7 @@ fn layout_atomic( let margin = pbm.margin.auto_is(Length::zero); let pbm_sums = &(&pbm.padding + &pbm.border) + &margin; let position = style.clone_position(); - - let mut child_positioning_context = None; + let positioning_context_length_before_layout = ifc.positioning_context.len(); // We need to know the inline size of the atomic before deciding whether to do the line break. let mut fragment = match atomic { @@ -854,15 +853,9 @@ fn layout_atomic( "Mixed writing modes are not supported yet" ); - let collects_for_nearest_positioned_ancestor = ifc - .positioning_context - .collects_for_nearest_positioned_ancestor(); - child_positioning_context = Some(PositioningContext::new_for_subtree( - collects_for_nearest_positioned_ancestor, - )); let independent_layout = non_replaced.layout( layout_context, - child_positioning_context.as_mut().unwrap(), + &mut ifc.positioning_context, &containing_block_for_children, ); @@ -916,11 +909,11 @@ fn layout_atomic( start_corner += &relative_adjustement(atomic.style(), ifc.containing_block) } - if let Some(mut child_positioning_context) = child_positioning_context.take() { - child_positioning_context - .adjust_static_position_of_hoisted_fragments_with_offset(&start_corner); - ifc.positioning_context.append(child_positioning_context); - } + ifc.positioning_context + .adjust_static_position_of_hoisted_fragments_with_offset( + &start_corner, + positioning_context_length_before_layout, + ); fragment.content_rect.start_corner = start_corner; diff --git a/components/layout_2020/flow/mod.rs b/components/layout_2020/flow/mod.rs index 10449d50274..e5695655296 100644 --- a/components/layout_2020/flow/mod.rs +++ b/components/layout_2020/flow/mod.rs @@ -15,7 +15,7 @@ use crate::fragment_tree::{ BaseFragmentInfo, BoxFragment, CollapsedBlockMargins, CollapsedMargin, Fragment, }; use crate::geom::flow_relative::{Rect, Sides, Vec2}; -use crate::positioned::{AbsolutelyPositionedBox, PositioningContext}; +use crate::positioned::{AbsolutelyPositionedBox, PositioningContext, PositioningContextLength}; use crate::replaced::ReplacedContent; use crate::sizing::{self, ContentSizes}; use crate::style_ext::{ComputedValuesExt, PaddingBorderMargin}; @@ -449,7 +449,10 @@ fn layout_block_level_children_in_parallel( .into_iter() .map(|(mut fragment, mut child_positioning_context)| { placement_state.place_fragment(&mut fragment, None); - child_positioning_context.adjust_static_position_of_hoisted_fragments(&fragment); + child_positioning_context.adjust_static_position_of_hoisted_fragments( + &fragment, + PositioningContextLength::zero(), + ); positioning_context.append(child_positioning_context); fragment }) @@ -472,8 +475,6 @@ fn layout_block_level_children_sequentially( collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin, ) -> FlowLayout { let mut placement_state = PlacementState::new(collapsible_with_parent_start_margin); - let collects_for_nearest_positioned_ancestor = - positioning_context.collects_for_nearest_positioned_ancestor(); // Because floats are involved, we do layout for this block formatting context in tree // order without parallelism. This enables mutable access to a `SequentialLayoutState` that @@ -481,11 +482,10 @@ fn layout_block_level_children_sequentially( let fragments = child_boxes .iter() .map(|child_box| { - let mut child_positioning_context = - PositioningContext::new_for_subtree(collects_for_nearest_positioned_ancestor); + let positioning_context_length_before_layout = positioning_context.len(); let mut fragment = child_box.borrow_mut().layout( layout_context, - &mut child_positioning_context, + positioning_context, containing_block, Some(&mut *sequential_layout_state), Some(CollapsibleWithParentStartMargin( @@ -494,9 +494,10 @@ fn layout_block_level_children_sequentially( ); placement_state.place_fragment(&mut fragment, Some(sequential_layout_state)); - - child_positioning_context.adjust_static_position_of_hoisted_fragments(&fragment); - positioning_context.append(child_positioning_context); + positioning_context.adjust_static_position_of_hoisted_fragments( + &fragment, + positioning_context_length_before_layout, + ); fragment }) diff --git a/components/layout_2020/positioned.rs b/components/layout_2020/positioned.rs index fa94773965b..85c4731686d 100644 --- a/components/layout_2020/positioned.rs +++ b/components/layout_2020/positioned.rs @@ -158,13 +158,16 @@ impl PositioningContext { /// 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. + /// This function is used to update the static position of hoisted boxes added after + /// the given index at every level of the fragment tree as the hoisted fragments move + /// up to their containing blocks. Once an ancestor fragment is laid out, this + /// function can be used to aggregate its offset to any descendent boxes that are + /// being hoisted. In this case, the appropriate index to use is the result of + /// [`PositioningContext::len()`] cached before laying out the [`Fragment`]. pub(crate) fn adjust_static_position_of_hoisted_fragments( &mut self, parent_fragment: &Fragment, + index: PositioningContextLength, ) { let start_offset = match &parent_fragment { Fragment::Box(b) | Fragment::Float(b) => &b.content_rect.start_corner, @@ -172,13 +175,14 @@ impl PositioningContext { Fragment::Anonymous(a) => &a.rect.start_corner, _ => unreachable!(), }; - self.adjust_static_position_of_hoisted_fragments_with_offset(start_offset); + self.adjust_static_position_of_hoisted_fragments_with_offset(start_offset, index); } /// See documentation for [adjust_static_position_of_hoisted_fragments]. pub(crate) fn adjust_static_position_of_hoisted_fragments_with_offset( &mut self, start_offset: &Vec2, + index: PositioningContextLength, ) { let update_fragment_if_needed = |hoisted_fragment: &mut HoistedAbsolutelyPositionedBox| { let mut fragment = hoisted_fragment.fragment.borrow_mut(); @@ -193,10 +197,14 @@ impl PositioningContext { self.for_nearest_positioned_ancestor .as_mut() .map(|hoisted_boxes| { - hoisted_boxes.iter_mut().for_each(update_fragment_if_needed); + hoisted_boxes + .iter_mut() + .skip(index.for_nearest_positioned_ancestor) + .for_each(update_fragment_if_needed); }); self.for_nearest_containing_block_for_all_descendants .iter_mut() + .skip(index.for_nearest_containing_block_for_all_descendants) .for_each(update_fragment_if_needed); } @@ -343,6 +351,43 @@ impl PositioningContext { .as_mut() .map(|v| v.clear()); } + + /// Get the length of this [PositioningContext]. + pub(crate) fn len(&self) -> PositioningContextLength { + PositioningContextLength { + for_nearest_positioned_ancestor: self + .for_nearest_positioned_ancestor + .as_ref() + .map_or(0, |vec| vec.len()), + for_nearest_containing_block_for_all_descendants: self + .for_nearest_containing_block_for_all_descendants + .len(), + } + } +} + +/// A data structure which stores the size of a positioning context. +pub(crate) struct PositioningContextLength { + /// The number of boxes that will be hoisted the the nearest positioned ancestor for + /// layout. + for_nearest_positioned_ancestor: usize, + /// The number of boxes that will be hoisted the the nearest ancestor which + /// establishes a containing block for all descendants for layout. + for_nearest_containing_block_for_all_descendants: usize, +} + +impl Zero for PositioningContextLength { + fn zero() -> Self { + PositioningContextLength { + for_nearest_positioned_ancestor: 0, + for_nearest_containing_block_for_all_descendants: 0, + } + } + + fn is_zero(&self) -> bool { + self.for_nearest_positioned_ancestor == 0 && + self.for_nearest_containing_block_for_all_descendants == 0 + } } impl HoistedAbsolutelyPositionedBox { @@ -640,6 +685,7 @@ impl HoistedAbsolutelyPositionedBox { // adjust it to account for the start corner of this absolute. positioning_context.adjust_static_position_of_hoisted_fragments_with_offset( &new_fragment.content_rect.start_corner, + PositioningContextLength::zero(), ); for_nearest_containing_block_for_all_descendants