layout: Refactor establishes_containing_block_for_all_descendants() (#36725)

Instead of grouping all the `will-change` conditions together, move each
one of them next to the condition for the relevant property.

This avoids the need to call `is_transformable()` multiple times, and
will also be needed for #35391.

Testing: unneeded (no change in behavior)

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
This commit is contained in:
Oriol Brufau 2025-04-28 03:17:35 -07:00 committed by GitHub
parent b1d0b6a37b
commit cf41012257
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 52 additions and 30 deletions

View file

@ -530,7 +530,7 @@ impl StackingContext {
if effects.filter.0.is_empty() && if effects.filter.0.is_empty() &&
effects.opacity == 1.0 && effects.opacity == 1.0 &&
effects.mix_blend_mode == ComputedMixBlendMode::Normal && effects.mix_blend_mode == ComputedMixBlendMode::Normal &&
!style.has_transform_or_perspective(FragmentFlags::empty()) && !style.has_effective_transform_or_perspective(FragmentFlags::empty()) &&
style.clone_clip_path() == ClipPath::None style.clone_clip_path() == ClipPath::None
{ {
return false; return false;
@ -1584,7 +1584,10 @@ impl BoxFragment {
&self, &self,
containing_block_rect: &PhysicalRect<Au>, containing_block_rect: &PhysicalRect<Au>,
) -> Option<ReferenceFrameData> { ) -> Option<ReferenceFrameData> {
if !self.style.has_transform_or_perspective(self.base.flags) { if !self
.style
.has_effective_transform_or_perspective(self.base.flags)
{
return None; return None;
} }

View file

@ -320,7 +320,8 @@ pub(crate) trait ComputedValuesExt {
containing_block_writing_mode: WritingMode, containing_block_writing_mode: WritingMode,
) -> LogicalSides<LengthPercentageOrAuto<'_>>; ) -> LogicalSides<LengthPercentageOrAuto<'_>>;
fn is_transformable(&self, fragment_flags: FragmentFlags) -> bool; fn is_transformable(&self, fragment_flags: FragmentFlags) -> bool;
fn has_transform_or_perspective(&self, fragment_flags: FragmentFlags) -> bool; fn has_transform_or_perspective_style(&self) -> bool;
fn has_effective_transform_or_perspective(&self, fragment_flags: FragmentFlags) -> bool;
fn z_index_applies(&self, fragment_flags: FragmentFlags) -> bool; fn z_index_applies(&self, fragment_flags: FragmentFlags) -> bool;
fn effective_z_index(&self, fragment_flags: FragmentFlags) -> i32; fn effective_z_index(&self, fragment_flags: FragmentFlags) -> i32;
fn effective_overflow(&self, fragment_flags: FragmentFlags) -> AxesOverflow; fn effective_overflow(&self, fragment_flags: FragmentFlags) -> AxesOverflow;
@ -522,15 +523,20 @@ impl ComputedValuesExt for ComputedValues {
!self.is_inline_box(fragment_flags) !self.is_inline_box(fragment_flags)
} }
/// Returns true if this style has a transform, or perspective property set and /// Returns true if this style has a transform or perspective property set.
fn has_transform_or_perspective_style(&self) -> bool {
!self.get_box().transform.0.is_empty() ||
self.get_box().scale != GenericScale::None ||
self.get_box().rotate != GenericRotate::None ||
self.get_box().translate != GenericTranslate::None ||
self.get_box().perspective != Perspective::None
}
/// Returns true if this style has a transform or perspective property set, and
/// it applies to this element. /// it applies to this element.
fn has_transform_or_perspective(&self, fragment_flags: FragmentFlags) -> bool { #[inline]
self.is_transformable(fragment_flags) && fn has_effective_transform_or_perspective(&self, fragment_flags: FragmentFlags) -> bool {
(!self.get_box().transform.0.is_empty() || self.is_transformable(fragment_flags) && self.has_transform_or_perspective_style()
self.get_box().scale != GenericScale::None ||
self.get_box().rotate != GenericRotate::None ||
self.get_box().translate != GenericTranslate::None ||
self.get_box().perspective != Perspective::None)
} }
/// Whether the `z-index` property applies to this fragment. /// Whether the `z-index` property applies to this fragment.
@ -705,7 +711,6 @@ impl ComputedValuesExt for ComputedValues {
if self.is_transformable(fragment_flags) && if self.is_transformable(fragment_flags) &&
(!self.get_box().transform.0.is_empty() || (!self.get_box().transform.0.is_empty() ||
self.get_box().transform_style == ComputedTransformStyle::Preserve3d || self.get_box().transform_style == ComputedTransformStyle::Preserve3d ||
self.get_box().perspective != Perspective::None ||
will_change_bits will_change_bits
.intersects(WillChangeBits::TRANSFORM | WillChangeBits::PERSPECTIVE)) .intersects(WillChangeBits::TRANSFORM | WillChangeBits::PERSPECTIVE))
{ {
@ -795,29 +800,43 @@ impl ComputedValuesExt for ComputedValues {
&self, &self,
fragment_flags: FragmentFlags, fragment_flags: FragmentFlags,
) -> bool { ) -> bool {
if self.has_transform_or_perspective(fragment_flags) {
return true;
}
if !self.get_effects().filter.0.is_empty() {
return true;
}
// See <https://drafts.csswg.org/css-transforms-2/#transform-style-property>.
if self.is_transformable(fragment_flags) &&
self.get_box().transform_style == ComputedTransformStyle::Preserve3d
{
return true;
}
// From <https://www.w3.org/TR/css-will-change/#valdef-will-change-custom-ident>: // From <https://www.w3.org/TR/css-will-change/#valdef-will-change-custom-ident>:
// > If any non-initial value of a property would cause the element to generate a // > If any non-initial value of a property would cause the element to generate a
// > containing block for fixed positioned elements, specifying that property in will-change // > containing block for fixed positioned elements, specifying that property in will-change
// > must cause the element to generate a containing block for fixed positioned elements. // > must cause the element to generate a containing block for fixed positioned elements.
let will_change_bits = self.clone_will_change().bits; let will_change_bits = self.clone_will_change().bits;
if will_change_bits.intersects(WillChangeBits::FIXPOS_CB_NON_SVG) ||
(will_change_bits // From <https://drafts.csswg.org/css-transforms-1/#transform-rendering>:
.intersects(WillChangeBits::TRANSFORM | WillChangeBits::PERSPECTIVE) && // > any value other than `none` for the `transform` property also causes the element
self.is_transformable(fragment_flags)) // > to establish a containing block for all descendants.
//
// From <https://www.w3.org/TR/css-transforms-2/#individual-transforms>
// > all other values […] create a stacking context and containing block for all
// > descendants, per usual for transforms.
//
// From <https://drafts.csswg.org/css-transforms-2/#perspective-property>:
// > The use of this property with any value other than `none` […] establishes a
// > containing block for all descendants, just like the `transform` property does.
//
// From <https://drafts.csswg.org/css-transforms-2/#transform-style-property>:
// > A computed value of `preserve-3d` for `transform-style` on a transformable element
// > establishes both a stacking context and a containing block for all descendants.
if self.is_transformable(fragment_flags) &&
(self.has_transform_or_perspective_style() ||
self.get_box().transform_style == ComputedTransformStyle::Preserve3d ||
will_change_bits
.intersects(WillChangeBits::TRANSFORM | WillChangeBits::PERSPECTIVE))
{
return true;
}
// From <https://www.w3.org/TR/filter-effects-1/#propdef-filter>:
// > A value other than none for the filter property results in the creation of a containing
// > block for absolute and fixed positioned descendants unless the element it applies to is
// > a document root element in the current browsing context.
// FIXME(#35391): Need to check if this is the root element.
if !self.get_effects().filter.0.is_empty() ||
will_change_bits.intersects(WillChangeBits::FIXPOS_CB_NON_SVG)
{ {
return true; return true;
} }