mirror of
https://github.com/servo/servo.git
synced 2025-07-28 01:30:32 +01:00
Fix stretch
sizes on replaced abspos (#34430)
We were sizing absolutely positioned replaced elements within their actual containing block instead of the inset-modified containing block. Then the `stretch` keyword would result in a wrong size. Signed-off-by: Oriol Brufau <obrufau@igalia.com>
This commit is contained in:
parent
54761b4f32
commit
5201c84fb4
5 changed files with 189 additions and 120 deletions
|
@ -458,29 +458,9 @@ impl HoistedAbsolutelyPositionedBox {
|
||||||
let style = context.style().clone();
|
let style = context.style().clone();
|
||||||
let containing_block = &containing_block.into();
|
let containing_block = &containing_block.into();
|
||||||
let pbm = style.padding_border_margin(containing_block);
|
let pbm = style.padding_border_margin(containing_block);
|
||||||
|
let computed_size = style.content_box_size(containing_block, &pbm);
|
||||||
let (computed_size, computed_min_size, computed_max_size) = match context {
|
let computed_min_size = style.content_min_box_size(containing_block, &pbm);
|
||||||
IndependentFormattingContext::Replaced(replaced) => {
|
let computed_max_size = style.content_max_box_size(containing_block, &pbm);
|
||||||
// https://drafts.csswg.org/css2/visudet.html#abs-replaced-width
|
|
||||||
// https://drafts.csswg.org/css2/visudet.html#abs-replaced-height
|
|
||||||
let content_box_sizes_and_pbm =
|
|
||||||
style.content_box_sizes_and_padding_border_margin(&containing_block.into());
|
|
||||||
let used_size = replaced
|
|
||||||
.contents
|
|
||||||
.used_size_as_if_inline_element(
|
|
||||||
containing_block,
|
|
||||||
&style,
|
|
||||||
&content_box_sizes_and_pbm,
|
|
||||||
)
|
|
||||||
.map(|size| Size::Numeric(*size));
|
|
||||||
(used_size, Default::default(), Default::default())
|
|
||||||
},
|
|
||||||
IndependentFormattingContext::NonReplaced(_) => (
|
|
||||||
style.content_box_size(containing_block, &pbm),
|
|
||||||
style.content_min_box_size(containing_block, &pbm),
|
|
||||||
style.content_max_box_size(containing_block, &pbm),
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
let shared_fragment = self.fragment.borrow();
|
let shared_fragment = self.fragment.borrow();
|
||||||
let static_position_rect = shared_fragment
|
let static_position_rect = shared_fragment
|
||||||
|
@ -541,6 +521,28 @@ impl HoistedAbsolutelyPositionedBox {
|
||||||
flip_anchor: false,
|
flip_anchor: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if let IndependentFormattingContext::Replaced(replaced) = context {
|
||||||
|
// https://drafts.csswg.org/css2/visudet.html#abs-replaced-width
|
||||||
|
// https://drafts.csswg.org/css2/visudet.html#abs-replaced-height
|
||||||
|
let inset_sums = LogicalVec2 {
|
||||||
|
inline: inline_axis_solver.inset_sum(),
|
||||||
|
block: block_axis_solver.inset_sum(),
|
||||||
|
};
|
||||||
|
let used_size = replaced
|
||||||
|
.contents
|
||||||
|
.used_size_as_if_inline_element_from_content_box_sizes(
|
||||||
|
containing_block,
|
||||||
|
&style,
|
||||||
|
replaced.preferred_aspect_ratio(&pbm.padding_border_sums),
|
||||||
|
computed_size,
|
||||||
|
computed_min_size,
|
||||||
|
computed_max_size,
|
||||||
|
pbm.padding_border_sums + pbm.margin.auto_is(Au::zero).sum() + inset_sums,
|
||||||
|
);
|
||||||
|
inline_axis_solver.override_size(used_size.inline);
|
||||||
|
block_axis_solver.override_size(used_size.block);
|
||||||
|
}
|
||||||
|
|
||||||
// The block axis can depend on layout results, so we only solve it tentatively,
|
// The block axis can depend on layout results, so we only solve it tentatively,
|
||||||
// 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();
|
let mut block_axis = block_axis_solver.solve_tentatively();
|
||||||
|
@ -563,7 +565,10 @@ 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.map(|size| size.to_numeric().unwrap());
|
content_size = LogicalVec2 {
|
||||||
|
inline: inline_axis.size.to_definite().unwrap(),
|
||||||
|
block: block_axis.size.to_definite().unwrap(),
|
||||||
|
};
|
||||||
fragments = replaced.contents.make_fragments(
|
fragments = replaced.contents.make_fragments(
|
||||||
&style,
|
&style,
|
||||||
content_size.to_physical_size(containing_block_writing_mode),
|
content_size.to_physical_size(containing_block_writing_mode),
|
||||||
|
@ -592,18 +597,19 @@ impl HoistedAbsolutelyPositionedBox {
|
||||||
containing_block,
|
containing_block,
|
||||||
);
|
);
|
||||||
|
|
||||||
let (block_size, inline_size) = match independent_layout
|
let (block_size, inline_size) =
|
||||||
.content_inline_size_for_table
|
match independent_layout.content_inline_size_for_table {
|
||||||
{
|
|
||||||
Some(table_inline_size) => {
|
Some(table_inline_size) => {
|
||||||
// Tables can override their sizes regardless of the sizing properties,
|
// Tables can override their sizes regardless of the sizing properties,
|
||||||
// so we may need to solve again to update margins.
|
// so we may need to solve again to update margins.
|
||||||
if inline_size != table_inline_size {
|
if inline_size != table_inline_size {
|
||||||
inline_axis = inline_axis_solver.solve_with_size(table_inline_size);
|
inline_axis_solver.override_size(table_inline_size);
|
||||||
|
inline_axis = inline_axis_solver.solve_tentatively();
|
||||||
}
|
}
|
||||||
let table_block_size = independent_layout.content_block_size;
|
let table_block_size = independent_layout.content_block_size;
|
||||||
if block_axis.size != SizeConstraint::Definite(table_block_size) {
|
if block_axis.size != SizeConstraint::Definite(table_block_size) {
|
||||||
block_axis = block_axis_solver.solve_with_size(table_block_size);
|
block_axis_solver.override_size(table_block_size);
|
||||||
|
block_axis = block_axis_solver.solve_tentatively();
|
||||||
}
|
}
|
||||||
(table_block_size, table_inline_size)
|
(table_block_size, table_inline_size)
|
||||||
},
|
},
|
||||||
|
@ -718,6 +724,10 @@ impl AbsoluteBoxOffsets<'_> {
|
||||||
pub(crate) fn either_specified(&self) -> bool {
|
pub(crate) fn either_specified(&self) -> bool {
|
||||||
!self.start.is_auto() || !self.end.is_auto()
|
!self.start.is_auto() || !self.end.is_auto()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn either_auto(&self) -> bool {
|
||||||
|
self.start.is_auto() || self.end.is_auto()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct AxisResult {
|
struct AxisResult {
|
||||||
|
@ -742,6 +752,32 @@ struct AbsoluteAxisSolver<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> AbsoluteAxisSolver<'a> {
|
impl<'a> AbsoluteAxisSolver<'a> {
|
||||||
|
/// Returns the amount that we need to subtract from the containing block size in order to
|
||||||
|
/// obtain the inset-modified containing block that we will use for sizing purposes.
|
||||||
|
/// (Note that for alignment purposes, we may re-resolve auto insets to a different value.)
|
||||||
|
/// <https://drafts.csswg.org/css-position/#resolving-insets>
|
||||||
|
fn inset_sum(&self) -> Au {
|
||||||
|
match (
|
||||||
|
self.box_offsets.start.non_auto(),
|
||||||
|
self.box_offsets.end.non_auto(),
|
||||||
|
) {
|
||||||
|
(None, None) => {
|
||||||
|
if self.flip_anchor {
|
||||||
|
self.containing_size -
|
||||||
|
self.static_position_rect_axis.origin -
|
||||||
|
self.static_position_rect_axis.length
|
||||||
|
} else {
|
||||||
|
self.static_position_rect_axis.origin
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(Some(start), None) => start.to_used_value(self.containing_size),
|
||||||
|
(None, Some(end)) => end.to_used_value(self.containing_size),
|
||||||
|
(Some(start), Some(end)) => {
|
||||||
|
start.to_used_value(self.containing_size) + end.to_used_value(self.containing_size)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// This unifies some of the parts in common in:
|
/// This unifies some of the parts in common in:
|
||||||
///
|
///
|
||||||
/// * <https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-width>
|
/// * <https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-width>
|
||||||
|
@ -789,35 +825,22 @@ impl<'a> AbsoluteAxisSolver<'a> {
|
||||||
SizeConstraint::new(preferred_size, min_size, max_size)
|
SizeConstraint::new(preferred_size, min_size, max_size)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let solve_for_inset = |inset| {
|
if self.box_offsets.either_auto() {
|
||||||
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 stretch_size =
|
let stretch_size = self.containing_size -
|
||||||
self.containing_size - inset - self.padding_border_sum - margin_start - margin_end;
|
self.inset_sum() -
|
||||||
|
self.padding_border_sum -
|
||||||
|
margin_start -
|
||||||
|
margin_end;
|
||||||
let size = solve_size(Size::FitContent, stretch_size);
|
let size = solve_size(Size::FitContent, stretch_size);
|
||||||
AxisResult {
|
AxisResult {
|
||||||
size,
|
size,
|
||||||
margin_start,
|
margin_start,
|
||||||
margin_end,
|
margin_end,
|
||||||
}
|
}
|
||||||
};
|
|
||||||
match (
|
|
||||||
self.box_offsets.start.non_auto(),
|
|
||||||
self.box_offsets.end.non_auto(),
|
|
||||||
) {
|
|
||||||
(None, None) => solve_for_inset(if self.flip_anchor {
|
|
||||||
self.containing_size -
|
|
||||||
self.static_position_rect_axis.origin -
|
|
||||||
self.static_position_rect_axis.length
|
|
||||||
} else {
|
} else {
|
||||||
self.static_position_rect_axis.origin
|
let mut free_space = self.containing_size - self.inset_sum() - self.padding_border_sum;
|
||||||
}),
|
|
||||||
(Some(start), None) => solve_for_inset(start.to_used_value(self.containing_size)),
|
|
||||||
(None, Some(end)) => solve_for_inset(end.to_used_value(self.containing_size)),
|
|
||||||
(Some(start), Some(end)) => {
|
|
||||||
let start = start.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 stretch_size = free_space -
|
let stretch_size = free_space -
|
||||||
self.computed_margin_start.auto_is(Au::zero) -
|
self.computed_margin_start.auto_is(Au::zero) -
|
||||||
self.computed_margin_end.auto_is(Au::zero);
|
self.computed_margin_end.auto_is(Au::zero);
|
||||||
|
@ -841,9 +864,7 @@ impl<'a> AbsoluteAxisSolver<'a> {
|
||||||
(margin_start, free_space - margin_start)
|
(margin_start, free_space - margin_start)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
(AuOrAuto::Auto, AuOrAuto::LengthPercentage(end)) => {
|
(AuOrAuto::Auto, AuOrAuto::LengthPercentage(end)) => (free_space - end, end),
|
||||||
(free_space - end, end)
|
|
||||||
},
|
|
||||||
(AuOrAuto::LengthPercentage(start), AuOrAuto::Auto) => {
|
(AuOrAuto::LengthPercentage(start), AuOrAuto::Auto) => {
|
||||||
(start, free_space - start)
|
(start, free_space - start)
|
||||||
},
|
},
|
||||||
|
@ -856,7 +877,6 @@ impl<'a> AbsoluteAxisSolver<'a> {
|
||||||
margin_start,
|
margin_start,
|
||||||
margin_end,
|
margin_end,
|
||||||
}
|
}
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -864,20 +884,10 @@ impl<'a> AbsoluteAxisSolver<'a> {
|
||||||
self.solve(None::<fn() -> ContentSizes>)
|
self.solve(None::<fn() -> ContentSizes>)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn solve_with_size(&mut self, size: Au) -> AxisResult {
|
fn override_size(&mut self, size: Au) {
|
||||||
// Override sizes
|
self.computed_size = Size::Numeric(size);
|
||||||
let old_size = mem::replace(&mut self.computed_size, Size::Numeric(size));
|
self.computed_min_size = Size::default();
|
||||||
let old_min_size = mem::take(&mut self.computed_min_size);
|
self.computed_max_size = Size::default();
|
||||||
let old_max_size = mem::take(&mut self.computed_max_size);
|
|
||||||
|
|
||||||
let result = self.solve_tentatively();
|
|
||||||
|
|
||||||
// Restore original sizes
|
|
||||||
self.computed_size = old_size;
|
|
||||||
self.computed_min_size = old_min_size;
|
|
||||||
self.computed_max_size = old_max_size;
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn origin_for_margin_box(
|
fn origin_for_margin_box(
|
||||||
|
|
26
tests/wpt/meta/MANIFEST.json
vendored
26
tests/wpt/meta/MANIFEST.json
vendored
|
@ -242884,6 +242884,32 @@
|
||||||
],
|
],
|
||||||
{}
|
{}
|
||||||
]
|
]
|
||||||
|
],
|
||||||
|
"positioned-replaced-2.html": [
|
||||||
|
"9a991d236ef2ddd7b8df1d4bff43578e5e741a68",
|
||||||
|
[
|
||||||
|
null,
|
||||||
|
[
|
||||||
|
[
|
||||||
|
"/css/reference/ref-filled-green-100px-square-only.html",
|
||||||
|
"=="
|
||||||
|
]
|
||||||
|
],
|
||||||
|
{}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"positioned-replaced-3.html": [
|
||||||
|
"c11fcfd9521fc212929524e63f02a4ea218052c2",
|
||||||
|
[
|
||||||
|
null,
|
||||||
|
[
|
||||||
|
[
|
||||||
|
"/css/reference/ref-filled-green-100px-square-only.html",
|
||||||
|
"=="
|
||||||
|
]
|
||||||
|
],
|
||||||
|
{}
|
||||||
|
]
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"svg-intrinsic-size-005.html": [
|
"svg-intrinsic-size-005.html": [
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
[positioned-replaced-1.html]
|
|
||||||
expected: FAIL
|
|
18
tests/wpt/tests/css/css-sizing/stretch/positioned-replaced-2.html
vendored
Normal file
18
tests/wpt/tests/css/css-sizing/stretch/positioned-replaced-2.html
vendored
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com">
|
||||||
|
<link rel="match" href="../../reference/ref-filled-green-100px-square-only.html">
|
||||||
|
<link rel="help" href="https://github.com/servo/servo/pull/34430">
|
||||||
|
<p>Test passes if there is a filled green square.</p>
|
||||||
|
<style>
|
||||||
|
canvas {
|
||||||
|
position: absolute;
|
||||||
|
background: green;
|
||||||
|
width: stretch;
|
||||||
|
height: stretch;
|
||||||
|
top: 50px;
|
||||||
|
left: 50px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<div style="display: flow-root; position: relative; width: 150px; height: 150px; margin-top: -50px; margin-left: -50px;">
|
||||||
|
<canvas width="50" height="25"></canvas>
|
||||||
|
</div>
|
17
tests/wpt/tests/css/css-sizing/stretch/positioned-replaced-3.html
vendored
Normal file
17
tests/wpt/tests/css/css-sizing/stretch/positioned-replaced-3.html
vendored
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com">
|
||||||
|
<link rel="match" href="../../reference/ref-filled-green-100px-square-only.html">
|
||||||
|
<link rel="help" href="https://github.com/servo/servo/pull/34430">
|
||||||
|
<p>Test passes if there is a filled green square.</p>
|
||||||
|
<style>
|
||||||
|
canvas {
|
||||||
|
position: absolute;
|
||||||
|
background: green;
|
||||||
|
width: stretch;
|
||||||
|
height: stretch;
|
||||||
|
inset: 50px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<div style="display: flow-root; position: relative; width: 200px; height: 200px; margin-top: -50px; margin-left: -50px;">
|
||||||
|
<canvas width="50" height="25"></canvas>
|
||||||
|
</div>
|
Loading…
Add table
Add a link
Reference in a new issue