Auto merge of #25862 - mrobinson:transforms-containing-block, r=pcwalton,nox

Have transforms and filters be CBs for all descendants in layout_2020

This is a feature that was never properly implemented in the previous
layout system. We still need to preserve their in-tree order in the
display list though.

<!-- Please describe your changes on the following line: -->

---
<!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `___` with appropriate data: -->
- [x] `./mach build -d` does not report any errors
- [x] `./mach test-tidy` does not report any errors
- [ ] These changes fix #___ (GitHub issue number if applicable)

<!-- Either: -->
- [x] There are tests for these changes OR
- [ ] These changes do not require tests because ___

<!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.-->

<!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->
This commit is contained in:
bors-servo 2020-03-02 07:23:15 -05:00 committed by GitHub
commit 19323e8f28
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 354 additions and 217 deletions

View file

@ -37,7 +37,12 @@ type ItemTag = (u64, u16);
type HitInfo = Option<ItemTag>; type HitInfo = Option<ItemTag>;
pub struct DisplayListBuilder<'a> { pub struct DisplayListBuilder<'a> {
/// The current SpatialId and ClipId information for this `DisplayListBuilder`.
current_space_and_clip: wr::SpaceAndClipInfo, 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 context: &'a LayoutContext<'a>,
pub wr: wr::DisplayListBuilder, pub wr: wr::DisplayListBuilder,
@ -56,6 +61,7 @@ impl<'a> DisplayListBuilder<'a> {
) -> Self { ) -> Self {
Self { Self {
current_space_and_clip: wr::SpaceAndClipInfo::root_scroll(pipeline_id), current_space_and_clip: wr::SpaceAndClipInfo::root_scroll(pipeline_id),
nearest_reference_frame: wr::SpatialId::root_reference_frame(pipeline_id),
is_contentful: false, is_contentful: false,
context, context,
wr: wr::DisplayListBuilder::new(pipeline_id, viewport_size), wr: wr::DisplayListBuilder::new(pipeline_id, viewport_size),
@ -68,9 +74,14 @@ impl<'a> DisplayListBuilder<'a> {
} }
fn clipping_and_scrolling_scope<R>(&mut self, f: impl FnOnce(&mut Self) -> R) -> R { fn clipping_and_scrolling_scope<R>(&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); 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 result
} }
} }

View file

