mirror of
https://github.com/servo/servo.git
synced 2025-08-03 12:40:06 +01:00
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:
commit
19323e8f28
16 changed files with 354 additions and 217 deletions
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
))
|
))
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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 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>) {
|
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 it’s possible that we discover (the static position of)
|
// Loop because it’s 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(),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
[preserve3d-button.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[transform-abspos-001.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[transform-abspos-002.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[transform-abspos-003.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[transform-abspos-004.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[transform-abspos-006.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[transform-abspos-007.html]
|
|
||||||
expected: FAIL
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
[filtered-inline-is-container.html]
|
||||||
|
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
||||||
[transform_stacking_context_a.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[translate_clip_nested.html]
|
|
||||||
expected: FAIL
|
|
Loading…
Add table
Add a link
Reference in a new issue