diff --git a/components/layout_2020/display_list/stacking_context.rs b/components/layout_2020/display_list/stacking_context.rs index 5d69a201135..bbcf4f838d0 100644 --- a/components/layout_2020/display_list/stacking_context.rs +++ b/components/layout_2020/display_list/stacking_context.rs @@ -5,9 +5,7 @@ use crate::cell::ArcRefCell; use crate::display_list::conversions::ToWebRender; use crate::display_list::DisplayListBuilder; -use crate::fragments::{ - AbsoluteOrFixedPositionedFragment, AnonymousFragment, BoxFragment, Fragment, -}; +use crate::fragments::{AnonymousFragment, BoxFragment, Fragment}; use crate::geom::PhysicalRect; use crate::style_ext::ComputedValuesExt; use euclid::default::Rect; @@ -44,56 +42,107 @@ impl ContainingBlock { rect: *rect, } } + + pub(crate) fn new_replacing_rect(&self, rect: &PhysicalRect) -> Self { + ContainingBlock { + space_and_clip: self.space_and_clip, + rect: *rect, + } + } } #[derive(Clone)] -pub(crate) struct ContainingBlockInfo { - /// The positioning rectangle established by the parent. This is sometimes - /// called the "containing block" in layout_2020. - pub rect: PhysicalRect, +pub(crate) struct ContainingBlockManager<'a, T> { + // The containing block for all non-absolute descendants. "...if the element's + // position is 'relative' or 'static', the containing block is formed by the + // content edge of the nearest block container ancestor box." This is also + // the case for 'position: sticky' elements. + // https://www.w3.org/TR/CSS2/visudet.html#containing-block-details + pub for_non_absolute_descendants: &'a T, - /// The nearest real containing block at this point in the construction of - /// the stacking context tree. - pub nearest_containing_block: Option, + // The containing block for absolute descendants. "If the element has + // 'position: absolute', the containing block is + // established by the nearest ancestor with a 'position' of 'absolute', + // 'relative' or 'fixed', in the following way: + // 1. In the case that the ancestor is an inline element, the containing + // block is the bounding box around the padding boxes of the first and the + // last inline boxes generated for that element. In CSS 2.1, if the inline + // element is split across multiple lines, the containing block is + // undefined. + // 2. Otherwise, the containing block is formed by the padding edge of the + // ancestor." + // https://www.w3.org/TR/CSS2/visudet.html#containing-block-details + // If the ancestor forms a containing block for all descendants (see below), + // this value will be None and absolute descendants will use the containing + // block for fixed descendants. + pub for_absolute_descendants: Option<&'a T>, - /// The nearest containing block for all descendants at this point in the - /// stacking context tree. This containing blocks contains fixed position - /// elements. - pub containing_block_for_all_descendants: ContainingBlock, + // The containing block for fixed and absolute descendants. + // "For elements whose layout is governed by the CSS box model, any value + // other than none for the transform property also causes the element to + // establish a containing block for all descendants. Its padding box will be + // used to layout for all of its absolute-position descendants, + // fixed-position descendants, and descendant fixed background attachments." + // https://w3c.github.io/csswg-drafts/css-transforms-1/#containing-block-for-all-descendants + // See `ComputedValues::establishes_containing_block_for_all_descendants` + // for a list of conditions where an element forms a containing block for + // all descendants. + pub for_absolute_and_fixed_descendants: &'a T, } -pub(crate) struct StackingContextBuilder<'a> { - /// The current SpatialId and ClipId information for this `DisplayListBuilder`. - pub current_space_and_clip: wr::SpaceAndClipInfo, - - /// The id of the nearest ancestor reference frame for this `DisplayListBuilder`. - nearest_reference_frame: wr::SpatialId, - - wr: &'a mut wr::DisplayListBuilder, -} - -impl<'a> StackingContextBuilder<'a> { - pub fn new(wr: &'a mut wr::DisplayListBuilder) -> Self { - Self { - current_space_and_clip: wr::SpaceAndClipInfo::root_scroll(wr.pipeline_id), - nearest_reference_frame: wr::SpatialId::root_reference_frame(wr.pipeline_id), - wr, +impl<'a, T> ContainingBlockManager<'a, T> { + fn get_containing_block_for_fragment(&self, fragment: &Fragment) -> &T { + if let Fragment::Box(box_fragment) = fragment { + match box_fragment.style.clone_position() { + ComputedPosition::Fixed => self.for_absolute_and_fixed_descendants, + ComputedPosition::Absolute => self + .for_absolute_descendants + .unwrap_or(self.for_absolute_and_fixed_descendants), + _ => self.for_non_absolute_descendants, + } + } else { + self.for_non_absolute_descendants } } - fn clipping_and_scrolling_scope(&mut self, f: impl FnOnce(&mut Self) -> R) -> R { - let previous_space_and_clip = self.current_space_and_clip; - let previous_nearest_reference_frame = self.nearest_reference_frame; + pub(crate) fn new_for_non_absolute_descendants( + &self, + for_non_absolute_descendants: &'a T, + ) -> Self { + return ContainingBlockManager { + for_non_absolute_descendants, + for_absolute_descendants: self.for_absolute_descendants, + for_absolute_and_fixed_descendants: self.for_absolute_and_fixed_descendants, + }; + } - let result = f(self); + pub(crate) fn new_for_absolute_descendants( + &self, + for_non_absolute_descendants: &'a T, + for_absolute_descendants: &'a T, + ) -> Self { + return ContainingBlockManager { + for_non_absolute_descendants, + for_absolute_descendants: Some(for_absolute_descendants), + for_absolute_and_fixed_descendants: self.for_absolute_and_fixed_descendants, + }; + } - self.current_space_and_clip = previous_space_and_clip; - self.nearest_reference_frame = previous_nearest_reference_frame; - - result + pub(crate) fn new_for_absolute_and_fixed_descendants( + &self, + for_non_absolute_descendants: &'a T, + for_absolute_and_fixed_descendants: &'a T, + ) -> Self { + return ContainingBlockManager { + for_non_absolute_descendants, + for_absolute_descendants: None, + for_absolute_and_fixed_descendants, + }; } } +pub(crate) type ContainingBlockInfo<'a> = ContainingBlockManager<'a, ContainingBlock>; + #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] pub(crate) enum StackingContextSection { BackgroundsAndBorders, @@ -448,11 +497,12 @@ impl Fragment { pub(crate) fn build_stacking_context_tree( &self, fragment_ref: &ArcRefCell, - builder: &mut StackingContextBuilder, + wr: &mut wr::DisplayListBuilder, containing_block_info: &ContainingBlockInfo, stacking_context: &mut StackingContext, mode: StackingContextBuildMode, ) { + let containing_block = containing_block_info.get_containing_block_for_fragment(self); match self { Fragment::Box(fragment) => { if mode == StackingContextBuildMode::SkipHoisted && @@ -461,31 +511,41 @@ impl Fragment { return; } - // If this fragment has a transform applied that makes it take up no spae + // If this fragment has a transform applied that makes it take up no space // then we don't need to create any stacking contexts for it. let has_non_invertible_transform = - fragment.has_non_invertible_transform(&containing_block_info.rect.to_untyped()); + fragment.has_non_invertible_transform(&containing_block.rect.to_untyped()); if has_non_invertible_transform { return; } fragment.build_stacking_context_tree( fragment_ref, - builder, + wr, + containing_block, containing_block_info, stacking_context, ); }, Fragment::AbsoluteOrFixedPositioned(fragment) => { - fragment.build_stacking_context_tree( - builder, + let shared_fragment = fragment.borrow(); + let fragment_ref = match shared_fragment.fragment.as_ref() { + Some(fragment_ref) => fragment_ref, + None => unreachable!("Found hoisted box with missing fragment."), + }; + + fragment_ref.borrow().build_stacking_context_tree( + fragment_ref, + wr, containing_block_info, stacking_context, + StackingContextBuildMode::IncludeHoisted, ); }, Fragment::Anonymous(fragment) => { fragment.build_stacking_context_tree( - builder, + wr, + containing_block, containing_block_info, stacking_context, ); @@ -493,8 +553,8 @@ impl Fragment { Fragment::Text(_) | Fragment::Image(_) | Fragment::IFrame(_) => { stacking_context.fragments.push(StackingContextFragment { section: StackingContextSection::Content, - space_and_clip: builder.current_space_and_clip, - containing_block: containing_block_info.rect, + space_and_clip: containing_block.space_and_clip, + containing_block: containing_block.rect, fragment: fragment_ref.clone(), }); }, @@ -542,105 +602,120 @@ impl BoxFragment { StackingContextSection::BlockBackgroundsAndBorders } - fn build_containing_block<'a>( - &'a self, - builder: &mut StackingContextBuilder, - padding_rect: &PhysicalRect, - containing_block_info: &mut ContainingBlockInfo, - ) { - if !self - .style - .establishes_containing_block_for_absolute_descendants() - { - return; - } - - let new_containing_block = - ContainingBlock::new(padding_rect, builder.current_space_and_clip); - - if self - .style - .establishes_containing_block_for_all_descendants() - { - containing_block_info.nearest_containing_block = None; - containing_block_info.containing_block_for_all_descendants = new_containing_block; - } else { - containing_block_info.nearest_containing_block = Some(new_containing_block); - } - } - fn build_stacking_context_tree( &self, fragment: &ArcRefCell, - builder: &mut StackingContextBuilder, - containing_block_info: &ContainingBlockInfo, - stacking_context: &mut StackingContext, - ) { - builder.clipping_and_scrolling_scope(|builder| { - self.adjust_spatial_id_for_positioning(builder); - - match self.get_stacking_context_type() { - Some(context_type) => { - self.build_stacking_context_tree_creating_stacking_context( - fragment, - builder, - containing_block_info, - stacking_context, - context_type, - ); - }, - None => { - self.build_stacking_context_tree_for_children( - fragment, - builder, - containing_block_info, - stacking_context, - ); - }, - } - }); - } - - fn build_stacking_context_tree_creating_stacking_context( - &self, - fragment: &ArcRefCell, - builder: &mut StackingContextBuilder, + wr: &mut wr::DisplayListBuilder, + containing_block: &ContainingBlock, + containing_block_info: &ContainingBlockInfo, + parent_stacking_context: &mut StackingContext, + ) { + self.build_stacking_context_tree_maybe_creating_reference_frame( + fragment, + wr, + containing_block, + containing_block_info, + parent_stacking_context, + ); + } + + fn build_stacking_context_tree_maybe_creating_reference_frame( + &self, + fragment: &ArcRefCell, + wr: &mut wr::DisplayListBuilder, + containing_block: &ContainingBlock, containing_block_info: &ContainingBlockInfo, parent_stacking_context: &mut StackingContext, - context_type: StackingContextType, ) { - // If we are creating a stacking context, we may also need to create a reference - // frame first. let reference_frame_data = - self.reference_frame_data_if_necessary(&containing_block_info.rect); + match self.reference_frame_data_if_necessary(&containing_block.rect) { + Some(reference_frame_data) => reference_frame_data, + None => { + return self.build_stacking_context_tree_maybe_creating_stacking_context( + fragment, + wr, + containing_block, + containing_block_info, + parent_stacking_context, + ); + }, + }; - // WebRender reference frames establish a new coordinate system at their origin - // (the border box of the fragment). We need to ensure that any coordinates we - // give to WebRender in this reference frame are relative to the fragment border - // box. We do this by adjusting the containing block origin. - let mut new_containing_block_info = containing_block_info.clone(); + let new_spatial_id = wr.push_reference_frame( + reference_frame_data.origin.to_webrender(), + containing_block.space_and_clip.spatial_id, + self.style.get_box().transform_style.to_webrender(), + wr::PropertyBinding::Value(reference_frame_data.transform), + reference_frame_data.kind, + ); - if let Some(reference_frame_data) = &reference_frame_data { - new_containing_block_info.rect.origin -= reference_frame_data.origin.to_vector(); - builder.current_space_and_clip.spatial_id = builder.wr.push_reference_frame( - reference_frame_data.origin.to_webrender(), - builder.current_space_and_clip.spatial_id, - self.style.get_box().transform_style.to_webrender(), - wr::PropertyBinding::Value(reference_frame_data.transform), - reference_frame_data.kind, - ); - builder.nearest_reference_frame = builder.current_space_and_clip.spatial_id; - } + // WebRender reference frames establish a new coordinate system at their + // origin (the border box of the fragment). We need to ensure that any + // coordinates we give to WebRender in this reference frame are relative + // to the fragment border box. We do this by adjusting the containing + // block origin. Note that the `for_absolute_descendants` and + // `for_all_absolute_and_fixed_descendants` properties are now bogus, + // but all fragments that establish reference frames also establish + // containing blocks for absolute and fixed descendants, so those + // properties will be replaced before recursing into children. + assert!(self + .style + .establishes_containing_block_for_all_descendants()); + let adjusted_containing_block = ContainingBlock::new( + &containing_block + .rect + .translate(-reference_frame_data.origin.to_vector()), + wr::SpaceAndClipInfo { + spatial_id: new_spatial_id, + clip_id: containing_block.space_and_clip.clip_id, + }, + ); + let new_containing_block_info = + containing_block_info.new_for_non_absolute_descendants(&adjusted_containing_block); + + self.build_stacking_context_tree_maybe_creating_stacking_context( + fragment, + wr, + &adjusted_containing_block, + &new_containing_block_info, + parent_stacking_context, + ); + + wr.pop_reference_frame(); + } + + fn build_stacking_context_tree_maybe_creating_stacking_context( + &self, + fragment: &ArcRefCell, + wr: &mut wr::DisplayListBuilder, + containing_block: &ContainingBlock, + containing_block_info: &ContainingBlockInfo, + parent_stacking_context: &mut StackingContext, + ) { + let context_type = match self.get_stacking_context_type() { + Some(context_type) => context_type, + None => { + self.build_stacking_context_tree_for_children( + fragment, + wr, + containing_block, + containing_block_info, + parent_stacking_context, + ); + return; + }, + }; let mut child_stacking_context = StackingContext::new( - builder.current_space_and_clip.spatial_id, + containing_block.space_and_clip.spatial_id, self.style.clone(), context_type, ); self.build_stacking_context_tree_for_children( fragment, - builder, - &new_containing_block_info, + wr, + containing_block, + containing_block_info, &mut child_stacking_context, ); @@ -659,58 +734,91 @@ impl BoxFragment { parent_stacking_context .stacking_contexts .append(&mut stolen_children); - - if reference_frame_data.is_some() { - builder.wr.pop_reference_frame(); - } } fn build_stacking_context_tree_for_children<'a>( &'a self, fragment: &ArcRefCell, - builder: &mut StackingContextBuilder, + wr: &mut wr::DisplayListBuilder, + containing_block: &ContainingBlock, containing_block_info: &ContainingBlockInfo, stacking_context: &mut StackingContext, ) { - self.build_clip_frame_if_necessary(builder, containing_block_info); + let mut new_space_and_clip = containing_block.space_and_clip; + if let Some(new_clip_id) = + self.build_clip_frame_if_necessary(wr, new_space_and_clip, &containing_block.rect) + { + new_space_and_clip.clip_id = new_clip_id; + } + stacking_context.fragments.push(StackingContextFragment { - space_and_clip: builder.current_space_and_clip, + space_and_clip: new_space_and_clip, section: self.get_stacking_context_section(), - containing_block: containing_block_info.rect, + containing_block: containing_block.rect, fragment: fragment.clone(), }); if self.style.get_outline().outline_width.px() > 0.0 { stacking_context.fragments.push(StackingContextFragment { - space_and_clip: builder.current_space_and_clip, + space_and_clip: new_space_and_clip, section: StackingContextSection::Outline, - containing_block: containing_block_info.rect, + containing_block: containing_block.rect, fragment: fragment.clone(), }); } // We want to build the scroll frame after the background and border, because // they shouldn't scroll with the rest of the box content. - self.build_scroll_frame_if_necessary(builder, containing_block_info); + if let Some(scroll_space_and_clip) = + self.build_scroll_frame_if_necessary(wr, new_space_and_clip, &containing_block.rect) + { + new_space_and_clip = scroll_space_and_clip; + } let padding_rect = self .padding_rect() - .to_physical(self.style.writing_mode, &containing_block_info.rect) - .translate(containing_block_info.rect.origin.to_vector()); - let mut new_containing_block_info = containing_block_info.clone(); - new_containing_block_info.rect = self + .to_physical(self.style.writing_mode, &containing_block.rect) + .translate(containing_block.rect.origin.to_vector()); + let content_rect = self .content_rect - .to_physical(self.style.writing_mode, &new_containing_block_info.rect) - .translate(new_containing_block_info.rect.origin.to_vector()); + .to_physical(self.style.writing_mode, &containing_block.rect) + .translate(containing_block.rect.origin.to_vector()); - // If we establish a containing block we use the padding rect as the offset. This is - // because for all but the initial containing block, the padding rect determines - // the size and position of the containing block. - self.build_containing_block(builder, &padding_rect, &mut new_containing_block_info); + let for_absolute_descendants = ContainingBlock { + rect: padding_rect, + space_and_clip: new_space_and_clip, + }; + let for_non_absolute_descendants = ContainingBlock { + rect: content_rect, + space_and_clip: new_space_and_clip, + }; + + // Create a new `ContainingBlockInfo` for descendants depending on + // whether or not this fragment establishes a containing block for + // absolute and fixed descendants. + let new_containing_block_info = if self + .style + .establishes_containing_block_for_all_descendants() + { + containing_block_info.new_for_absolute_and_fixed_descendants( + &for_non_absolute_descendants, + &for_absolute_descendants, + ) + } else if self + .style + .establishes_containing_block_for_absolute_descendants() + { + containing_block_info.new_for_absolute_descendants( + &for_non_absolute_descendants, + &for_absolute_descendants, + ) + } else { + containing_block_info.new_for_non_absolute_descendants(&for_non_absolute_descendants) + }; for child in &self.children { child.borrow().build_stacking_context_tree( child, - builder, + wr, &new_containing_block_info, stacking_context, StackingContextBuildMode::SkipHoisted, @@ -718,81 +826,74 @@ impl BoxFragment { } } - fn adjust_spatial_id_for_positioning(&self, builder: &mut StackingContextBuilder) { - if self.style.get_box().position != ComputedPosition::Fixed { - return; - } - - // TODO(mrobinson): Eventually this should use the spatial id of the reference - // frame that is the parent of this one once we have full support for stacking - // contexts and transforms. - builder.current_space_and_clip.spatial_id = builder.nearest_reference_frame; - } - fn build_clip_frame_if_necessary( &self, - builder: &mut StackingContextBuilder, - containing_block_info: &ContainingBlockInfo, - ) { + wr: &mut wr::DisplayListBuilder, + current_space_and_clip: wr::SpaceAndClipInfo, + containing_block_rect: &PhysicalRect, + ) -> Option { let position = self.style.get_box().position; // https://drafts.csswg.org/css2/#clipping // The clip property applies only to absolutely positioned elements - if position == ComputedPosition::Absolute || position == ComputedPosition::Fixed { - let clip = self.style.get_effects().clip; - if let ClipRectOrAuto::Rect(r) = clip { - let border_rect = self - .border_rect() - .to_physical(self.style.writing_mode, &containing_block_info.rect); - let clip_rect = r - .for_border_rect(border_rect) - .translate(containing_block_info.rect.origin.to_vector()) - .to_webrender(); - - let parent = builder.current_space_and_clip; - builder.current_space_and_clip.clip_id = - builder.wr.define_clip_rect(&parent, clip_rect); - } + if position != ComputedPosition::Absolute && position != ComputedPosition::Fixed { + return None; } + + // Only rectangles are supported for now. + let clip_rect = match self.style.get_effects().clip { + ClipRectOrAuto::Rect(rect) => rect, + _ => return None, + }; + + let border_rect = self + .border_rect() + .to_physical(self.style.writing_mode, &containing_block_rect); + let clip_rect = clip_rect + .for_border_rect(border_rect) + .translate(containing_block_rect.origin.to_vector()) + .to_webrender(); + + Some(wr.define_clip_rect(¤t_space_and_clip, clip_rect)) } fn build_scroll_frame_if_necessary<'a>( &self, - builder: &mut StackingContextBuilder, - containing_block_info: &ContainingBlockInfo, - ) { + wr: &mut wr::DisplayListBuilder, + current_space_and_clip: wr::SpaceAndClipInfo, + containing_block_rect: &PhysicalRect, + ) -> Option { let overflow_x = self.style.get_box().overflow_x; let overflow_y = self.style.get_box().overflow_y; + if overflow_x == ComputedOverflow::Visible && overflow_y == ComputedOverflow::Visible { + return None; + } - let original_scroll_and_clip_info = builder.current_space_and_clip; - if overflow_x != ComputedOverflow::Visible || overflow_y != ComputedOverflow::Visible { - let external_id = wr::ExternalScrollId( - self.tag.to_display_list_fragment_id(), - builder.wr.pipeline_id, - ); + let external_id = + wr::ExternalScrollId(self.tag.to_display_list_fragment_id(), wr.pipeline_id); - let sensitivity = if ComputedOverflow::Hidden == overflow_x && - ComputedOverflow::Hidden == overflow_y - { + let sensitivity = + if ComputedOverflow::Hidden == overflow_x && ComputedOverflow::Hidden == overflow_y { wr::ScrollSensitivity::Script } else { wr::ScrollSensitivity::ScriptAndInputEvents }; - let padding_rect = self - .padding_rect() - .to_physical(self.style.writing_mode, &containing_block_info.rect) - .translate(containing_block_info.rect.origin.to_vector()) - .to_webrender(); - builder.current_space_and_clip = builder.wr.define_scroll_frame( - &original_scroll_and_clip_info, + let padding_rect = self + .padding_rect() + .to_physical(self.style.writing_mode, &containing_block_rect) + .translate(containing_block_rect.origin.to_vector()) + .to_webrender(); + Some( + wr.define_scroll_frame( + ¤t_space_and_clip, Some(external_id), - self.scrollable_overflow(&containing_block_info.rect) + self.scrollable_overflow(&containing_block_rect) .to_webrender(), padding_rect, sensitivity, LayoutVector2D::zero(), - ); - } + ), + ) } /// Optionally returns the data for building a reference frame, without yet building it. @@ -925,19 +1026,23 @@ impl BoxFragment { impl AnonymousFragment { fn build_stacking_context_tree( &self, - builder: &mut StackingContextBuilder, + wr: &mut wr::DisplayListBuilder, + containing_block: &ContainingBlock, containing_block_info: &ContainingBlockInfo, stacking_context: &mut StackingContext, ) { - let mut new_containing_block_info = containing_block_info.clone(); - new_containing_block_info.rect = self + let rect = self .rect - .to_physical(self.mode, &containing_block_info.rect) - .translate(containing_block_info.rect.origin.to_vector()); + .to_physical(self.mode, &containing_block.rect) + .translate(containing_block.rect.origin.to_vector()); + let new_containing_block = containing_block.new_replacing_rect(&rect); + let new_containing_block_info = + containing_block_info.new_for_non_absolute_descendants(&new_containing_block); + for child in &self.children { child.borrow().build_stacking_context_tree( child, - builder, + wr, &new_containing_block_info, stacking_context, StackingContextBuildMode::SkipHoisted, @@ -945,44 +1050,3 @@ impl AnonymousFragment { } } } - -impl AbsoluteOrFixedPositionedFragment { - fn build_stacking_context_tree( - &self, - builder: &mut StackingContextBuilder, - containing_block_info: &ContainingBlockInfo, - stacking_context: &mut StackingContext, - ) { - let hoisted_fragment = self.hoisted_fragment.borrow(); - let fragment_ref = match hoisted_fragment.fragment.as_ref() { - Some(fragment_ref) => fragment_ref, - None => unreachable!("Found hoisted box with missing fragment."), - }; - - let containing_block = match self.position { - ComputedPosition::Fixed => &containing_block_info.containing_block_for_all_descendants, - ComputedPosition::Absolute => containing_block_info - .nearest_containing_block - .as_ref() - .unwrap_or(&containing_block_info.containing_block_for_all_descendants), - ComputedPosition::Static | ComputedPosition::Relative => unreachable!( - "Found an AbsoluteOrFixedPositionedFragment for a \ - non-absolutely or fixed position fragment." - ), - }; - - builder.clipping_and_scrolling_scope(|builder| { - let mut new_containing_block_info = containing_block_info.clone(); - new_containing_block_info.rect = containing_block.rect; - builder.current_space_and_clip = containing_block.space_and_clip; - - fragment_ref.borrow().build_stacking_context_tree( - fragment_ref, - builder, - &new_containing_block_info, - stacking_context, - StackingContextBuildMode::IncludeHoisted, - ); - }); - } -} diff --git a/components/layout_2020/flexbox/layout.rs b/components/layout_2020/flexbox/layout.rs index e9722c29c8b..b6f450ac046 100644 --- a/components/layout_2020/flexbox/layout.rs +++ b/components/layout_2020/flexbox/layout.rs @@ -8,9 +8,7 @@ use super::geom::{ use super::{FlexContainer, FlexLevelBox}; use crate::context::LayoutContext; use crate::formatting_contexts::{IndependentFormattingContext, IndependentLayout}; -use crate::fragments::{ - AbsoluteOrFixedPositionedFragment, BoxFragment, CollapsedBlockMargins, Fragment, -}; +use crate::fragments::{BoxFragment, CollapsedBlockMargins, Fragment}; use crate::geom::flow_relative::{Rect, Sides, Vec2}; use crate::geom::LengthOrAuto; use crate::positioned::{AbsolutelyPositionedBox, PositioningContext}; @@ -200,11 +198,6 @@ impl FlexContainer { Fragment::Box(flex_item_fragments.next().unwrap()) }, Ok(absolutely_positioned) => { - let position = absolutely_positioned - .borrow() - .context - .style() - .clone_position(); let hoisted_box = AbsolutelyPositionedBox::to_hoisted( absolutely_positioned, Vec2::zero(), @@ -213,10 +206,7 @@ impl FlexContainer { ); let hoisted_fragment = hoisted_box.fragment.clone(); positioning_context.push(hoisted_box); - Fragment::AbsoluteOrFixedPositioned(AbsoluteOrFixedPositionedFragment { - hoisted_fragment, - position, - }) + Fragment::AbsoluteOrFixedPositioned(hoisted_fragment) }, }) .collect::>(); diff --git a/components/layout_2020/flow/inline.rs b/components/layout_2020/flow/inline.rs index 84fb22ae042..498a63e88b0 100644 --- a/components/layout_2020/flow/inline.rs +++ b/components/layout_2020/flow/inline.rs @@ -8,8 +8,8 @@ use crate::flow::float::FloatBox; use crate::flow::FlowLayout; use crate::formatting_contexts::IndependentFormattingContext; use crate::fragments::{ - AbsoluteOrFixedPositionedFragment, AnonymousFragment, BoxFragment, CollapsedBlockMargins, - DebugId, FontMetrics, Fragment, Tag, TextFragment, + AnonymousFragment, BoxFragment, CollapsedBlockMargins, DebugId, FontMetrics, Fragment, Tag, + TextFragment, }; use crate::geom::flow_relative::{Rect, Sides, Vec2}; use crate::positioned::{ @@ -338,14 +338,9 @@ impl InlineFormattingContext { ); let hoisted_fragment = hoisted_box.fragment.clone(); ifc.push_hoisted_box_to_positioning_context(hoisted_box); - ifc.current_nesting_level.fragments_so_far.push( - Fragment::AbsoluteOrFixedPositioned( - AbsoluteOrFixedPositionedFragment { - hoisted_fragment, - position: style.clone_position(), - }, - ), - ); + ifc.current_nesting_level + .fragments_so_far + .push(Fragment::AbsoluteOrFixedPositioned(hoisted_fragment)); }, InlineLevelBox::OutOfFlowFloatBox(_box_) => { // TODO diff --git a/components/layout_2020/flow/mod.rs b/components/layout_2020/flow/mod.rs index f46f2724f3d..c2ce669c51d 100644 --- a/components/layout_2020/flow/mod.rs +++ b/components/layout_2020/flow/mod.rs @@ -12,8 +12,7 @@ use crate::formatting_contexts::{ IndependentFormattingContext, IndependentLayout, NonReplacedFormattingContext, }; use crate::fragments::{ - AbsoluteOrFixedPositionedFragment, AnonymousFragment, BoxFragment, CollapsedBlockMargins, - CollapsedMargin, Fragment, Tag, + AnonymousFragment, BoxFragment, CollapsedBlockMargins, CollapsedMargin, Fragment, Tag, }; use crate::geom::flow_relative::{Rect, Sides, Vec2}; use crate::positioned::{AbsolutelyPositionedBox, PositioningContext}; @@ -215,10 +214,7 @@ fn layout_block_level_children( placement_state.current_block_direction_position, inline: Length::new(0.), }; - fragment - .hoisted_fragment - .borrow_mut() - .adjust_offsets(offset); + fragment.borrow_mut().adjust_offsets(offset); }, Fragment::Anonymous(_) => {}, _ => unreachable!(), @@ -380,10 +376,7 @@ impl BlockLevelBox { ); let hoisted_fragment = hoisted_box.fragment.clone(); positioning_context.push(hoisted_box); - Fragment::AbsoluteOrFixedPositioned(AbsoluteOrFixedPositionedFragment { - hoisted_fragment, - position: box_.borrow().context.style().clone_position(), - }) + Fragment::AbsoluteOrFixedPositioned(hoisted_fragment) }, BlockLevelBox::OutOfFlowFloatBox(_box_) => { // FIXME: call layout_maybe_position_relative_fragment here diff --git a/components/layout_2020/flow/root.rs b/components/layout_2020/flow/root.rs index fb05049f4a8..e76f1b8da78 100644 --- a/components/layout_2020/flow/root.rs +++ b/components/layout_2020/flow/root.rs @@ -6,7 +6,6 @@ use crate::cell::ArcRefCell; use crate::context::LayoutContext; use crate::display_list::stacking_context::{ ContainingBlock, ContainingBlockInfo, StackingContext, StackingContextBuildMode, - StackingContextBuilder, }; use crate::dom_traversal::{iter_child_nodes, Contents, NodeAndStyleInfo, NodeExt}; use crate::element_data::LayoutBox; @@ -40,6 +39,7 @@ use style::properties::ComputedValues; use style::selector_parser::PseudoElement; use style::values::computed::Length; use style_traits::CSSPixel; +use webrender_api::{ClipId, SpaceAndClipInfo, SpatialId}; #[derive(Serialize)] pub struct BoxTree { @@ -386,30 +386,7 @@ impl BoxTree { impl FragmentTree { pub fn build_display_list(&self, builder: &mut crate::display_list::DisplayListBuilder) { - let mut stacking_context = StackingContext::create_root(&builder.wr); - { - let mut stacking_context_builder = StackingContextBuilder::new(&mut builder.wr); - let containing_block_info = ContainingBlockInfo { - rect: self.initial_containing_block, - nearest_containing_block: None, - containing_block_for_all_descendants: ContainingBlock::new( - &self.initial_containing_block, - stacking_context_builder.current_space_and_clip, - ), - }; - - for fragment in &self.root_fragments { - fragment.borrow().build_stacking_context_tree( - fragment, - &mut stacking_context_builder, - &containing_block_info, - &mut stacking_context, - StackingContextBuildMode::SkipHoisted, - ); - } - } - - stacking_context.sort(); + let stacking_context = self.build_stacking_context_tree(builder); // Paint the canvas’ background (if any) before/under everything else stacking_context.build_canvas_background_display_list( @@ -421,6 +398,47 @@ impl FragmentTree { stacking_context.build_display_list(builder); } + fn build_stacking_context_tree( + &self, + builder: &mut crate::display_list::DisplayListBuilder, + ) -> StackingContext { + let mut stacking_context = StackingContext::create_root(&builder.wr); + let pipeline_id = builder.wr.pipeline_id; + let cb_for_non_fixed_descendants = ContainingBlock::new( + &self.initial_containing_block, + SpaceAndClipInfo::root_scroll(pipeline_id), + ); + let cb_for_fixed_descendants = ContainingBlock::new( + &self.initial_containing_block, + SpaceAndClipInfo { + spatial_id: SpatialId::root_reference_frame(pipeline_id), + clip_id: ClipId::root(pipeline_id), + }, + ); + + for fragment in &self.root_fragments { + fragment.borrow().build_stacking_context_tree( + fragment, + &mut builder.wr, + // We need to specify all three containing blocks here, because absolute + // descdendants of the root cannot share the containing block we specify + // for fixed descendants. In this case, they need to have the spatial + // id of the root scroll frame, whereas fixed descendants need the + // spatial id of the root reference frame so that they do not scroll with + // page content. + &ContainingBlockInfo { + for_non_absolute_descendants: &cb_for_non_fixed_descendants, + for_absolute_descendants: Some(&cb_for_non_fixed_descendants), + for_absolute_and_fixed_descendants: &cb_for_fixed_descendants, + }, + &mut stacking_context, + StackingContextBuildMode::SkipHoisted, + ); + } + stacking_context.sort(); + stacking_context + } + pub fn print(&self) { let mut print_tree = PrintTree::new("Fragment Tree".to_string()); for fragment in &self.root_fragments { diff --git a/components/layout_2020/fragments.rs b/components/layout_2020/fragments.rs index b54fa90a52b..687bdbe083e 100644 --- a/components/layout_2020/fragments.rs +++ b/components/layout_2020/fragments.rs @@ -19,7 +19,6 @@ use serde::ser::{Serialize, Serializer}; use servo_arc::Arc as ServoArc; use std::sync::Arc; use style::computed_values::overflow_x::T as ComputedOverflow; -use style::computed_values::position::T as ComputedPosition; use style::dom::OpaqueNode; use style::logical_geometry::WritingMode; use style::properties::ComputedValues; @@ -68,18 +67,19 @@ impl Tag { pub(crate) enum Fragment { Box(BoxFragment), Anonymous(AnonymousFragment), - AbsoluteOrFixedPositioned(AbsoluteOrFixedPositionedFragment), + /// Absolute and fixed position fragments are hoisted up so that they + /// are children of the BoxFragment that establishes their containing + /// blocks, so that they can be laid out properly. When this happens + /// an `AbsoluteOrFixedPositioned` fragment is left at the original tree + /// position. This allows these hoisted fragments to be painted with + /// regard to their original tree order during stacking context tree / + /// display list construction. + AbsoluteOrFixedPositioned(ArcRefCell), Text(TextFragment), Image(ImageFragment), IFrame(IFrameFragment), } -#[derive(Serialize)] -pub(crate) struct AbsoluteOrFixedPositionedFragment { - pub position: ComputedPosition, - pub hoisted_fragment: ArcRefCell, -} - #[derive(Serialize)] pub(crate) struct BoxFragment { pub tag: Tag, @@ -214,7 +214,9 @@ impl Fragment { pub fn print(&self, tree: &mut PrintTree) { match self { Fragment::Box(fragment) => fragment.print(tree), - Fragment::AbsoluteOrFixedPositioned(fragment) => fragment.print(tree), + Fragment::AbsoluteOrFixedPositioned(_) => { + tree.add_item("AbsoluteOrFixedPositioned".to_string()); + }, Fragment::Anonymous(fragment) => fragment.print(tree), Fragment::Text(fragment) => fragment.print(tree), Fragment::Image(fragment) => fragment.print(tree), @@ -280,12 +282,6 @@ impl Fragment { } } -impl AbsoluteOrFixedPositionedFragment { - pub fn print(&self, tree: &mut PrintTree) { - tree.add_item(format!("AbsoluteOrFixedPositionedFragment")); - } -} - impl AnonymousFragment { pub fn no_op(mode: WritingMode) -> Self { Self { diff --git a/tests/wpt/metadata/MANIFEST.json b/tests/wpt/metadata/MANIFEST.json index 4c58dc8e874..171360f8363 100644 --- a/tests/wpt/metadata/MANIFEST.json +++ b/tests/wpt/metadata/MANIFEST.json @@ -257891,6 +257891,19 @@ {} ] ], + "transform-containing-block-and-scrolling-area-for-fixed.html": [ + "2fd5f3873a211fc3a0970fabc9e1d23873bf2afe", + [ + null, + [ + [ + "/css/css-transforms/transform-containing-block-and-scrolling-area-for-fixed-ref.html", + "==" + ] + ], + {} + ] + ], "transform-containing-block-dynamic-1a.html": [ "7e6a10dda1c24e86f7635bd202efec48e6089fbd", [ @@ -400583,6 +400596,10 @@ "5122ad98c209f7a46fe9cad2a81497db2001cee2", [] ], + "transform-containing-block-and-scrolling-area-for-fixed-ref.html": [ + "2e88d4493fc23356a1f623983bd85292ca44ade5", + [] + ], "transform-descendant-ref.html": [ "ac60e7f52fd7477bfbe77bba38a568b1706c8ae9", [] diff --git a/tests/wpt/metadata/css/css-transforms/transform-containing-block-and-scrolling-area-for-fixed.html.ini b/tests/wpt/metadata/css/css-transforms/transform-containing-block-and-scrolling-area-for-fixed.html.ini new file mode 100644 index 00000000000..c195fe4c2a7 --- /dev/null +++ b/tests/wpt/metadata/css/css-transforms/transform-containing-block-and-scrolling-area-for-fixed.html.ini @@ -0,0 +1,2 @@ +[transform-containing-block-and-scrolling-area-for-fixed.html] + expected: FAIL diff --git a/tests/wpt/web-platform-tests/css/css-transforms/transform-containing-block-and-scrolling-area-for-fixed-ref.html b/tests/wpt/web-platform-tests/css/css-transforms/transform-containing-block-and-scrolling-area-for-fixed-ref.html new file mode 100644 index 00000000000..2e88d4493fc --- /dev/null +++ b/tests/wpt/web-platform-tests/css/css-transforms/transform-containing-block-and-scrolling-area-for-fixed-ref.html @@ -0,0 +1,26 @@ + + + + + + +
+
+
+ + diff --git a/tests/wpt/web-platform-tests/css/css-transforms/transform-containing-block-and-scrolling-area-for-fixed.html b/tests/wpt/web-platform-tests/css/css-transforms/transform-containing-block-and-scrolling-area-for-fixed.html new file mode 100644 index 00000000000..2fd5f3873a2 --- /dev/null +++ b/tests/wpt/web-platform-tests/css/css-transforms/transform-containing-block-and-scrolling-area-for-fixed.html @@ -0,0 +1,43 @@ + + +CSS transforms: Transformed elements with overflow: hidden create scrolling areas for fixed descendants + + + + + + + + +
+
+
+
+ +