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>;
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<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);
self.current_space_and_clip = previous;
self.current_space_and_clip = previous_space_and_clip;
self.nearest_reference_frame = previous_nearest_reference_frame;
result
}
}

View file

@ -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<StackingContextType> {
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<Length>,
) -> 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
}

View file

@ -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,
))

View file

@ -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,

View file

@ -25,7 +25,12 @@ pub(crate) struct AbsolutelyPositionedBox {
pub(crate) struct PositioningContext<'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)]
@ -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<HoistedAbsolutelyPositionedBox<'box_tree>>,
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 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>) {
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<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
.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 its 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<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,
) {
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<HoistedAbsolutelyPositionedBox<'box_tree>>,
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(),
)
},
)
}
}

View file

@ -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<LengthPercentage>;
fn border_width(&self) -> flow_relative::Sides<Length>;
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 {
@ -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<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