@ -6,6 +6,7 @@ use crate::display_list::conversions::ToWebRender;
use crate::display_list::DisplayListBuilder; use crate::display_list::DisplayListBuilder;
use crate::fragments::{AnonymousFragment, BoxFragment, Fragment}; use crate::fragments::{AnonymousFragment, BoxFragment, Fragment};
use crate::geom::PhysicalRect; use crate::geom::PhysicalRect;
use crate::style_ext::ComputedValuesExt;
use euclid::default::Rect; use euclid::default::Rect;
use gfx_traits::{combine_id_with_fragment_type, FragmentType}; use gfx_traits::{combine_id_with_fragment_type, FragmentType};
use std::cmp::Ordering; 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::mix_blend_mode::T as ComputedMixBlendMode;
use style::computed_values::overflow_x::T as ComputedOverflow; use style::computed_values::overflow_x::T as ComputedOverflow;
use style::computed_values::position::T as ComputedPosition; 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::computed::Length;
use style::values::generics::box_::Perspective; use style::values::generics::box_::Perspective;
use style::values::generics::transform; use style::values::generics::transform;
@ -96,7 +96,7 @@ impl<'a> StackingContext<'a> {
fn z_index(&self) -> i32 { fn z_index(&self) -> i32 {
match self.initializing_fragment { match self.initializing_fragment {
Some(fragment) => fragment.effective_z_index(), Some(fragment) => fragment.style.effective_z_index(),
None => 0, None => 0,
} }
} }
@ -259,7 +259,7 @@ impl Fragment {
impl BoxFragment { impl BoxFragment {
fn get_stacking_context_type(&self) -> Option<StackingContextType> { fn get_stacking_context_type(&self) -> Option<StackingContextType> {
if self.establishes_stacking_context() { if self.style.establishes_stacking_context() {
return Some(StackingContextType::Real); return Some(StackingContextType::Real);
} }
@ -291,68 +291,6 @@ impl BoxFragment {
StackingContextSection::BlockBackgroundsAndBorders 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>( fn build_stacking_context_tree<'a>(
&'a self, &'a self,
fragment: &'a Fragment, fragment: &'a Fragment,
@ -452,8 +390,7 @@ impl BoxFragment {
// TODO(mrobinson): Eventually this should use the spatial id of the reference // 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 // frame that is the parent of this one once we have full support for stacking
// contexts and transforms. // contexts and transforms.
builder.current_space_and_clip.spatial_id = builder.current_space_and_clip.spatial_id = builder.nearest_reference_frame;
wr::SpatialId::root_reference_frame(builder.wr.pipeline_id);
} }
fn build_scroll_frame_if_necessary( fn build_scroll_frame_if_necessary(
@ -505,7 +442,7 @@ impl BoxFragment {
builder: &mut DisplayListBuilder, builder: &mut DisplayListBuilder,
border_rect: &PhysicalRect<Length>, border_rect: &PhysicalRect<Length>,
) -> bool { ) -> bool {
if !self.has_transform_or_perspective() { if !self.style.has_transform_or_perspective() {
return false; return false;
} }
let untyped_border_rect = border_rect.to_untyped(); let untyped_border_rect = border_rect.to_untyped();
@ -535,6 +472,7 @@ impl BoxFragment {
wr::PropertyBinding::Value(reference_frame_transform), wr::PropertyBinding::Value(reference_frame_transform),
reference_frame_kind, reference_frame_kind,
); );
builder.nearest_reference_frame = builder.current_space_and_clip.spatial_id;
true true
} }

View file

@ -223,7 +223,8 @@ fn layout_block_level_children<'a>(
}) })
.collect() .collect()
} else { } 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 let mut fragments = child_boxes
.par_iter() .par_iter()
.enumerate() .enumerate()
@ -238,7 +239,7 @@ fn layout_block_level_children<'a>(
/* float_context = */ None, /* float_context = */ None,
) )
}, },
|| PositioningContext::new_for_rayon(has_positioned_ancestor), || PositioningContext::new_for_rayon(collects_for_nearest_positioned_ancestor),
PositioningContext::append, PositioningContext::append,
) )
.collect(); .collect();
@ -275,7 +276,7 @@ impl BlockLevelBox {
tag, tag,
style, style,
contents, contents,
} => Fragment::Box(positioning_context.for_maybe_position_relative( } => Fragment::Box(positioning_context.layout_maybe_position_relative_fragment(
layout_context, layout_context,
containing_block, containing_block,
style, style,
@ -293,7 +294,7 @@ impl BlockLevelBox {
}, },
)), )),
BlockLevelBox::Independent(contents) => { BlockLevelBox::Independent(contents) => {
Fragment::Box(positioning_context.for_maybe_position_relative( Fragment::Box(positioning_context.layout_maybe_position_relative_fragment(
layout_context, layout_context,
containing_block, containing_block,
&contents.style, &contents.style,
@ -326,8 +327,7 @@ impl BlockLevelBox {
)) ))
}, },
BlockLevelBox::OutOfFlowFloatBox(_box_) => { BlockLevelBox::OutOfFlowFloatBox(_box_) => {
// FIXME: call for_maybe_position_relative here // FIXME: call layout_maybe_position_relative_fragment here
// TODO
Fragment::Anonymous(AnonymousFragment::no_op( Fragment::Anonymous(AnonymousFragment::no_op(
containing_block.style.writing_mode, containing_block.style.writing_mode,
)) ))

View file

@ -137,7 +137,8 @@ impl BoxTreeRoot {
}; };
let dummy_tree_rank = 0; 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( let mut independent_layout = self.0.layout(
layout_context, layout_context,
&mut positioning_context, &mut positioning_context,
@ -145,7 +146,7 @@ impl BoxTreeRoot {
dummy_tree_rank, dummy_tree_rank,
); );
positioning_context.layout_in_initial_containing_block( positioning_context.layout_initial_containing_block_children(
layout_context, layout_context,
&initial_containing_block, &initial_containing_block,
&mut independent_layout.fragments, &mut independent_layout.fragments,

View file

@ -25,7 +25,12 @@ pub(crate) struct AbsolutelyPositionedBox {
pub(crate) struct PositioningContext<'box_tree> { pub(crate) struct PositioningContext<'box_tree> {
for_nearest_positioned_ancestor: Option<Vec<HoistedAbsolutelyPositionedBox<'box_tree>>>, for_nearest_positioned_ancestor: Option<Vec<HoistedAbsolutelyPositionedBox<'box_tree>>>,
for_initial_containing_block: Vec<HoistedAbsolutelyPositionedBox<'box_tree>>,
// 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<'box_tree>>,
} }
#[derive(Debug)] #[derive(Debug)]
@ -127,62 +132,158 @@ impl AbsolutelyPositionedBox {
} }
impl<'box_tree> PositioningContext<'box_tree> { 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 { Self {
for_nearest_positioned_ancestor: None, 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 { Self {
for_nearest_positioned_ancestor: if has_positioned_ancestor { for_nearest_positioned_ancestor: if collects_for_nearest_positioned_ancestor {
Some(Vec::new()) Some(Vec::new())
} else { } else {
None 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() 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, &mut self,
layout_context: &LayoutContext, layout_context: &LayoutContext,
containing_block: &ContainingBlock, containing_block: &ContainingBlock,
style: &ComputedValues, style: &ComputedValues,
f: impl FnOnce(&mut Self) -> BoxFragment, fragment_layout_fn: impl FnOnce(&mut Self) -> BoxFragment,
) -> BoxFragment { ) -> BoxFragment {
if style.clone_position() == Position::Relative { debug_assert!(style.clone_position() != Position::Fixed);
let mut fragment = debug_assert!(style.clone_position() != Position::Absolute);
// Establing a containing block for absolutely positioned descendants
Self::for_positioned(layout_context, &mut self.for_initial_containing_block, f);
fragment.content_rect.start_corner += &relative_adjustement(style, containing_block); if style.establishes_containing_block_for_all_descendants() {
fragment let mut fragment = Self::layout_containing_block_for_all_descendants(
} else { layout_context,
f(self) 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, layout_context: &LayoutContext,
for_initial_containing_block: &mut Vec<HoistedAbsolutelyPositionedBox<'box_tree>>, style: &ComputedValues,
f: impl FnOnce(&mut Self) -> BoxFragment, for_nearest_containing_block_for_all_descendants: &mut Vec<
HoistedAbsolutelyPositionedBox<'box_tree>,
>,
fragment_layout_fn: impl FnOnce(&mut Self) -> BoxFragment,
) -> 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 { let mut new = Self {
for_nearest_positioned_ancestor: Some(Vec::new()), 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); let mut positioned_box_fragment = fragment_layout_fn(&mut new);
new.layout_in_positioned_ancestor(layout_context, &mut positioned_box_fragment); new.layout_positioned_fragment_children(layout_context, &mut positioned_box_fragment);
*for_initial_containing_block = new.for_initial_containing_block; *for_nearest_containing_block_for_all_descendants =
new.for_nearest_containing_block_for_all_descendants;
positioned_box_fragment 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 rects 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 its 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>) { pub(crate) fn push(&mut self, box_: HoistedAbsolutelyPositionedBox<'box_tree>) {
if let Some(nearest) = &mut self.for_nearest_positioned_ancestor { if let Some(nearest) = &mut self.for_nearest_positioned_ancestor {
match box_ match box_
@ -196,13 +297,14 @@ impl<'box_tree> PositioningContext<'box_tree> {
Position::Static | Position::Relative => unreachable!(), 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) { pub(crate) fn append(&mut self, other: Self) {
vec_append_owned( vec_append_owned(
&mut self.for_initial_containing_block, &mut self.for_nearest_containing_block_for_all_descendants,
other.for_initial_containing_block, other.for_nearest_containing_block_for_all_descendants,
); );
match ( match (
self.for_nearest_positioned_ancestor.as_mut(), self.for_nearest_positioned_ancestor.as_mut(),
@ -219,7 +321,8 @@ impl<'box_tree> PositioningContext<'box_tree> {
tree_rank_in_parent: usize, tree_rank_in_parent: usize,
f: impl FnOnce(&mut Self) -> Vec<Fragment>, f: impl FnOnce(&mut Self) -> Vec<Fragment>,
) -> Vec<Fragment> { ) -> Vec<Fragment> {
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 let for_nearest_so_far = self
.for_nearest_positioned_ancestor .for_nearest_positioned_ancestor
.as_ref() .as_ref()
@ -228,7 +331,8 @@ impl<'box_tree> PositioningContext<'box_tree> {
let fragments = f(self); let fragments = f(self);
adjust_static_positions( 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, &fragments,
tree_rank_in_parent, tree_rank_in_parent,
); );
@ -242,7 +346,7 @@ impl<'box_tree> PositioningContext<'box_tree> {
fragments fragments
} }
pub(crate) fn layout_in_initial_containing_block( pub(crate) fn layout_initial_containing_block_children(
&mut self, &mut self,
layout_context: &LayoutContext, layout_context: &LayoutContext,
initial_containing_block: &DefiniteContainingBlock, initial_containing_block: &DefiniteContainingBlock,
@ -252,18 +356,21 @@ impl<'box_tree> PositioningContext<'box_tree> {
// Loop because its possible that we discover (the static position of) // Loop because its possible that we discover (the static position of)
// more absolutely-positioned boxes while doing layout for others. // 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( HoistedAbsolutelyPositionedBox::layout_many(
layout_context, layout_context,
&std::mem::take(&mut self.for_initial_containing_block), &std::mem::take(&mut self.for_nearest_containing_block_for_all_descendants),
fragments, fragments,
&mut self.for_initial_containing_block, &mut self.for_nearest_containing_block_for_all_descendants,
initial_containing_block, initial_containing_block,
) )
} }
} }
fn layout_in_positioned_ancestor( fn layout_positioned_fragment_children(
&mut self, &mut self,
layout_context: &LayoutContext, layout_context: &LayoutContext,
positioned_box_fragment: &mut BoxFragment, positioned_box_fragment: &mut BoxFragment,
@ -285,7 +392,7 @@ impl<'box_tree> PositioningContext<'box_tree> {
layout_context, layout_context,
&for_here, &for_here,
&mut children, &mut children,
&mut self.for_initial_containing_block, &mut self.for_nearest_containing_block_for_all_descendants,
&containing_block, &containing_block,
); );
positioned_box_fragment positioned_box_fragment
@ -304,16 +411,18 @@ impl<'box_tree> HoistedAbsolutelyPositionedBox<'box_tree> {
layout_context: &LayoutContext, layout_context: &LayoutContext,
boxes: &[Self], boxes: &[Self],
fragments: &mut Vec<Fragment>, fragments: &mut Vec<Fragment>,
for_initial_containing_block: &mut Vec<HoistedAbsolutelyPositionedBox<'box_tree>>, for_nearest_containing_block_for_all_descendants: &mut Vec<
HoistedAbsolutelyPositionedBox<'box_tree>,
>,
containing_block: &DefiniteContainingBlock, containing_block: &DefiniteContainingBlock,
) { ) {
if layout_context.use_rayon { if layout_context.use_rayon {
fragments.par_extend(boxes.par_iter().mapfold_reduce_into( fragments.par_extend(boxes.par_iter().mapfold_reduce_into(
for_initial_containing_block, for_nearest_containing_block_for_all_descendants,
|for_initial_containing_block, box_| { |for_nearest_containing_block_for_all_descendants, box_| {
Fragment::Box(box_.layout( Fragment::Box(box_.layout(
layout_context, layout_context,
for_initial_containing_block, for_nearest_containing_block_for_all_descendants,
containing_block, containing_block,
)) ))
}, },
@ -324,7 +433,7 @@ impl<'box_tree> HoistedAbsolutelyPositionedBox<'box_tree> {
fragments.extend(boxes.iter().map(|box_| { fragments.extend(boxes.iter().map(|box_| {
Fragment::Box(box_.layout( Fragment::Box(box_.layout(
layout_context, layout_context,
for_initial_containing_block, for_nearest_containing_block_for_all_descendants,
containing_block, containing_block,
)) ))
})) }))
@ -334,7 +443,9 @@ impl<'box_tree> HoistedAbsolutelyPositionedBox<'box_tree> {
pub(crate) fn layout( pub(crate) fn layout(
&self, &self,
layout_context: &LayoutContext, layout_context: &LayoutContext,
for_initial_containing_block: &mut Vec<HoistedAbsolutelyPositionedBox<'box_tree>>, for_nearest_containing_block_for_all_descendants: &mut Vec<
HoistedAbsolutelyPositionedBox<'box_tree>,
>,
containing_block: &DefiniteContainingBlock, containing_block: &DefiniteContainingBlock,
) -> BoxFragment { ) -> BoxFragment {
let style = &self.absolutely_positioned_box.contents.style; let style = &self.absolutely_positioned_box.contents.style;
@ -396,91 +507,101 @@ impl<'box_tree> HoistedAbsolutelyPositionedBox<'box_tree> {
block_end: block_axis.margin_end, block_end: block_axis.margin_end,
}; };
let for_icb = for_initial_containing_block; let for_containing_block_for_all_descendants =
PositioningContext::for_positioned(layout_context, for_icb, |positioning_context| { for_nearest_containing_block_for_all_descendants;
let size; PositioningContext::create_and_layout_positioned(
let fragments; layout_context,
match self.absolutely_positioned_box.contents.as_replaced() { style,
Ok(replaced) => { for_containing_block_for_all_descendants,
// https://drafts.csswg.org/css2/visudet.html#abs-replaced-width |positioning_context| {
// https://drafts.csswg.org/css2/visudet.html#abs-replaced-height let size;
let style = &self.absolutely_positioned_box.contents.style; let fragments;
size = replaced_used_size.unwrap(); match self.absolutely_positioned_box.contents.as_replaced() {
fragments = replaced.make_fragments(style, size.clone()); Ok(replaced) => {
}, // https://drafts.csswg.org/css2/visudet.html#abs-replaced-width
Err(non_replaced) => { // https://drafts.csswg.org/css2/visudet.html#abs-replaced-height
// https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-width let style = &self.absolutely_positioned_box.contents.style;
// https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-height size = replaced_used_size.unwrap();
let inline_size = inline_axis.size.auto_is(|| { fragments = replaced.make_fragments(style, size.clone());
let available_size = match inline_axis.anchor { },
Anchor::Start(start) => { Err(non_replaced) => {
cbis - start - pb.inline_sum() - margin.inline_sum() // https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-width
}, // https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-height
Anchor::End(end) => cbis - end - pb.inline_sum() - margin.inline_sum(), 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 // https://drafts.csswg.org/css-writing-modes/#orthogonal-flows
.contents assert_eq!(
.content_sizes containing_block.style.writing_mode,
.shrink_to_fit(available_size) 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 { size = Vec2 {
inline_size, inline: inline_size,
block_size: block_axis.size, block: block_axis
style, .size
}; .auto_is(|| independent_layout.content_block_size),
// https://drafts.csswg.org/css-writing-modes/#orthogonal-flows };
assert_eq!( fragments = independent_layout.fragments
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 { let inline_start = match inline_axis.anchor {
inline: inline_size, Anchor::Start(start) => start + pb.inline_start + margin.inline_start,
block: block_axis Anchor::End(end) => {
.size cbis - end - pb.inline_end - margin.inline_end - size.inline
.auto_is(|| independent_layout.content_block_size), },
}; };
fragments = independent_layout.fragments 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 { let content_rect = Rect {
Anchor::Start(start) => start + pb.inline_start + margin.inline_start, start_corner: Vec2 {
Anchor::End(end) => cbis - end - pb.inline_end - margin.inline_end - size.inline, inline: inline_start,
}; block: block_start,
let block_start = match block_axis.anchor { },
Anchor::Start(start) => start + pb.block_start + margin.block_start, size,
Anchor::End(end) => cbbs - end - pb.block_end - margin.block_end - size.block, };
};
let content_rect = Rect { BoxFragment::new(
start_corner: Vec2 { self.absolutely_positioned_box.contents.tag,
inline: inline_start, style.clone(),
block: block_start, fragments,
}, content_rect,
size, padding,
}; border,
margin,
BoxFragment::new( CollapsedBlockMargins::zero(),
self.absolutely_positioned_box.contents.tag, )
style.clone(), },
fragments, )
content_rect,
padding,
border,
margin,
CollapsedBlockMargins::zero(),
)
})
} }
} }

View file

@ -3,9 +3,13 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use crate::geom::{flow_relative, PhysicalSides, PhysicalSize}; 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::properties::ComputedValues;
use style::values::computed::{Length, LengthPercentage, LengthPercentageOrAuto}; use style::values::computed::{Length, LengthPercentage, LengthPercentageOrAuto};
use style::values::computed::{NonNegativeLengthPercentage, Size}; use style::values::computed::{NonNegativeLengthPercentage, Size};
use style::values::generics::box_::Perspective;
use style::values::generics::length::MaxSize; use style::values::generics::length::MaxSize;
use style::values::specified::box_ as stylo; use style::values::specified::box_ as stylo;
@ -49,6 +53,10 @@ pub(crate) trait ComputedValuesExt {
fn padding(&self) -> flow_relative::Sides<LengthPercentage>; fn padding(&self) -> flow_relative::Sides<LengthPercentage>;
fn border_width(&self) -> flow_relative::Sides<Length>; fn border_width(&self) -> flow_relative::Sides<Length>;
fn margin(&self) -> flow_relative::Sides<LengthPercentageOrAuto>; fn margin(&self) -> flow_relative::Sides<LengthPercentageOrAuto>;
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 { impl ComputedValuesExt for ComputedValues {
@ -165,6 +173,80 @@ impl ComputedValuesExt for ComputedValues {
self.writing_mode, 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<stylo::Display> for Display { impl From<stylo::Display> for Display {

View file

@ -1,2 +0,0 @@
[preserve3d-button.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[transform-abspos-001.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[transform-abspos-002.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[transform-abspos-003.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[transform-abspos-004.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[transform-abspos-006.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[transform-abspos-007.html]
expected: FAIL

View file

@ -0,0 +1,2 @@
[filtered-inline-is-container.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[transform_stacking_context_a.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[translate_clip_nested.html]
expected: FAIL