diff --git a/components/layout_2020/display_list/mod.rs b/components/layout_2020/display_list/mod.rs index 11b45edab91..1d2bb10c838 100644 --- a/components/layout_2020/display_list/mod.rs +++ b/components/layout_2020/display_list/mod.rs @@ -37,7 +37,12 @@ type ItemTag = (u64, u16); type HitInfo = Option; pub struct DisplayListBuilder<'a> { + /// The current SpatialId and ClipId information for this `DisplayListBuilder`. current_space_and_clip: wr::SpaceAndClipInfo, + + /// The id of the nearest ancestor reference frame for this `DisplayListBuilder`. + nearest_reference_frame: wr::SpatialId, + pub context: &'a LayoutContext<'a>, pub wr: wr::DisplayListBuilder, @@ -56,6 +61,7 @@ impl<'a> DisplayListBuilder<'a> { ) -> Self { Self { current_space_and_clip: wr::SpaceAndClipInfo::root_scroll(pipeline_id), + nearest_reference_frame: wr::SpatialId::root_reference_frame(pipeline_id), is_contentful: false, context, wr: wr::DisplayListBuilder::new(pipeline_id, viewport_size), @@ -68,9 +74,14 @@ impl<'a> DisplayListBuilder<'a> { } fn clipping_and_scrolling_scope(&mut self, f: impl FnOnce(&mut Self) -> R) -> R { - let previous = self.current_space_and_clip; + let previous_space_and_clip = self.current_space_and_clip; + let previous_nearest_reference_frame = self.nearest_reference_frame; + let result = f(self); - self.current_space_and_clip = previous; + + self.current_space_and_clip = previous_space_and_clip; + self.nearest_reference_frame = previous_nearest_reference_frame; + result } } diff --git a/components/layout_2020/display_list/stacking_context.rs b/components/layout_2020/display_list/stacking_context.rs index 7e4d27e2357..83299a38b52 100644 --- a/components/layout_2020/display_list/stacking_context.rs +++ b/components/layout_2020/display_list/stacking_context.rs @@ -6,6 +6,7 @@ use crate::display_list::conversions::ToWebRender; use crate::display_list::DisplayListBuilder; use crate::fragments::{AnonymousFragment, BoxFragment, Fragment}; use crate::geom::PhysicalRect; +use crate::style_ext::ComputedValuesExt; use euclid::default::Rect; use gfx_traits::{combine_id_with_fragment_type, FragmentType}; use std::cmp::Ordering; @@ -14,7 +15,6 @@ use style::computed_values::float::T as ComputedFloat; use style::computed_values::mix_blend_mode::T as ComputedMixBlendMode; use style::computed_values::overflow_x::T as ComputedOverflow; use style::computed_values::position::T as ComputedPosition; -use style::computed_values::transform_style::T as ComputedTransformStyle; use style::values::computed::Length; use style::values::generics::box_::Perspective; use style::values::generics::transform; @@ -96,7 +96,7 @@ impl<'a> StackingContext<'a> { fn z_index(&self) -> i32 { match self.initializing_fragment { - Some(fragment) => fragment.effective_z_index(), + Some(fragment) => fragment.style.effective_z_index(), None => 0, } } @@ -259,7 +259,7 @@ impl Fragment { impl BoxFragment { fn get_stacking_context_type(&self) -> Option { - if self.establishes_stacking_context() { + if self.style.establishes_stacking_context() { return Some(StackingContextType::Real); } @@ -291,68 +291,6 @@ impl BoxFragment { StackingContextSection::BlockBackgroundsAndBorders } - /// Returns true if this fragment establishes a new stacking context and false otherwise. - fn establishes_stacking_context(&self) -> bool { - let effects = self.style.get_effects(); - if effects.opacity != 1.0 { - return true; - } - - if effects.mix_blend_mode != ComputedMixBlendMode::Normal { - return true; - } - - if self.has_transform_or_perspective() { - return true; - } - - if !self.style.get_effects().filter.0.is_empty() { - return true; - } - - if self.style.get_box().transform_style == ComputedTransformStyle::Preserve3d || - self.style.overrides_transform_style() - { - return true; - } - - // Fixed position and sticky position always create stacking contexts. - // TODO(mrobinson): We need to handle sticky positioning here when we support it. - if self.style.get_box().position == ComputedPosition::Fixed { - return true; - } - - // Statically positioned fragments don't establish stacking contexts if the previous - // conditions are not fulfilled. Furthermore, z-index doesn't apply to statically - // positioned fragments. - if self.style.get_box().position == ComputedPosition::Static { - return false; - } - - // For absolutely and relatively positioned fragments we only establish a stacking - // context if there is a z-index set. - // See https://www.w3.org/TR/CSS2/visuren.html#z-index - !self.style.get_position().z_index.is_auto() - } - - // Get the effective z-index of this fragment. Z-indices only apply to positioned element - // per CSS 2 9.9.1 (http://www.w3.org/TR/CSS2/visuren.html#z-index), so this value may differ - // from the value specified in the style. - fn effective_z_index(&self) -> i32 { - match self.style.get_box().position { - ComputedPosition::Static => {}, - _ => return self.style.get_position().z_index.integer_or(0), - } - - 0 - } - - /// Returns true if this fragment has a transform, or perspective property set. - fn has_transform_or_perspective(&self) -> bool { - !self.style.get_box().transform.0.is_empty() || - self.style.get_box().perspective != Perspective::None - } - fn build_stacking_context_tree<'a>( &'a self, fragment: &'a Fragment, @@ -452,8 +390,7 @@ impl BoxFragment { // 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 = - wr::SpatialId::root_reference_frame(builder.wr.pipeline_id); + builder.current_space_and_clip.spatial_id = builder.nearest_reference_frame; } fn build_scroll_frame_if_necessary( @@ -505,7 +442,7 @@ impl BoxFragment { builder: &mut DisplayListBuilder, border_rect: &PhysicalRect, ) -> bool { - if !self.has_transform_or_perspective() { + if !self.style.has_transform_or_perspective() { return false; } let untyped_border_rect = border_rect.to_untyped(); @@ -535,6 +472,7 @@ impl BoxFragment { wr::PropertyBinding::Value(reference_frame_transform), reference_frame_kind, ); + builder.nearest_reference_frame = builder.current_space_and_clip.spatial_id; true } diff --git a/components/layout_2020/flow/mod.rs b/components/layout_2020/flow/mod.rs index d6ab15071f6..bc50177fe1e 100644 --- a/components/layout_2020/flow/mod.rs +++ b/components/layout_2020/flow/mod.rs @@ -223,7 +223,8 @@ fn layout_block_level_children<'a>( }) .collect() } else { - let has_positioned_ancestor = positioning_context.has_positioned_ancestor(); + let collects_for_nearest_positioned_ancestor = + positioning_context.collects_for_nearest_positioned_ancestor(); let mut fragments = child_boxes .par_iter() .enumerate() @@ -238,7 +239,7 @@ fn layout_block_level_children<'a>( /* float_context = */ None, ) }, - || PositioningContext::new_for_rayon(has_positioned_ancestor), + || PositioningContext::new_for_rayon(collects_for_nearest_positioned_ancestor), PositioningContext::append, ) .collect(); @@ -275,7 +276,7 @@ impl BlockLevelBox { tag, style, contents, - } => Fragment::Box(positioning_context.for_maybe_position_relative( + } => Fragment::Box(positioning_context.layout_maybe_position_relative_fragment( layout_context, containing_block, style, @@ -293,7 +294,7 @@ impl BlockLevelBox { }, )), BlockLevelBox::Independent(contents) => { - Fragment::Box(positioning_context.for_maybe_position_relative( + Fragment::Box(positioning_context.layout_maybe_position_relative_fragment( layout_context, containing_block, &contents.style, @@ -326,8 +327,7 @@ impl BlockLevelBox { )) }, BlockLevelBox::OutOfFlowFloatBox(_box_) => { - // FIXME: call for_maybe_position_relative here - // TODO + // FIXME: call layout_maybe_position_relative_fragment here Fragment::Anonymous(AnonymousFragment::no_op( containing_block.style.writing_mode, )) diff --git a/components/layout_2020/flow/root.rs b/components/layout_2020/flow/root.rs index f1fcad7fd45..6932e66acf1 100644 --- a/components/layout_2020/flow/root.rs +++ b/components/layout_2020/flow/root.rs @@ -137,7 +137,8 @@ impl BoxTreeRoot { }; let dummy_tree_rank = 0; - let mut positioning_context = PositioningContext::new_for_initial_containing_block(); + let mut positioning_context = + PositioningContext::new_for_containing_block_for_all_descendants(); let mut independent_layout = self.0.layout( layout_context, &mut positioning_context, @@ -145,7 +146,7 @@ impl BoxTreeRoot { dummy_tree_rank, ); - positioning_context.layout_in_initial_containing_block( + positioning_context.layout_initial_containing_block_children( layout_context, &initial_containing_block, &mut independent_layout.fragments, diff --git a/components/layout_2020/positioned.rs b/components/layout_2020/positioned.rs index 3747b4661b5..a0f0aac1c94 100644 --- a/components/layout_2020/positioned.rs +++ b/components/layout_2020/positioned.rs @@ -25,7 +25,12 @@ pub(crate) struct AbsolutelyPositionedBox { pub(crate) struct PositioningContext<'box_tree> { for_nearest_positioned_ancestor: Option>>, - for_initial_containing_block: Vec>, + + // 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>, } #[derive(Debug)] @@ -127,62 +132,158 @@ impl AbsolutelyPositionedBox { } impl<'box_tree> PositioningContext<'box_tree> { - pub(crate) fn new_for_initial_containing_block() -> Self { + pub(crate) fn new_for_containing_block_for_all_descendants() -> Self { Self { for_nearest_positioned_ancestor: None, - for_initial_containing_block: Vec::new(), + for_nearest_containing_block_for_all_descendants: Vec::new(), } } - pub(crate) fn new_for_rayon(has_positioned_ancestor: bool) -> Self { + pub(crate) fn new_for_rayon(collects_for_nearest_positioned_ancestor: bool) -> Self { Self { - for_nearest_positioned_ancestor: if has_positioned_ancestor { + for_nearest_positioned_ancestor: if collects_for_nearest_positioned_ancestor { Some(Vec::new()) } else { None }, - for_initial_containing_block: Vec::new(), + for_nearest_containing_block_for_all_descendants: Vec::new(), } } - pub(crate) fn has_positioned_ancestor(&self) -> bool { + pub(crate) fn collects_for_nearest_positioned_ancestor(&self) -> bool { self.for_nearest_positioned_ancestor.is_some() } - pub(crate) fn for_maybe_position_relative( + /// 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`. + pub(crate) fn layout_maybe_position_relative_fragment( &mut self, layout_context: &LayoutContext, containing_block: &ContainingBlock, style: &ComputedValues, - f: impl FnOnce(&mut Self) -> BoxFragment, + fragment_layout_fn: impl FnOnce(&mut Self) -> BoxFragment, ) -> BoxFragment { - if style.clone_position() == Position::Relative { - let mut fragment = - // Establing a containing block for absolutely positioned descendants - Self::for_positioned(layout_context, &mut self.for_initial_containing_block, f); + debug_assert!(style.clone_position() != Position::Fixed); + debug_assert!(style.clone_position() != Position::Absolute); - fragment.content_rect.start_corner += &relative_adjustement(style, containing_block); - fragment - } else { - f(self) + if style.establishes_containing_block_for_all_descendants() { + let mut fragment = Self::layout_containing_block_for_all_descendants( + layout_context, + fragment_layout_fn, + ); + if style.clone_position() == Position::Relative { + fragment.content_rect.start_corner += + &relative_adjustement(style, containing_block); + } + return fragment; } + + if style.clone_position() == Position::Relative { + let mut fragment = Self::create_and_layout_positioned( + layout_context, + style, + &mut self.for_nearest_containing_block_for_all_descendants, + fragment_layout_fn, + ); + fragment.content_rect.start_corner += &relative_adjustement(style, containing_block); + return fragment; + } + + // We don't need to create a new PositioningContext for this Fragment, so + // we pass in the current one to the fragment layout closure. + fragment_layout_fn(self) } - fn for_positioned( + /// Given `fragment_layout_fn`, a closure which lays out a fragment in a provided + /// `PositioningContext`, create a positioning context a positioned fragment and lay out the + /// fragment and all its children. Returns the resulting `BoxFragment`. + fn create_and_layout_positioned( layout_context: &LayoutContext, - for_initial_containing_block: &mut Vec>, - f: impl FnOnce(&mut Self) -> BoxFragment, + style: &ComputedValues, + for_nearest_containing_block_for_all_descendants: &mut Vec< + HoistedAbsolutelyPositionedBox<'box_tree>, + >, + fragment_layout_fn: impl FnOnce(&mut Self) -> BoxFragment, ) -> BoxFragment { + if style.establishes_containing_block_for_all_descendants() { + return Self::layout_containing_block_for_all_descendants( + layout_context, + fragment_layout_fn, + ); + } + let mut new = Self { for_nearest_positioned_ancestor: Some(Vec::new()), - for_initial_containing_block: std::mem::take(for_initial_containing_block), + for_nearest_containing_block_for_all_descendants: std::mem::take( + for_nearest_containing_block_for_all_descendants, + ), }; - let mut positioned_box_fragment = f(&mut new); - new.layout_in_positioned_ancestor(layout_context, &mut positioned_box_fragment); - *for_initial_containing_block = new.for_initial_containing_block; + let mut positioned_box_fragment = fragment_layout_fn(&mut new); + new.layout_positioned_fragment_children(layout_context, &mut positioned_box_fragment); + *for_nearest_containing_block_for_all_descendants = + new.for_nearest_containing_block_for_all_descendants; positioned_box_fragment } + /// Given `fragment_layout_fn`, a closure which lays out a fragment in a provided + /// `PositioningContext`, create a positioning context for a fragment that establishes a + /// containing block for all descendants and lay out the fragment and all its children using + /// the new positioning context. Returns the resulting `BoxFragment`. + fn layout_containing_block_for_all_descendants( + layout_context: &LayoutContext, + fragment_layout_fn: impl FnOnce(&mut Self) -> BoxFragment, + ) -> BoxFragment { + let mut containing_block_for_all_descendants = + Self::new_for_containing_block_for_all_descendants(); + debug_assert!(containing_block_for_all_descendants + .for_nearest_positioned_ancestor + .is_none()); + + let mut new_fragment = fragment_layout_fn(&mut containing_block_for_all_descendants); + + let padding_rect = Rect { + size: new_fragment.content_rect.size.clone(), + // Ignore the content rect’s position in its own containing block: + start_corner: Vec2::zero(), + } + .inflate(&new_fragment.padding); + let containing_block = DefiniteContainingBlock { + size: padding_rect.size.clone(), + style: &new_fragment.style, + }; + + // Loop because it’s possible that we discover (the static position of) + // more absolutely-positioned boxes while doing layout for others. + let mut new_child_fragments = Vec::new(); + while !containing_block_for_all_descendants + .for_nearest_containing_block_for_all_descendants + .is_empty() + { + HoistedAbsolutelyPositionedBox::layout_many( + layout_context, + &std::mem::take( + &mut containing_block_for_all_descendants + .for_nearest_containing_block_for_all_descendants, + ), + &mut new_child_fragments, + &mut containing_block_for_all_descendants + .for_nearest_containing_block_for_all_descendants, + &containing_block, + ) + } + + new_fragment + .children + .push(Fragment::Anonymous(AnonymousFragment::new( + padding_rect, + new_child_fragments, + new_fragment.style.writing_mode, + ))); + + new_fragment + } + pub(crate) fn push(&mut self, box_: HoistedAbsolutelyPositionedBox<'box_tree>) { if let Some(nearest) = &mut self.for_nearest_positioned_ancestor { match box_ @@ -196,13 +297,14 @@ impl<'box_tree> PositioningContext<'box_tree> { Position::Static | Position::Relative => unreachable!(), } } - self.for_initial_containing_block.push(box_) + self.for_nearest_containing_block_for_all_descendants + .push(box_) } pub(crate) fn append(&mut self, other: Self) { vec_append_owned( - &mut self.for_initial_containing_block, - other.for_initial_containing_block, + &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(), @@ -219,7 +321,8 @@ impl<'box_tree> PositioningContext<'box_tree> { tree_rank_in_parent: usize, f: impl FnOnce(&mut Self) -> Vec, ) -> Vec { - let for_icb_so_far = self.for_initial_containing_block.len(); + 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() @@ -228,7 +331,8 @@ impl<'box_tree> PositioningContext<'box_tree> { let fragments = f(self); adjust_static_positions( - &mut self.for_initial_containing_block[for_icb_so_far..], + &mut self.for_nearest_containing_block_for_all_descendants + [for_containing_block_for_all_descendants..], &fragments, tree_rank_in_parent, ); @@ -242,7 +346,7 @@ impl<'box_tree> PositioningContext<'box_tree> { fragments } - pub(crate) fn layout_in_initial_containing_block( + pub(crate) fn layout_initial_containing_block_children( &mut self, layout_context: &LayoutContext, initial_containing_block: &DefiniteContainingBlock, @@ -252,18 +356,21 @@ impl<'box_tree> PositioningContext<'box_tree> { // Loop because it’s possible that we discover (the static position of) // more absolutely-positioned boxes while doing layout for others. - while !self.for_initial_containing_block.is_empty() { + while !self + .for_nearest_containing_block_for_all_descendants + .is_empty() + { HoistedAbsolutelyPositionedBox::layout_many( layout_context, - &std::mem::take(&mut self.for_initial_containing_block), + &std::mem::take(&mut self.for_nearest_containing_block_for_all_descendants), fragments, - &mut self.for_initial_containing_block, + &mut self.for_nearest_containing_block_for_all_descendants, initial_containing_block, ) } } - fn layout_in_positioned_ancestor( + fn layout_positioned_fragment_children( &mut self, layout_context: &LayoutContext, positioned_box_fragment: &mut BoxFragment, @@ -285,7 +392,7 @@ impl<'box_tree> PositioningContext<'box_tree> { layout_context, &for_here, &mut children, - &mut self.for_initial_containing_block, + &mut self.for_nearest_containing_block_for_all_descendants, &containing_block, ); positioned_box_fragment @@ -304,16 +411,18 @@ impl<'box_tree> HoistedAbsolutelyPositionedBox<'box_tree> { layout_context: &LayoutContext, boxes: &[Self], fragments: &mut Vec, - for_initial_containing_block: &mut Vec>, + for_nearest_containing_block_for_all_descendants: &mut Vec< + HoistedAbsolutelyPositionedBox<'box_tree>, + >, containing_block: &DefiniteContainingBlock, ) { if layout_context.use_rayon { fragments.par_extend(boxes.par_iter().mapfold_reduce_into( - for_initial_containing_block, - |for_initial_containing_block, box_| { + for_nearest_containing_block_for_all_descendants, + |for_nearest_containing_block_for_all_descendants, box_| { Fragment::Box(box_.layout( layout_context, - for_initial_containing_block, + for_nearest_containing_block_for_all_descendants, containing_block, )) }, @@ -324,7 +433,7 @@ impl<'box_tree> HoistedAbsolutelyPositionedBox<'box_tree> { fragments.extend(boxes.iter().map(|box_| { Fragment::Box(box_.layout( layout_context, - for_initial_containing_block, + for_nearest_containing_block_for_all_descendants, containing_block, )) })) @@ -334,7 +443,9 @@ impl<'box_tree> HoistedAbsolutelyPositionedBox<'box_tree> { pub(crate) fn layout( &self, layout_context: &LayoutContext, - for_initial_containing_block: &mut Vec>, + for_nearest_containing_block_for_all_descendants: &mut Vec< + HoistedAbsolutelyPositionedBox<'box_tree>, + >, containing_block: &DefiniteContainingBlock, ) -> BoxFragment { let style = &self.absolutely_positioned_box.contents.style; @@ -396,91 +507,101 @@ impl<'box_tree> HoistedAbsolutelyPositionedBox<'box_tree> { block_end: block_axis.margin_end, }; - let for_icb = for_initial_containing_block; - PositioningContext::for_positioned(layout_context, for_icb, |positioning_context| { - let size; - let fragments; - match self.absolutely_positioned_box.contents.as_replaced() { - Ok(replaced) => { - // https://drafts.csswg.org/css2/visudet.html#abs-replaced-width - // https://drafts.csswg.org/css2/visudet.html#abs-replaced-height - let style = &self.absolutely_positioned_box.contents.style; - size = replaced_used_size.unwrap(); - fragments = replaced.make_fragments(style, size.clone()); - }, - Err(non_replaced) => { - // https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-width - // https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-height - let inline_size = inline_axis.size.auto_is(|| { - let available_size = match inline_axis.anchor { - Anchor::Start(start) => { - cbis - start - pb.inline_sum() - margin.inline_sum() - }, - Anchor::End(end) => cbis - end - pb.inline_sum() - margin.inline_sum(), + let for_containing_block_for_all_descendants = + for_nearest_containing_block_for_all_descendants; + PositioningContext::create_and_layout_positioned( + layout_context, + style, + for_containing_block_for_all_descendants, + |positioning_context| { + let size; + let fragments; + match self.absolutely_positioned_box.contents.as_replaced() { + Ok(replaced) => { + // https://drafts.csswg.org/css2/visudet.html#abs-replaced-width + // https://drafts.csswg.org/css2/visudet.html#abs-replaced-height + let style = &self.absolutely_positioned_box.contents.style; + size = replaced_used_size.unwrap(); + fragments = replaced.make_fragments(style, size.clone()); + }, + Err(non_replaced) => { + // https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-width + // https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-height + let inline_size = inline_axis.size.auto_is(|| { + let available_size = match inline_axis.anchor { + Anchor::Start(start) => { + cbis - start - pb.inline_sum() - margin.inline_sum() + }, + Anchor::End(end) => { + cbis - end - pb.inline_sum() - margin.inline_sum() + }, + }; + self.absolutely_positioned_box + .contents + .content_sizes + .shrink_to_fit(available_size) + }); + + let containing_block_for_children = ContainingBlock { + inline_size, + block_size: block_axis.size, + style, }; - self.absolutely_positioned_box - .contents - .content_sizes - .shrink_to_fit(available_size) - }); + // https://drafts.csswg.org/css-writing-modes/#orthogonal-flows + assert_eq!( + containing_block.style.writing_mode, + containing_block_for_children.style.writing_mode, + "Mixed writing modes are not supported yet" + ); + let dummy_tree_rank = 0; + let independent_layout = non_replaced.layout( + layout_context, + positioning_context, + &containing_block_for_children, + dummy_tree_rank, + ); - let containing_block_for_children = ContainingBlock { - inline_size, - block_size: block_axis.size, - style, - }; - // https://drafts.csswg.org/css-writing-modes/#orthogonal-flows - assert_eq!( - containing_block.style.writing_mode, - containing_block_for_children.style.writing_mode, - "Mixed writing modes are not supported yet" - ); - let dummy_tree_rank = 0; - let independent_layout = non_replaced.layout( - layout_context, - positioning_context, - &containing_block_for_children, - dummy_tree_rank, - ); + size = Vec2 { + inline: inline_size, + block: block_axis + .size + .auto_is(|| independent_layout.content_block_size), + }; + fragments = independent_layout.fragments + }, + }; - size = Vec2 { - inline: inline_size, - block: block_axis - .size - .auto_is(|| independent_layout.content_block_size), - }; - fragments = independent_layout.fragments - }, - }; + let inline_start = match inline_axis.anchor { + Anchor::Start(start) => start + pb.inline_start + margin.inline_start, + Anchor::End(end) => { + cbis - end - pb.inline_end - margin.inline_end - size.inline + }, + }; + let block_start = match block_axis.anchor { + Anchor::Start(start) => start + pb.block_start + margin.block_start, + Anchor::End(end) => cbbs - end - pb.block_end - margin.block_end - size.block, + }; - let inline_start = match inline_axis.anchor { - Anchor::Start(start) => start + pb.inline_start + margin.inline_start, - Anchor::End(end) => cbis - end - pb.inline_end - margin.inline_end - size.inline, - }; - let block_start = match block_axis.anchor { - Anchor::Start(start) => start + pb.block_start + margin.block_start, - Anchor::End(end) => cbbs - end - pb.block_end - margin.block_end - size.block, - }; + let content_rect = Rect { + start_corner: Vec2 { + inline: inline_start, + block: block_start, + }, + size, + }; - let content_rect = Rect { - start_corner: Vec2 { - inline: inline_start, - block: block_start, - }, - size, - }; - - BoxFragment::new( - self.absolutely_positioned_box.contents.tag, - style.clone(), - fragments, - content_rect, - padding, - border, - margin, - CollapsedBlockMargins::zero(), - ) - }) + BoxFragment::new( + self.absolutely_positioned_box.contents.tag, + style.clone(), + fragments, + content_rect, + padding, + border, + margin, + CollapsedBlockMargins::zero(), + ) + }, + ) } } diff --git a/components/layout_2020/style_ext.rs b/components/layout_2020/style_ext.rs index 30b384f3884..44245e2c89d 100644 --- a/components/layout_2020/style_ext.rs +++ b/components/layout_2020/style_ext.rs @@ -3,9 +3,13 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use crate::geom::{flow_relative, PhysicalSides, PhysicalSize}; +use style::computed_values::mix_blend_mode::T as ComputedMixBlendMode; +use style::computed_values::position::T as ComputedPosition; +use style::computed_values::transform_style::T as ComputedTransformStyle; use style::properties::ComputedValues; use style::values::computed::{Length, LengthPercentage, LengthPercentageOrAuto}; use style::values::computed::{NonNegativeLengthPercentage, Size}; +use style::values::generics::box_::Perspective; use style::values::generics::length::MaxSize; use style::values::specified::box_ as stylo; @@ -49,6 +53,10 @@ pub(crate) trait ComputedValuesExt { fn padding(&self) -> flow_relative::Sides; fn border_width(&self) -> flow_relative::Sides; fn margin(&self) -> flow_relative::Sides; + fn has_transform_or_perspective(&self) -> bool; + fn effective_z_index(&self) -> i32; + fn establishes_stacking_context(&self) -> bool; + fn establishes_containing_block_for_all_descendants(&self) -> bool; } impl ComputedValuesExt for ComputedValues { @@ -165,6 +173,80 @@ impl ComputedValuesExt for ComputedValues { self.writing_mode, ) } + + /// Returns true if this style has a transform, or perspective property set. + fn has_transform_or_perspective(&self) -> bool { + !self.get_box().transform.0.is_empty() || self.get_box().perspective != Perspective::None + } + + /// Get the effective z-index of this fragment. Z-indices only apply to positioned elements + /// per CSS 2 9.9.1 (http://www.w3.org/TR/CSS2/visuren.html#z-index), so this value may differ + /// from the value specified in the style. + fn effective_z_index(&self) -> i32 { + match self.get_box().position { + ComputedPosition::Static => 0, + _ => self.get_position().z_index.integer_or(0), + } + } + + /// Returns true if this fragment establishes a new stacking context and false otherwise. + fn establishes_stacking_context(&self) -> bool { + let effects = self.get_effects(); + if effects.opacity != 1.0 { + return true; + } + + if effects.mix_blend_mode != ComputedMixBlendMode::Normal { + return true; + } + + if self.has_transform_or_perspective() { + return true; + } + + if !self.get_effects().filter.0.is_empty() { + return true; + } + + if self.get_box().transform_style == ComputedTransformStyle::Preserve3d || + self.overrides_transform_style() + { + return true; + } + + // Fixed position and sticky position always create stacking contexts. + // TODO(mrobinson): We need to handle sticky positioning here when we support it. + if self.get_box().position == ComputedPosition::Fixed { + return true; + } + + // Statically positioned fragments don't establish stacking contexts if the previous + // conditions are not fulfilled. Furthermore, z-index doesn't apply to statically + // positioned fragments. + if self.get_box().position == ComputedPosition::Static { + return false; + } + + // For absolutely and relatively positioned fragments we only establish a stacking + // context if there is a z-index set. + // See https://www.w3.org/TR/CSS2/visuren.html#z-index + !self.get_position().z_index.is_auto() + } + + /// Returns true if this style establishes a containing block for all descendants + /// including fixed and absolutely positioned ones. + fn establishes_containing_block_for_all_descendants(&self) -> bool { + if self.has_transform_or_perspective() { + return true; + } + + if !self.get_effects().filter.0.is_empty() { + return true; + } + + // TODO: We need to handle CSS Contain here. + false + } } impl From for Display { diff --git a/tests/wpt/metadata-layout-2020/css/css-transforms/preserve3d-button.html.ini b/tests/wpt/metadata-layout-2020/css/css-transforms/preserve3d-button.html.ini deleted file mode 100644 index 0c6784a80ed..00000000000 --- a/tests/wpt/metadata-layout-2020/css/css-transforms/preserve3d-button.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[preserve3d-button.html] - expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/css-transforms/transform-abspos-001.html.ini b/tests/wpt/metadata-layout-2020/css/css-transforms/transform-abspos-001.html.ini deleted file mode 100644 index a99bde1ab74..00000000000 --- a/tests/wpt/metadata-layout-2020/css/css-transforms/transform-abspos-001.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[transform-abspos-001.html] - expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/css-transforms/transform-abspos-002.html.ini b/tests/wpt/metadata-layout-2020/css/css-transforms/transform-abspos-002.html.ini deleted file mode 100644 index 55e530b062c..00000000000 --- a/tests/wpt/metadata-layout-2020/css/css-transforms/transform-abspos-002.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[transform-abspos-002.html] - expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/css-transforms/transform-abspos-003.html.ini b/tests/wpt/metadata-layout-2020/css/css-transforms/transform-abspos-003.html.ini deleted file mode 100644 index 80712e40569..00000000000 --- a/tests/wpt/metadata-layout-2020/css/css-transforms/transform-abspos-003.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[transform-abspos-003.html] - expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/css-transforms/transform-abspos-004.html.ini b/tests/wpt/metadata-layout-2020/css/css-transforms/transform-abspos-004.html.ini deleted file mode 100644 index 3196c91458d..00000000000 --- a/tests/wpt/metadata-layout-2020/css/css-transforms/transform-abspos-004.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[transform-abspos-004.html] - expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/css-transforms/transform-abspos-006.html.ini b/tests/wpt/metadata-layout-2020/css/css-transforms/transform-abspos-006.html.ini deleted file mode 100644 index 45970f93d38..00000000000 --- a/tests/wpt/metadata-layout-2020/css/css-transforms/transform-abspos-006.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[transform-abspos-006.html] - expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/css-transforms/transform-abspos-007.html.ini b/tests/wpt/metadata-layout-2020/css/css-transforms/transform-abspos-007.html.ini deleted file mode 100644 index 8bccac28610..00000000000 --- a/tests/wpt/metadata-layout-2020/css/css-transforms/transform-abspos-007.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[transform-abspos-007.html] - expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/filter-effects/filtered-inline-is-container.html.ini b/tests/wpt/metadata-layout-2020/css/filter-effects/filtered-inline-is-container.html.ini new file mode 100644 index 00000000000..b41a8df9ac0 --- /dev/null +++ b/tests/wpt/metadata-layout-2020/css/filter-effects/filtered-inline-is-container.html.ini @@ -0,0 +1,2 @@ +[filtered-inline-is-container.html] + expected: FAIL diff --git a/tests/wpt/mozilla/meta-layout-2020/css/transform_stacking_context_a.html.ini b/tests/wpt/mozilla/meta-layout-2020/css/transform_stacking_context_a.html.ini deleted file mode 100644 index 18d0ddfbd1f..00000000000 --- a/tests/wpt/mozilla/meta-layout-2020/css/transform_stacking_context_a.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[transform_stacking_context_a.html] - expected: FAIL diff --git a/tests/wpt/mozilla/meta-layout-2020/css/translate_clip_nested.html.ini b/tests/wpt/mozilla/meta-layout-2020/css/translate_clip_nested.html.ini deleted file mode 100644 index 2c1f8cb35d9..00000000000 --- a/tests/wpt/mozilla/meta-layout-2020/css/translate_clip_nested.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[translate_clip_nested.html] - expected: FAIL