Implement keyword sizes on absolute/fixed positioned elements (#33950)

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
This commit is contained in:
Oriol Brufau 2024-10-22 20:50:12 +02:00 committed by GitHub
parent c5ee573c6d
commit 1c26c0335e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
41 changed files with 72 additions and 129 deletions

View file

@ -624,7 +624,7 @@ impl ToLogicalWithContainingBlock<LogicalRect<Au>> for PhysicalRect<Au> {
/// The possible values accepted by the sizing properties. /// The possible values accepted by the sizing properties.
/// <https://drafts.csswg.org/css-sizing/#sizing-properties> /// <https://drafts.csswg.org/css-sizing/#sizing-properties>
#[derive(Clone)] #[derive(Clone, PartialEq)]
pub(crate) enum Size<T> { pub(crate) enum Size<T> {
/// Represents an `auto` value for the preferred and minimum size properties, /// Represents an `auto` value for the preferred and minimum size properties,
/// or `none` for the maximum size properties. /// or `none` for the maximum size properties.
@ -644,6 +644,8 @@ pub(crate) enum Size<T> {
Numeric(T), Numeric(T),
} }
impl<T: Copy> Copy for Size<T> {}
impl<T> Default for Size<T> { impl<T> Default for Size<T> {
#[inline] #[inline]
fn default() -> Self { fn default() -> Self {

View file

@ -2,6 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* 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 std::cell::LazyCell;
use std::mem; use std::mem;
use app_units::Au; use app_units::Au;
@ -25,7 +26,7 @@ use crate::fragment_tree::{
}; };
use crate::geom::{ use crate::geom::{
AuOrAuto, LengthPercentageOrAuto, LogicalRect, LogicalSides, LogicalVec2, PhysicalPoint, AuOrAuto, LengthPercentageOrAuto, LogicalRect, LogicalSides, LogicalVec2, PhysicalPoint,
PhysicalRect, PhysicalVec, ToLogical, ToLogicalWithContainingBlock, PhysicalRect, PhysicalVec, Size, ToLogical, ToLogicalWithContainingBlock,
}; };
use crate::sizing::ContentSizes; use crate::sizing::ContentSizes;
use crate::style_ext::{Clamp, ComputedValuesExt, DisplayInside}; use crate::style_ext::{Clamp, ComputedValuesExt, DisplayInside};
@ -461,17 +462,13 @@ impl HoistedAbsolutelyPositionedBox {
let used_size = replaced let used_size = replaced
.contents .contents
.used_size_as_if_inline_element(containing_block, &style, &pbm) .used_size_as_if_inline_element(containing_block, &style, &pbm)
.map(|size| AuOrAuto::LengthPercentage(*size)); .map(|size| Size::Numeric(*size));
(used_size, Default::default(), Default::default()) (used_size, Default::default(), Default::default())
}, },
IndependentFormattingContext::NonReplaced(_) => ( IndependentFormattingContext::NonReplaced(_) => (
style.content_box_size_deprecated(containing_block, &pbm), style.content_box_size(containing_block, &pbm),
style style.content_min_box_size(containing_block, &pbm),
.content_min_box_size_deprecated(containing_block, &pbm) style.content_max_box_size(containing_block, &pbm),
.map(|value| value.map(Au::from).auto_is(Au::zero)),
style
.content_max_box_size_deprecated(containing_block, &pbm)
.map(|value| value.map(Au::from)),
), ),
}; };
@ -536,14 +533,15 @@ impl HoistedAbsolutelyPositionedBox {
flip_anchor: false, flip_anchor: false,
}; };
// We can solve the inline axis, but the block one can depend on layout results, // The block axis can depend on layout results, so we only solve it tentatively,
// so we may have to resolve it properly later on. // we may have to resolve it properly later on.
let mut block_axis = block_axis_solver.solve_tentatively();
// The inline axis can be fully resolved, computing intrinsic sizes using the
// tentative block size.
let mut inline_axis = inline_axis_solver.solve(Some(|| { let mut inline_axis = inline_axis_solver.solve(Some(|| {
let block_size = computed_size.block.map(|v| {
v.clamp_between_extremums(computed_min_size.block, computed_max_size.block)
});
let containing_block_for_children = let containing_block_for_children =
IndefiniteContainingBlock::new_for_style_and_block_size(&style, block_size); IndefiniteContainingBlock::new_for_style_and_block_size(&style, block_axis.size);
context context
.inline_content_sizes( .inline_content_sizes(
layout_context, layout_context,
@ -552,7 +550,6 @@ impl HoistedAbsolutelyPositionedBox {
) )
.sizes .sizes
})); }));
let mut block_axis = block_axis_solver.solve_tentatively();
let mut positioning_context = PositioningContext::new_for_style(&style).unwrap(); let mut positioning_context = PositioningContext::new_for_style(&style).unwrap();
let mut new_fragment = { let mut new_fragment = {
@ -562,7 +559,7 @@ impl HoistedAbsolutelyPositionedBox {
IndependentFormattingContext::Replaced(replaced) => { IndependentFormattingContext::Replaced(replaced) => {
// https://drafts.csswg.org/css2/visudet.html#abs-replaced-width // https://drafts.csswg.org/css2/visudet.html#abs-replaced-width
// https://drafts.csswg.org/css2/visudet.html#abs-replaced-height // https://drafts.csswg.org/css2/visudet.html#abs-replaced-height
content_size = computed_size.auto_is(|| unreachable!()); content_size = computed_size.map(|size| size.to_numeric().unwrap());
fragments = replaced.contents.make_fragments( fragments = replaced.contents.make_fragments(
&style, &style,
containing_block, containing_block,
@ -752,9 +749,9 @@ struct AbsoluteAxisSolver<'a> {
padding_border_sum: Au, padding_border_sum: Au,
computed_margin_start: AuOrAuto, computed_margin_start: AuOrAuto,
computed_margin_end: AuOrAuto, computed_margin_end: AuOrAuto,
computed_size: AuOrAuto, computed_size: Size<Au>,
computed_min_size: Au, computed_min_size: Size<Au>,
computed_max_size: Option<Au>, computed_max_size: Size<Au>,
avoid_negative_margin_start: bool, avoid_negative_margin_start: bool,
box_offsets: AbsoluteBoxOffsets<'a>, box_offsets: AbsoluteBoxOffsets<'a>,
static_position_rect_axis: RectAxis, static_position_rect_axis: RectAxis,
@ -775,24 +772,54 @@ impl<'a> AbsoluteAxisSolver<'a> {
/// ///
/// In the replaced case, `size` is never `Auto`. /// In the replaced case, `size` is never `Auto`.
fn solve(&self, get_content_size: Option<impl FnOnce() -> ContentSizes>) -> AxisResult { fn solve(&self, get_content_size: Option<impl FnOnce() -> ContentSizes>) -> AxisResult {
let solve_for_anchor = |anchor: Anchor| { let mut get_content_size = get_content_size.map(|get_content_size| {
// The provided `get_content_size` is a FnOnce but we may need its result multiple times.
// A LazyCell will only invoke it once if needed, and then reuse the result.
let content_size = LazyCell::new(get_content_size);
move || *content_size
});
let mut solve_size = |initial_behavior, stretch_size: Au| {
let initial_is_stretch = initial_behavior == Size::Stretch;
let stretch_size = stretch_size.max(Au::zero());
get_content_size
.as_mut()
.map(|mut get_content_size| {
let min_size = self
.computed_min_size
.resolve_non_initial(stretch_size, &mut get_content_size)
.unwrap_or_default();
let max_size = self
.computed_max_size
.resolve_non_initial(stretch_size, &mut get_content_size);
self.computed_size
.resolve(initial_behavior, stretch_size, &mut get_content_size)
.clamp_between_extremums(min_size, max_size)
})
.or_else(|| {
self.computed_size
.maybe_resolve_extrinsic(Some(stretch_size))
.or(initial_is_stretch.then_some(stretch_size))
.map(|size| {
let min_size = self
.computed_min_size
.maybe_resolve_extrinsic(Some(stretch_size))
.unwrap_or_default();
let max_size = self
.computed_max_size
.maybe_resolve_extrinsic(Some(stretch_size));
size.clamp_between_extremums(min_size, max_size)
})
})
};
let mut solve_for_anchor = |anchor: Anchor| {
let margin_start = self.computed_margin_start.auto_is(Au::zero); let margin_start = self.computed_margin_start.auto_is(Au::zero);
let margin_end = self.computed_margin_end.auto_is(Au::zero); let margin_end = self.computed_margin_end.auto_is(Au::zero);
let size = self let stretch_size = self.containing_size -
.computed_size anchor.inset() -
.non_auto() self.padding_border_sum -
.or_else(|| { margin_start -
let content_size = get_content_size?(); margin_end;
let available_size = self.containing_size - let size = solve_size(Size::FitContent, stretch_size)
anchor.inset() -
self.padding_border_sum -
margin_start -
margin_end;
Some(content_size.shrink_to_fit(available_size))
})
.map(|size| {
size.clamp_between_extremums(self.computed_min_size, self.computed_max_size)
})
.map_or(AuOrAuto::Auto, AuOrAuto::LengthPercentage); .map_or(AuOrAuto::Auto, AuOrAuto::LengthPercentage);
AxisResult { AxisResult {
anchor, anchor,
@ -820,14 +847,10 @@ impl<'a> AbsoluteAxisSolver<'a> {
let start = start.to_used_value(self.containing_size); let start = start.to_used_value(self.containing_size);
let end = end.to_used_value(self.containing_size); let end = end.to_used_value(self.containing_size);
let mut free_space = self.containing_size - start - end - self.padding_border_sum; let mut free_space = self.containing_size - start - end - self.padding_border_sum;
let used_size = self let stretch_size = free_space -
.computed_size self.computed_margin_start.auto_is(Au::zero) -
.auto_is(|| { self.computed_margin_end.auto_is(Au::zero);
free_space - let used_size = solve_size(Size::Stretch, stretch_size).unwrap();
self.computed_margin_start.auto_is(Au::zero) -
self.computed_margin_end.auto_is(Au::zero)
})
.clamp_between_extremums(self.computed_min_size, self.computed_max_size);
free_space -= used_size; free_space -= used_size;
let (margin_start, margin_end) = let (margin_start, margin_end) =
match (self.computed_margin_start, self.computed_margin_end) { match (self.computed_margin_start, self.computed_margin_end) {
@ -865,9 +888,9 @@ impl<'a> AbsoluteAxisSolver<'a> {
fn solve_with_size(&mut self, size: Au) -> AxisResult { fn solve_with_size(&mut self, size: Au) -> AxisResult {
// Override sizes // Override sizes
let old_size = mem::replace(&mut self.computed_size, AuOrAuto::LengthPercentage(size)); let old_size = mem::replace(&mut self.computed_size, Size::Numeric(size));
let old_min_size = mem::take(&mut self.computed_min_size); let old_min_size = mem::take(&mut self.computed_min_size);
let old_max_size = self.computed_max_size.take(); let old_max_size = mem::take(&mut self.computed_max_size);
let result = self.solve_tentatively(); let result = self.solve_tentatively();

View file

@ -1,2 +0,0 @@
[position-absolute-fit-content.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[block-size-with-min-or-max-content-6.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[block-size-with-min-or-max-content-7.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[div-fit-content-auto-margin-bottom.tentative.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[div-fit-content-auto-margin-top.tentative.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[div-fit-content-auto-margin.tentative.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[div-fit-content-block-size.tentative.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[div-fit-content-orthogonal-auto-margin-left.tentative.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[div-fit-content-orthogonal-auto-margin-right.tentative.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[div-fit-content-orthogonal-auto-margin.tentative.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[div-fit-content-orthogonal-block-size.tentative.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[div-max-content-auto-margin-bottom.tentative.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[div-max-content-auto-margin-top.tentative.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[div-max-content-auto-margin.tentative.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[div-max-content-block-size.tentative.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[div-max-content-orthogonal-auto-margin-left.tentative.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[div-max-content-orthogonal-auto-margin-right.tentative.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[div-max-content-orthogonal-auto-margin.tentative.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[div-max-content-orthogonal-block-size.tentative.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[div-min-content-auto-margin-bottom.tentative.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[div-min-content-auto-margin-top.tentative.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[div-min-content-auto-margin.tentative.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[div-min-content-block-size.tentative.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[div-min-content-orthogonal-auto-margin-left.tentative.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[div-min-content-orthogonal-auto-margin-right.tentative.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[div-min-content-orthogonal-auto-margin.tentative.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[div-min-content-orthogonal-block-size.tentative.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[div-orthogonal-left-and-non-auto-margin.tentative.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[div-top-and-non-auto-margin.tentative.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[fit-content-block-size-abspos.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[fit-content-block-size-fixedpos.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[intrinsic-percent-replaced-005.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[intrinsic-percent-replaced-007.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[intrinsic-percent-replaced-dynamic-005.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[intrinsic-percent-replaced-dynamic-007.html]
expected: FAIL

View file

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

View file

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

View file

@ -1,2 +0,0 @@
[positioned-non-replaced-1.html]
expected: FAIL

View file

@ -1,12 +1,6 @@
[dialog.html] [dialog.html]
[Open dialog in width: 540px iframe]
expected: FAIL
[Modal dialog in width: 540px iframe] [Modal dialog in width: 540px iframe]
expected: FAIL expected: FAIL
[Open dialog in width: 538px iframe]
expected: FAIL
[Modal dialog in width: 538px iframe] [Modal dialog in width: 538px iframe]
expected: FAIL expected: FAIL