diff options
Diffstat (limited to 'components/layout')
-rw-r--r-- | components/layout/dom.rs | 12 | ||||
-rw-r--r-- | components/layout/flexbox/layout.rs | 22 | ||||
-rw-r--r-- | components/layout/flow/float.rs | 3 | ||||
-rw-r--r-- | components/layout/flow/inline/line.rs | 3 | ||||
-rw-r--r-- | components/layout/flow/inline/mod.rs | 3 | ||||
-rw-r--r-- | components/layout/flow/mod.rs | 9 | ||||
-rw-r--r-- | components/layout/flow/root.rs | 3 | ||||
-rw-r--r-- | components/layout/formatting_contexts.rs | 5 | ||||
-rw-r--r-- | components/layout/positioned.rs | 339 | ||||
-rw-r--r-- | components/layout/replaced.rs | 35 | ||||
-rw-r--r-- | components/layout/style_ext.rs | 20 | ||||
-rw-r--r-- | components/layout/table/construct.rs | 27 | ||||
-rw-r--r-- | components/layout/table/layout.rs | 47 | ||||
-rw-r--r-- | components/layout/taffy/layout.rs | 71 | ||||
-rw-r--r-- | components/layout/taffy/mod.rs | 5 |
15 files changed, 224 insertions, 380 deletions
diff --git a/components/layout/dom.rs b/components/layout/dom.rs index add4b3ac2d5..8f2697e670a 100644 --- a/components/layout/dom.rs +++ b/components/layout/dom.rs @@ -15,8 +15,7 @@ use script_layout_interface::wrapper_traits::{ LayoutDataTrait, LayoutNode, ThreadSafeLayoutElement, ThreadSafeLayoutNode, }; use script_layout_interface::{ - GenericLayoutDataTrait, HTMLCanvasDataSource, LayoutElementType, - LayoutNodeType as ScriptLayoutNodeType, + GenericLayoutDataTrait, LayoutElementType, LayoutNodeType as ScriptLayoutNodeType, }; use servo_arc::Arc as ServoArc; use style::properties::ComputedValues; @@ -29,7 +28,7 @@ use crate::flow::BlockLevelBox; use crate::flow::inline::InlineItem; use crate::fragment_tree::Fragment; use crate::geom::PhysicalSize; -use crate::replaced::{CanvasInfo, CanvasSource}; +use crate::replaced::CanvasInfo; use crate::table::TableLevelBox; use crate::taffy::TaffyItemBox; @@ -220,12 +219,7 @@ where fn as_canvas(self) -> Option<(CanvasInfo, PhysicalSize<f64>)> { let node = self.to_threadsafe(); let canvas_data = node.canvas_data()?; - let source = match canvas_data.source { - HTMLCanvasDataSource::WebGL(texture_id) => CanvasSource::WebGL(texture_id), - HTMLCanvasDataSource::Image(image_key) => CanvasSource::Image(image_key), - HTMLCanvasDataSource::WebGPU(image_key) => CanvasSource::WebGPU(image_key), - HTMLCanvasDataSource::Empty => CanvasSource::Empty, - }; + let source = canvas_data.source; Some(( CanvasInfo { source }, PhysicalSize::new(canvas_data.width.into(), canvas_data.height.into()), diff --git a/components/layout/flexbox/layout.rs b/components/layout/flexbox/layout.rs index 77069236787..e69b792e272 100644 --- a/components/layout/flexbox/layout.rs +++ b/components/layout/flexbox/layout.rs @@ -49,7 +49,6 @@ use crate::{ struct FlexContext<'a> { config: FlexContainerConfig, layout_context: &'a LayoutContext<'a>, - positioning_context: &'a mut PositioningContext, containing_block: &'a ContainingBlock<'a>, // For items container_inner_size_constraint: FlexRelativeVec2<SizeConstraint>, } @@ -657,7 +656,6 @@ impl FlexContainer { let mut flex_context = FlexContext { config: self.config.clone(), layout_context, - positioning_context, containing_block, // https://drafts.csswg.org/css-flexbox/#definite-sizes container_inner_size_constraint: self.config.flex_axis.vec2_to_flex_relative( @@ -1774,16 +1772,8 @@ impl FlexItem<'_> { non_stretch_layout_result: Option<&mut FlexItemLayoutResult>, ) -> Option<FlexItemLayoutResult> { let containing_block = flex_context.containing_block; - let mut positioning_context = PositioningContext::new_for_style(self.box_.style()) - .unwrap_or_else(|| { - PositioningContext::new_for_subtree( - flex_context - .positioning_context - .collects_for_nearest_positioned_ancestor(), - ) - }); - let independent_formatting_context = &self.box_.independent_formatting_context; + let mut positioning_context = PositioningContext::default(); let item_writing_mode = independent_formatting_context.style().writing_mode; let item_is_horizontal = item_writing_mode.is_horizontal(); let flex_axis = flex_context.config.flex_axis; @@ -2616,15 +2606,7 @@ impl FlexItemBox { cross_size_stretches_to_container_size: bool, intrinsic_sizing_mode: IntrinsicSizingMode, ) -> Au { - let mut positioning_context = PositioningContext::new_for_style(self.style()) - .unwrap_or_else(|| { - PositioningContext::new_for_subtree( - flex_context - .positioning_context - .collects_for_nearest_positioned_ancestor(), - ) - }); - + let mut positioning_context = PositioningContext::default(); let style = self.independent_formatting_context.style(); match &self.independent_formatting_context.contents { IndependentFormattingContextContents::Replaced(replaced) => { diff --git a/components/layout/flow/float.rs b/components/layout/flow/float.rs index 0570ce0d0f4..dbc50c07603 100644 --- a/components/layout/flow/float.rs +++ b/components/layout/flow/float.rs @@ -913,11 +913,10 @@ impl FloatBox { positioning_context: &mut PositioningContext, containing_block: &ContainingBlock, ) -> BoxFragment { - let style = self.contents.style().clone(); positioning_context.layout_maybe_position_relative_fragment( layout_context, containing_block, - &style, + &self.contents.base, |positioning_context| { self.contents .layout_float_or_atomic_inline( diff --git a/components/layout/flow/inline/line.rs b/components/layout/flow/inline/line.rs index c42f32c9242..80bab1080ed 100644 --- a/components/layout/flow/inline/line.rs +++ b/components/layout/flow/inline/line.rs @@ -326,13 +326,12 @@ impl LineItemLayout<'_, '_> { let inline_box = self.layout.ifc.inline_boxes.get(identifier); let inline_box = &*(inline_box.borrow()); - let style = &inline_box.base.style; let space_above_baseline = inline_box_state.calculate_space_above_baseline(); let block_start_offset = self.calculate_inline_box_block_start(inline_box_state, space_above_baseline); let positioning_context_or_start_offset_in_parent = - match PositioningContext::new_for_style(style) { + match PositioningContext::new_for_layout_box_base(&inline_box.base) { Some(positioning_context) => Either::Left(positioning_context), None => Either::Right(self.current_positioning_context_mut().len()), }; diff --git a/components/layout/flow/inline/mod.rs b/components/layout/flow/inline/mod.rs index dabb9773410..2023f4e7174 100644 --- a/components/layout/flow/inline/mod.rs +++ b/components/layout/flow/inline/mod.rs @@ -2004,8 +2004,7 @@ impl IndependentFormattingContext { bidi_level: Level, ) { // We need to know the inline size of the atomic before deciding whether to do the line break. - let mut child_positioning_context = PositioningContext::new_for_style(self.style()) - .unwrap_or_else(|| PositioningContext::new_for_subtree(true)); + let mut child_positioning_context = PositioningContext::default(); let IndependentFloatOrAtomicLayoutResult { mut fragment, baselines, diff --git a/components/layout/flow/mod.rs b/components/layout/flow/mod.rs index d983e8592c4..772b150ae1c 100644 --- a/components/layout/flow/mod.rs +++ b/components/layout/flow/mod.rs @@ -689,16 +689,13 @@ fn layout_block_level_children_in_parallel( placement_state: &mut PlacementState, ignore_block_margins_for_stretch: LogicalSides1D<bool>, ) -> Vec<Fragment> { - let collects_for_nearest_positioned_ancestor = - positioning_context.collects_for_nearest_positioned_ancestor(); let mut layout_results: Vec<(Fragment, PositioningContext)> = Vec::with_capacity(child_boxes.len()); child_boxes .par_iter() .map(|child_box| { - let mut child_positioning_context = - PositioningContext::new_for_subtree(collects_for_nearest_positioned_ancestor); + let mut child_positioning_context = PositioningContext::default(); let fragment = child_box.borrow().layout( layout_context, &mut child_positioning_context, @@ -779,7 +776,7 @@ impl BlockLevelBox { ArcRefCell::new(positioning_context.layout_maybe_position_relative_fragment( layout_context, containing_block, - &base.style, + base, |positioning_context| { layout_in_flow_non_replaced_block_level_same_formatting_context( layout_context, @@ -798,7 +795,7 @@ impl BlockLevelBox { positioning_context.layout_maybe_position_relative_fragment( layout_context, containing_block, - independent.style(), + &independent.base, |positioning_context| { independent.layout_in_flow_block_level( layout_context, diff --git a/components/layout/flow/root.rs b/components/layout/flow/root.rs index c6498eeed63..bb9ff1d337a 100644 --- a/components/layout/flow/root.rs +++ b/components/layout/flow/root.rs @@ -385,8 +385,7 @@ impl BoxTree { style, }; - let mut positioning_context = - PositioningContext::new_for_containing_block_for_all_descendants(); + let mut positioning_context = PositioningContext::default(); let independent_layout = self.root.layout( layout_context, &mut positioning_context, diff --git a/components/layout/formatting_contexts.rs b/components/layout/formatting_contexts.rs index 4661c44592c..4982d0dae1a 100644 --- a/components/layout/formatting_contexts.rs +++ b/components/layout/formatting_contexts.rs @@ -295,10 +295,7 @@ impl IndependentNonReplacedContents { ); } - let mut child_positioning_context = PositioningContext::new_for_subtree( - positioning_context.collects_for_nearest_positioned_ancestor(), - ); - + let mut child_positioning_context = PositioningContext::default(); let result = self.layout_without_caching( layout_context, &mut child_positioning_context, diff --git a/components/layout/positioned.rs b/components/layout/positioned.rs index 5f08e4e86c5..ff361396fa3 100644 --- a/components/layout/positioned.rs +++ b/components/layout/positioned.rs @@ -29,6 +29,7 @@ use crate::geom::{ PhysicalPoint, PhysicalRect, PhysicalSides, PhysicalSize, PhysicalVec, Size, Sizes, ToLogical, ToLogicalWithContainingBlock, }; +use crate::layout_box_base::LayoutBoxBase; use crate::sizing::ContentSizes; use crate::style_ext::{Clamp, ComputedValuesExt, ContentBoxSizesAndPBM, DisplayInside}; use crate::{ @@ -42,16 +43,6 @@ pub(crate) struct AbsolutelyPositionedBox { } #[derive(Clone, MallocSizeOf)] -pub(crate) struct PositioningContext { - for_nearest_positioned_ancestor: Option<Vec<HoistedAbsolutelyPositionedBox>>, - - // For nearest `containing block for all descendants` as defined by the CSS transforms - // spec. - // https://www.w3.org/TR/css-transforms-1/#containing-block-for-all-descendants - for_nearest_containing_block_for_all_descendants: Vec<HoistedAbsolutelyPositionedBox>, -} - -#[derive(Clone, MallocSizeOf)] pub(crate) struct HoistedAbsolutelyPositionedBox { absolutely_positioned_box: ArcRefCell<AbsolutelyPositionedBox>, @@ -103,45 +94,26 @@ impl AbsolutelyPositionedBox { } } -impl PositioningContext { - pub(crate) fn new_for_containing_block_for_all_descendants() -> Self { - Self { - for_nearest_positioned_ancestor: None, - for_nearest_containing_block_for_all_descendants: Vec::new(), - } - } - - /// Create a [PositioningContext] 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()) - } else { - None - }, - for_nearest_containing_block_for_all_descendants: Vec::new(), - } - } +#[derive(Clone, Default, MallocSizeOf)] +pub(crate) struct PositioningContext { + absolutes: Vec<HoistedAbsolutelyPositionedBox>, +} - pub(crate) fn collects_for_nearest_positioned_ancestor(&self) -> bool { - self.for_nearest_positioned_ancestor.is_some() +impl PositioningContext { + #[inline] + pub(crate) fn new_for_layout_box_base(layout_box_base: &LayoutBoxBase) -> Option<Self> { + Self::new_for_style_and_fragment_flags( + &layout_box_base.style, + &layout_box_base.base_fragment_info.flags, + ) } - pub(crate) fn new_for_style(style: &ComputedValues) -> Option<Self> { - // NB: We never make PositioningContexts for replaced elements, which is why we always - // pass false here. - if style.establishes_containing_block_for_all_descendants(FragmentFlags::empty()) { - Some(Self::new_for_containing_block_for_all_descendants()) - } else if style - .establishes_containing_block_for_absolute_descendants(FragmentFlags::empty()) - { - Some(Self { - for_nearest_positioned_ancestor: Some(Vec::new()), - for_nearest_containing_block_for_all_descendants: Vec::new(), - }) + fn new_for_style_and_fragment_flags( + style: &ComputedValues, + flags: &FragmentFlags, + ) -> Option<Self> { + if style.establishes_containing_block_for_absolute_descendants(*flags) { + Some(Self::default()) } else { None } @@ -184,20 +156,9 @@ impl PositioningContext { offset: &PhysicalVec<Au>, index: PositioningContextLength, ) { - if let Some(hoisted_boxes) = self.for_nearest_positioned_ancestor.as_mut() { - hoisted_boxes - .iter_mut() - .skip(index.for_nearest_positioned_ancestor) - .for_each(|hoisted_fragment| { - hoisted_fragment - .fragment - .borrow_mut() - .adjust_offsets(offset) - }) - } - self.for_nearest_containing_block_for_all_descendants + self.absolutes .iter_mut() - .skip(index.for_nearest_containing_block_for_all_descendants) + .skip(index.0) .for_each(|hoisted_fragment| { hoisted_fragment .fragment @@ -213,38 +174,89 @@ impl PositioningContext { &mut self, layout_context: &LayoutContext, containing_block: &ContainingBlock, - style: &ComputedValues, + base: &LayoutBoxBase, fragment_layout_fn: impl FnOnce(&mut Self) -> BoxFragment, ) -> BoxFragment { - // Try to create a context, but if one isn't necessary, simply create the fragment - // using the given closure and the current `PositioningContext`. - let mut new_context = match Self::new_for_style(style) { - Some(new_context) => new_context, - None => return fragment_layout_fn(self), - }; + // If a new `PositioningContext` isn't necessary, simply create the fragment using + // the given closure and the current `PositioningContext`. + let establishes_containing_block_for_absolutes = base + .style + .establishes_containing_block_for_absolute_descendants(base.base_fragment_info.flags); + if !establishes_containing_block_for_absolutes { + return fragment_layout_fn(self); + } + let mut new_context = PositioningContext::default(); let mut new_fragment = fragment_layout_fn(&mut new_context); - new_context.layout_collected_children(layout_context, &mut new_fragment); - // If the new context has any hoisted boxes for the nearest containing block for - // pass them up the tree. + // Lay out all of the absolutely positioned children for this fragment, and, if it + // isn't a containing block for fixed elements, then pass those up to the parent. + new_context.layout_collected_children(layout_context, &mut new_fragment); self.append(new_context); - if style.clone_position() == Position::Relative { - new_fragment.content_rect.origin += relative_adjustement(style, containing_block) + if base.style.clone_position() == Position::Relative { + new_fragment.content_rect.origin += relative_adjustement(&base.style, containing_block) .to_physical_vector(containing_block.style.writing_mode) } new_fragment } + fn take_boxes_for_fragment( + &mut self, + new_fragment: &BoxFragment, + boxes_to_layout_out: &mut Vec<HoistedAbsolutelyPositionedBox>, + boxes_to_continue_hoisting_out: &mut Vec<HoistedAbsolutelyPositionedBox>, + ) { + debug_assert!( + new_fragment + .style + .establishes_containing_block_for_absolute_descendants(new_fragment.base.flags) + ); + + if new_fragment + .style + .establishes_containing_block_for_all_descendants(new_fragment.base.flags) + { + boxes_to_layout_out.append(&mut self.absolutes); + return; + } + + // TODO: This could potentially use `extract_if` when that is stabilized. + let (mut boxes_to_layout, mut boxes_to_continue_hoisting) = self + .absolutes + .drain(..) + .partition(|hoisted_box| hoisted_box.position() != Position::Fixed); + boxes_to_layout_out.append(&mut boxes_to_layout); + boxes_to_continue_hoisting_out.append(&mut boxes_to_continue_hoisting); + } + // Lay out the hoisted boxes collected into this `PositioningContext` and add them // to the given `BoxFragment`. - pub fn layout_collected_children( + pub(crate) fn layout_collected_children( &mut self, layout_context: &LayoutContext, new_fragment: &mut BoxFragment, ) { + if self.absolutes.is_empty() { + return; + } + + // Sometimes we create temporary PositioningContexts just to collect hoisted absolutes and + // then these are processed later. In that case and if this fragment doesn't establish a + // containing block for absolutes at all, we just do nothing. All hoisted fragments will + // later be passed up to a parent PositioningContext. + // + // Handling this case here, when the PositioningContext is completely ineffectual other than + // as a temporary container for hoisted boxes, means that callers can execute less conditional + // code. + if !new_fragment + .style + .establishes_containing_block_for_absolute_descendants(new_fragment.base.flags) + { + return; + } + let padding_rect = PhysicalRect::new( // Ignore the content rect’s position in its own containing block: PhysicalPoint::origin(), @@ -258,83 +270,58 @@ impl PositioningContext { style: &new_fragment.style, }; - let take_hoisted_boxes_pending_layout = - |context: &mut Self| match context.for_nearest_positioned_ancestor.as_mut() { - Some(fragments) => mem::take(fragments), - None => mem::take(&mut context.for_nearest_containing_block_for_all_descendants), - }; + let mut fixed_position_boxes_to_hoist = Vec::new(); + let mut boxes_to_layout = Vec::new(); + self.take_boxes_for_fragment( + new_fragment, + &mut boxes_to_layout, + &mut fixed_position_boxes_to_hoist, + ); - // Loop because it’s possible that we discover (the static position of) - // more absolutely-positioned boxes while doing layout for others. - let mut hoisted_boxes = take_hoisted_boxes_pending_layout(self); - let mut laid_out_child_fragments = Vec::new(); - while !hoisted_boxes.is_empty() { + // Laying out a `position: absolute` child (which only establishes a containing block for + // `position: absolute` descendants) can result in more `position: fixed` descendants + // collecting in `self.absolutes`. We need to loop here in order to keep either laying them + // out or putting them into `fixed_position_boxes_to_hoist`. We know there aren't any more + // when `self.absolutes` is empty. + while !boxes_to_layout.is_empty() { HoistedAbsolutelyPositionedBox::layout_many( layout_context, - &mut hoisted_boxes, - &mut laid_out_child_fragments, - &mut self.for_nearest_containing_block_for_all_descendants, + std::mem::take(&mut boxes_to_layout), + &mut new_fragment.children, + &mut self.absolutes, &containing_block, new_fragment.padding, ); - hoisted_boxes = take_hoisted_boxes_pending_layout(self); - } - - new_fragment.children.extend(laid_out_child_fragments); - } - pub(crate) fn push(&mut self, box_: HoistedAbsolutelyPositionedBox) { - if let Some(nearest) = &mut self.for_nearest_positioned_ancestor { - let position = box_ - .absolutely_positioned_box - .borrow() - .context - .style() - .clone_position(); - match position { - Position::Fixed => {}, // fall through - Position::Absolute => return nearest.push(box_), - Position::Static | Position::Relative | Position::Sticky => unreachable!(), - } + self.take_boxes_for_fragment( + new_fragment, + &mut boxes_to_layout, + &mut fixed_position_boxes_to_hoist, + ); } - self.for_nearest_containing_block_for_all_descendants - .push(box_) + + // We replace here instead of simply preserving these in `take_boxes_for_fragment` + // so that we don't have to continually re-iterate over them when laying out in the + // loop above. + self.absolutes = fixed_position_boxes_to_hoist; } - pub(crate) fn is_empty(&self) -> bool { - self.for_nearest_containing_block_for_all_descendants - .is_empty() && - self.for_nearest_positioned_ancestor - .as_ref() - .is_none_or(|vector| vector.is_empty()) + pub(crate) fn push(&mut self, hoisted_box: HoistedAbsolutelyPositionedBox) { + debug_assert!(matches!( + hoisted_box.position(), + Position::Absolute | Position::Fixed + )); + self.absolutes.push(hoisted_box); } - pub(crate) fn append(&mut self, other: Self) { - if other.is_empty() { + pub(crate) fn append(&mut self, mut other: Self) { + if other.absolutes.is_empty() { return; } - - vec_append_owned( - &mut self.for_nearest_containing_block_for_all_descendants, - other.for_nearest_containing_block_for_all_descendants, - ); - - match ( - self.for_nearest_positioned_ancestor.as_mut(), - other.for_nearest_positioned_ancestor, - ) { - (Some(us), Some(them)) => vec_append_owned(us, them), - (None, Some(them)) => { - // This is the case where we have laid out the absolute children in a containing - // block for absolutes and we then are passing up the fixed-position descendants - // to the containing block for all descendants. - vec_append_owned( - &mut self.for_nearest_containing_block_for_all_descendants, - them, - ); - }, - (None, None) => {}, - _ => unreachable!(), + if self.absolutes.is_empty() { + self.absolutes = other.absolutes; + } else { + self.absolutes.append(&mut other.absolutes) } } @@ -344,19 +331,16 @@ impl PositioningContext { initial_containing_block: &DefiniteContainingBlock, fragments: &mut Vec<Fragment>, ) { - debug_assert!(self.for_nearest_positioned_ancestor.is_none()); - - // Loop because it’s possible that we discover (the static position of) - // more absolutely-positioned boxes while doing layout for others. - while !self - .for_nearest_containing_block_for_all_descendants - .is_empty() - { + // Laying out a `position: absolute` child (which only establishes a containing block for + // `position: absolute` descendants) can result in more `position: fixed` descendants + // collecting in `self.absolutes`. We need to loop here in order to keep laying them out. We + // know there aren't any more when `self.absolutes` is empty. + while !self.absolutes.is_empty() { HoistedAbsolutelyPositionedBox::layout_many( layout_context, - &mut mem::take(&mut self.for_nearest_containing_block_for_all_descendants), + mem::take(&mut self.absolutes), fragments, - &mut self.for_nearest_containing_block_for_all_descendants, + &mut self.absolutes, initial_containing_block, Default::default(), ) @@ -365,58 +349,46 @@ impl PositioningContext { /// 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(), - } + PositioningContextLength(self.absolutes.len()) } /// Truncate this [PositioningContext] to the given [PositioningContextLength]. This /// is useful for "unhoisting" boxes in this context and returning it to the state at /// the time that [`PositioningContext::len()`] was called. pub(crate) fn truncate(&mut self, length: &PositioningContextLength) { - if let Some(vec) = self.for_nearest_positioned_ancestor.as_mut() { - vec.truncate(length.for_nearest_positioned_ancestor); - } - self.for_nearest_containing_block_for_all_descendants - .truncate(length.for_nearest_containing_block_for_all_descendants); + self.absolutes.truncate(length.0) } } /// A data structure which stores the size of a positioning context. #[derive(Clone, Copy, Debug, PartialEq)] -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, -} +pub(crate) struct PositioningContextLength(usize); impl Zero for PositioningContextLength { fn zero() -> Self { - PositioningContextLength { - for_nearest_positioned_ancestor: 0, - for_nearest_containing_block_for_all_descendants: 0, - } + Self(0) } fn is_zero(&self) -> bool { - self.for_nearest_positioned_ancestor == 0 && - self.for_nearest_containing_block_for_all_descendants == 0 + self.0.is_zero() } } impl HoistedAbsolutelyPositionedBox { + fn position(&self) -> Position { + let position = self + .absolutely_positioned_box + .borrow() + .context + .style() + .clone_position(); + assert!(position == Position::Fixed || position == Position::Absolute); + position + } + pub(crate) fn layout_many( layout_context: &LayoutContext, - boxes: &mut [Self], + mut boxes: Vec<Self>, fragments: &mut Vec<Fragment>, for_nearest_containing_block_for_all_descendants: &mut Vec<HoistedAbsolutelyPositionedBox>, containing_block: &DefiniteContainingBlock, @@ -463,7 +435,7 @@ impl HoistedAbsolutelyPositionedBox { pub(crate) fn layout( &mut self, layout_context: &LayoutContext, - for_nearest_containing_block_for_all_descendants: &mut Vec<HoistedAbsolutelyPositionedBox>, + hoisted_absolutes_from_children: &mut Vec<HoistedAbsolutelyPositionedBox>, containing_block: &DefiniteContainingBlock, containing_block_padding: PhysicalSides<Au>, ) -> Fragment { @@ -586,7 +558,7 @@ impl HoistedAbsolutelyPositionedBox { .sizes })); - let mut positioning_context = PositioningContext::new_for_style(&style).unwrap(); + let mut positioning_context = PositioningContext::default(); let mut new_fragment = { let content_size: LogicalVec2<Au>; let fragments; @@ -699,6 +671,10 @@ impl HoistedAbsolutelyPositionedBox { ) .with_specific_layout_info(specific_layout_info) }; + + // This is an absolutely positioned element, which means it also establishes a + // containing block for absolutes. We lay out any absolutely positioned children + // here and pass the rest to `hoisted_absolutes_from_children.` positioning_context.layout_collected_children(layout_context, &mut new_fragment); // Any hoisted boxes that remain in this positioning context are going to be hoisted @@ -711,8 +687,7 @@ impl HoistedAbsolutelyPositionedBox { PositioningContextLength::zero(), ); - for_nearest_containing_block_for_all_descendants - .extend(positioning_context.for_nearest_containing_block_for_all_descendants); + hoisted_absolutes_from_children.extend(positioning_context.absolutes); let fragment = Fragment::Box(ArcRefCell::new(new_fragment)); context.base.set_fragment(fragment.clone()); @@ -1014,14 +989,6 @@ impl AbsoluteAxisSolver<'_> { } } -fn vec_append_owned<T>(a: &mut Vec<T>, mut b: Vec<T>) { - if a.is_empty() { - *a = b - } else { - a.append(&mut b) - } -} - /// <https://drafts.csswg.org/css2/visuren.html#relative-positioning> pub(crate) fn relative_adjustement( style: &ComputedValues, diff --git a/components/layout/replaced.rs b/components/layout/replaced.rs index b82fb947074..bbebc57aa97 100644 --- a/components/layout/replaced.rs +++ b/components/layout/replaced.rs @@ -3,7 +3,6 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use std::cell::LazyCell; -use std::fmt; use std::sync::Arc; use app_units::Au; @@ -96,33 +95,9 @@ impl NaturalSizes { } } -#[derive(MallocSizeOf)] -pub(crate) enum CanvasSource { - WebGL(ImageKey), - Image(ImageKey), - WebGPU(ImageKey), - /// transparent black - Empty, -} - -impl fmt::Debug for CanvasSource { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "{}", - match *self { - CanvasSource::WebGL(_) => "WebGL", - CanvasSource::Image(_) => "Image", - CanvasSource::WebGPU(_) => "WebGPU", - CanvasSource::Empty => "Empty", - } - ) - } -} - #[derive(Debug, MallocSizeOf)] pub(crate) struct CanvasInfo { - pub source: CanvasSource, + pub source: Option<ImageKey>, } #[derive(Debug, MallocSizeOf)] @@ -388,12 +363,10 @@ impl ReplacedContents { return vec![]; } - let image_key = match canvas_info.source { - CanvasSource::WebGL(image_key) => image_key, - CanvasSource::WebGPU(image_key) => image_key, - CanvasSource::Image(image_key) => image_key, - CanvasSource::Empty => return vec![], + let Some(image_key) = canvas_info.source else { + return vec![]; }; + vec![Fragment::Image(ArcRefCell::new(ImageFragment { base: self.base_fragment_info.into(), style: style.clone(), diff --git a/components/layout/style_ext.rs b/components/layout/style_ext.rs index 33a22cdf438..68a4481a2be 100644 --- a/components/layout/style_ext.rs +++ b/components/layout/style_ext.rs @@ -712,15 +712,19 @@ impl ComputedValuesExt for ComputedValues { // From <https://www.w3.org/TR/css-transforms-1/#transform-rendering> // > For elements whose layout is governed by the CSS box model, any value other than // > `none` for the `transform` property results in the creation of a stacking context. + // + // From <https://www.w3.org/TR/css-transforms-2/#individual-transforms> + // > all other values […] create a stacking context and containing block for all + // > descendants, per usual for transforms. + // + // From <https://www.w3.org/TR/css-transforms-2/#perspective-property> + // > any value other than none establishes a stacking context. + // // From <https://www.w3.org/TR/css-transforms-2/#transform-style-property> // > A computed value of `preserve-3d` for `transform-style` on a transformable element // > establishes both a stacking context and a containing block for all descendants. - // From <https://www.w3.org/TR/css-transforms-2/#perspective-property> - // > any value other than none establishes a stacking context. - // TODO: handle individual transform properties (`translate`, `scale` and `rotate`). - // <https://www.w3.org/TR/css-transforms-2/#individual-transforms> if self.is_transformable(fragment_flags) && - (!self.get_box().transform.0.is_empty() || + (self.has_transform_or_perspective_style() || self.get_box().transform_style == ComputedTransformStyle::Preserve3d || will_change_bits .intersects(WillChangeBits::TRANSFORM | WillChangeBits::PERSPECTIVE)) @@ -845,9 +849,9 @@ impl ComputedValuesExt for ComputedValues { // > A value other than none for the filter property results in the creation of a containing // > block for absolute and fixed positioned descendants unless the element it applies to is // > a document root element in the current browsing context. - // FIXME(#35391): Need to check if this is the root element. - if !self.get_effects().filter.0.is_empty() || - will_change_bits.intersects(WillChangeBits::FIXPOS_CB_NON_SVG) + if !fragment_flags.contains(FragmentFlags::IS_ROOT_ELEMENT) && + (!self.get_effects().filter.0.is_empty() || + will_change_bits.intersects(WillChangeBits::FIXPOS_CB_NON_SVG)) { return true; } diff --git a/components/layout/table/construct.rs b/components/layout/table/construct.rs index f20360d3b56..56e11320be4 100644 --- a/components/layout/table/construct.rs +++ b/components/layout/table/construct.rs @@ -1019,15 +1019,16 @@ where DisplayLayoutInternal::TableCell => { // This value will already have filtered out rowspan=0 // in quirks mode, so we don't have to worry about that. - // - // The HTML specification limits the parsed value of `rowspan` to - // 65534 and `colspan` to 1000, so we also enforce the same limits - // when dealing with arbitrary DOM elements (perhaps created via - // script). let (rowspan, colspan) = if info.pseudo_element_type.is_none() { let node = info.node.to_threadsafe(); - let rowspan = node.get_rowspan().unwrap_or(1).min(65534) as usize; - let colspan = node.get_colspan().unwrap_or(1).min(1000) as usize; + let rowspan = node.get_rowspan().unwrap_or(1) as usize; + let colspan = node.get_colspan().unwrap_or(1) as usize; + + // The HTML specification clamps value of `rowspan` to [0, 65534] and + // `colspan` to [1, 1000]. + assert!((1..=1000).contains(&colspan)); + assert!((0..=65534).contains(&rowspan)); + (rowspan, colspan) } else { (1, 1) @@ -1140,21 +1141,19 @@ fn add_column<'dom, Node: NodeExt<'dom>>( is_anonymous: bool, ) -> ArcRefCell<TableTrack> { let span = if column_info.pseudo_element_type.is_none() { - column_info - .node - .to_threadsafe() - .get_span() - .unwrap_or(1) - .min(1000) as usize + column_info.node.to_threadsafe().get_span().unwrap_or(1) } else { 1 }; + // The HTML specification clamps value of `span` for `<col>` to [1, 1000]. + assert!((1..=1000).contains(&span)); + let column = ArcRefCell::new(TableTrack { base: LayoutBoxBase::new(column_info.into(), column_info.style.clone()), group_index, is_anonymous, }); - collection.extend(repeat(column.clone()).take(span)); + collection.extend(repeat(column.clone()).take(span as usize)); column } diff --git a/components/layout/table/layout.rs b/components/layout/table/layout.rs index 2261f7d165c..2efe339837e 100644 --- a/components/layout/table/layout.rs +++ b/components/layout/table/layout.rs @@ -1068,7 +1068,6 @@ impl<'a> TableLayout<'a> { &mut self, layout_context: &LayoutContext, containing_block_for_table: &ContainingBlock, - parent_positioning_context: &mut PositioningContext, ) { self.cells_laid_out = self .table @@ -1076,30 +1075,6 @@ impl<'a> TableLayout<'a> { .par_iter() .enumerate() .map(|(row_index, row_slots)| { - // When building the PositioningContext for this cell, we want it to have the same - // configuration for whatever PositioningContext the contents are ultimately added to. - let collect_for_nearest_positioned_ancestor = parent_positioning_context - .collects_for_nearest_positioned_ancestor() || - self.table.rows.get(row_index).is_some_and(|row| { - let row = row.borrow(); - let row_group_collects_for_nearest_positioned_ancestor = - row.group_index.is_some_and(|group_index| { - self.table.row_groups[group_index] - .borrow() - .base - .style - .establishes_containing_block_for_absolute_descendants( - FragmentFlags::empty(), - ) - }); - row_group_collects_for_nearest_positioned_ancestor || - row.base - .style - .establishes_containing_block_for_absolute_descendants( - FragmentFlags::empty(), - ) - }); - row_slots .par_iter() .enumerate() @@ -1141,10 +1116,7 @@ impl<'a> TableLayout<'a> { style: &cell.base.style, }; - let mut positioning_context = PositioningContext::new_for_subtree( - collect_for_nearest_positioned_ancestor, - ); - + let mut positioning_context = PositioningContext::default(); let layout = cell.contents.layout( layout_context, &mut positioning_context, @@ -1503,7 +1475,6 @@ impl<'a> TableLayout<'a> { layout_context: &LayoutContext, parent_positioning_context: &mut PositioningContext, ) -> BoxFragment { - let mut positioning_context = PositioningContext::new_for_style(caption.context.style()); let containing_block = &ContainingBlock { size: ContainingBlockSize { inline: self.table_width + self.pbm.padding_border_sums.inline, @@ -1517,6 +1488,8 @@ impl<'a> TableLayout<'a> { // stretch block size. https://drafts.csswg.org/css-sizing-4/#stretch-fit-sizing let ignore_block_margins_for_stretch = LogicalSides1D::new(false, false); + let mut positioning_context = + PositioningContext::new_for_layout_box_base(&caption.context.base); let mut box_fragment = caption.context.layout_in_flow_block_level( layout_context, positioning_context @@ -1769,11 +1742,7 @@ impl<'a> TableLayout<'a> { ) -> BoxFragment { self.distributed_column_widths = Self::distribute_width_to_columns(self.assignable_width, &self.columns); - self.layout_cells_in_row( - layout_context, - containing_block_for_children, - positioning_context, - ); + self.layout_cells_in_row(layout_context, containing_block_for_children); let table_writing_mode = containing_block_for_children.style.writing_mode; let first_layout_row_heights = self.do_first_row_layout(table_writing_mode); self.compute_table_height_and_final_row_heights( @@ -2325,7 +2294,7 @@ impl<'a> RowFragmentLayout<'a> { Self { row: table_row, rect, - positioning_context: PositioningContext::new_for_style(&table_row.base.style), + positioning_context: PositioningContext::new_for_layout_box_base(&table_row.base), containing_block, fragments: Vec::new(), } @@ -2379,11 +2348,11 @@ impl<'a> RowFragmentLayout<'a> { if let Some(mut row_positioning_context) = self.positioning_context.take() { row_positioning_context.layout_collected_children(layout_context, &mut row_fragment); - let positioning_context = row_group_fragment_layout + let parent_positioning_context = row_group_fragment_layout .as_mut() .and_then(|layout| layout.positioning_context.as_mut()) .unwrap_or(table_positioning_context); - positioning_context.append(row_positioning_context); + parent_positioning_context.append(row_positioning_context); } let fragment = Fragment::Box(ArcRefCell::new(row_fragment)); @@ -2410,7 +2379,7 @@ impl RowGroupFragmentLayout { let row_group = row_group.borrow(); ( dimensions.get_row_group_rect(&row_group), - PositioningContext::new_for_style(&row_group.base.style), + PositioningContext::new_for_layout_box_base(&row_group.base), ) }; Self { diff --git a/components/layout/taffy/layout.rs b/components/layout/taffy/layout.rs index a7581136bf2..a5838c1bd65 100644 --- a/components/layout/taffy/layout.rs +++ b/components/layout/taffy/layout.rs @@ -29,7 +29,7 @@ use crate::geom::{ use crate::layout_box_base::CacheableLayoutResult; use crate::positioned::{AbsolutelyPositionedBox, PositioningContext, PositioningContextLength}; use crate::sizing::{ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult}; -use crate::style_ext::{ComputedValuesExt, LayoutStyle}; +use crate::style_ext::LayoutStyle; use crate::{ConstraintSpace, ContainingBlock, ContainingBlockSize}; const DUMMY_NODE_ID: taffy::NodeId = taffy::NodeId::new(u64::MAX); @@ -250,28 +250,15 @@ impl taffy::LayoutPartialTree for TaffyContainerContext<'_> { }, style, }; - let layout = { - let mut child_positioning_context = - PositioningContext::new_for_style(style).unwrap_or_else(|| { - PositioningContext::new_for_subtree( - self.positioning_context - .collects_for_nearest_positioned_ancestor(), - ) - }); - - let layout = non_replaced.layout_without_caching( - self.layout_context, - &mut child_positioning_context, - &content_box_size_override, - containing_block, - false, /* depends_on_block_constraints */ - ); - // Store layout data on child for later access - child.positioning_context = child_positioning_context; - - layout - }; + child.positioning_context = PositioningContext::default(); + let layout = non_replaced.layout_without_caching( + self.layout_context, + &mut child.positioning_context, + &content_box_size_override, + containing_block, + false, /* depends_on_block_constraints */ + ); child.child_fragments = layout.fragments; self.child_specific_layout_infos[usize::from(node_id)] = @@ -372,8 +359,7 @@ impl ComputeInlineContentSizes for TaffyContainer { let mut grid_context = TaffyContainerContext { layout_context, - positioning_context: - &mut PositioningContext::new_for_containing_block_for_all_descendants(), + positioning_context: &mut PositioningContext::default(), content_box_size_override: containing_block, style, source_child_nodes: &self.children, @@ -539,17 +525,6 @@ impl TaffyContainer { let child_specific_layout_info: Option<SpecificLayoutInfo> = std::mem::take(&mut container_ctx.child_specific_layout_infos[child_id]); - let establishes_containing_block_for_absolute_descendants = - if let TaffyItemBoxInner::InFlowBox(independent_box) = &child.taffy_level_box { - child - .style - .establishes_containing_block_for_absolute_descendants( - independent_box.base_fragment_info().flags, - ) - } else { - false - }; - let fragment = match &mut child.taffy_level_box { TaffyItemBoxInner::InFlowBox(independent_box) => { let mut fragment_info = independent_box.base_fragment_info(); @@ -572,29 +547,21 @@ impl TaffyContainer { }) .with_specific_layout_info(child_specific_layout_info); - if establishes_containing_block_for_absolute_descendants { - child.positioning_context.layout_collected_children( - container_ctx.layout_context, - &mut box_fragment, - ); - } - - let fragment = Fragment::Box(ArcRefCell::new(box_fragment)); - + child.positioning_context.layout_collected_children( + container_ctx.layout_context, + &mut box_fragment, + ); child .positioning_context - .adjust_static_position_of_hoisted_fragments( - &fragment, + .adjust_static_position_of_hoisted_fragments_with_offset( + &box_fragment.content_rect.origin.to_vector(), PositioningContextLength::zero(), ); - let child_positioning_context = std::mem::replace( - &mut child.positioning_context, - PositioningContext::new_for_containing_block_for_all_descendants(), - ); container_ctx .positioning_context - .append(child_positioning_context); - fragment + .append(std::mem::take(&mut child.positioning_context)); + + Fragment::Box(ArcRefCell::new(box_fragment)) }, TaffyItemBoxInner::OutOfFlowAbsolutelyPositionedBox(abs_pos_box) => { fn resolve_alignment(value: AlignFlags, auto: AlignFlags) -> AlignFlags { diff --git a/components/layout/taffy/mod.rs b/components/layout/taffy/mod.rs index 55a678cd89a..b1ff753ea78 100644 --- a/components/layout/taffy/mod.rs +++ b/components/layout/taffy/mod.rs @@ -110,7 +110,7 @@ impl TaffyItemBox { Self { taffy_layout: Default::default(), child_fragments: Vec::new(), - positioning_context: PositioningContext::new_for_containing_block_for_all_descendants(), + positioning_context: PositioningContext::default(), style, taffy_level_box: inner, } @@ -118,8 +118,7 @@ impl TaffyItemBox { pub(crate) fn invalidate_cached_fragment(&mut self) { self.taffy_layout = Default::default(); - self.positioning_context = - PositioningContext::new_for_containing_block_for_all_descendants(); + self.positioning_context = PositioningContext::default(); match self.taffy_level_box { TaffyItemBoxInner::InFlowBox(ref independent_formatting_context) => { independent_formatting_context |