mirror of
https://github.com/servo/servo.git
synced 2025-07-24 15:50:21 +01:00
Complete implementation of keyword sizes for block layout (#34641)
Adds support for min-content, max-content, fit-content and stretch, for the case that was missing from #34568: block-level elements that establish an independent formatting context, when there are floats. Signed-off-by: Oriol Brufau <obrufau@igalia.com>
This commit is contained in:
parent
53740fdd16
commit
3d816d6d24
4 changed files with 165 additions and 78 deletions
|
@ -41,10 +41,7 @@ use crate::layout_box_base::LayoutBoxBase;
|
||||||
use crate::positioned::{AbsolutelyPositionedBox, PositioningContext, PositioningContextLength};
|
use crate::positioned::{AbsolutelyPositionedBox, PositioningContext, PositioningContextLength};
|
||||||
use crate::replaced::ReplacedContents;
|
use crate::replaced::ReplacedContents;
|
||||||
use crate::sizing::{self, ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult};
|
use crate::sizing::{self, ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult};
|
||||||
use crate::style_ext::{
|
use crate::style_ext::{Clamp, ComputedValuesExt, ContentBoxSizesAndPBM, PaddingBorderMargin};
|
||||||
Clamp, ComputedValuesExt, ContentBoxSizesAndPBM, ContentBoxSizesAndPBMDeprecated,
|
|
||||||
PaddingBorderMargin,
|
|
||||||
};
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ConstraintSpace, ContainingBlock, ContainingBlockSize, IndefiniteContainingBlock,
|
ConstraintSpace, ContainingBlock, ContainingBlockSize, IndefiniteContainingBlock,
|
||||||
SizeConstraint,
|
SizeConstraint,
|
||||||
|
@ -1143,23 +1140,15 @@ impl IndependentNonReplacedContents {
|
||||||
containing_block: &ContainingBlock<'_>,
|
containing_block: &ContainingBlock<'_>,
|
||||||
sequential_layout_state: &mut SequentialLayoutState,
|
sequential_layout_state: &mut SequentialLayoutState,
|
||||||
) -> BoxFragment {
|
) -> BoxFragment {
|
||||||
|
let style = &base.style;
|
||||||
let containing_block_writing_mode = containing_block.style.writing_mode;
|
let containing_block_writing_mode = containing_block.style.writing_mode;
|
||||||
let ContentBoxSizesAndPBMDeprecated {
|
let ContentBoxSizesAndPBM {
|
||||||
content_box_size,
|
content_box_size,
|
||||||
content_min_box_size,
|
content_min_box_size,
|
||||||
content_max_box_size,
|
content_max_box_size,
|
||||||
pbm,
|
pbm,
|
||||||
depends_on_block_constraints,
|
depends_on_block_constraints,
|
||||||
} = base
|
} = style.content_box_sizes_and_padding_border_margin(&containing_block.into());
|
||||||
.style
|
|
||||||
.content_box_sizes_and_padding_border_margin(&containing_block.into())
|
|
||||||
.into();
|
|
||||||
let content_min_box_size = content_min_box_size.auto_is(Au::zero);
|
|
||||||
|
|
||||||
let block_size = content_box_size.block.map(|block_size| {
|
|
||||||
block_size
|
|
||||||
.clamp_between_extremums(content_min_box_size.block, content_max_box_size.block)
|
|
||||||
});
|
|
||||||
|
|
||||||
let (margin_block_start, margin_block_end) =
|
let (margin_block_start, margin_block_end) =
|
||||||
solve_block_margins_for_in_flow_block_level(&pbm);
|
solve_block_margins_for_in_flow_block_level(&pbm);
|
||||||
|
@ -1178,7 +1167,6 @@ impl IndependentNonReplacedContents {
|
||||||
let mut content_size;
|
let mut content_size;
|
||||||
let mut layout;
|
let mut layout;
|
||||||
let mut placement_rect;
|
let mut placement_rect;
|
||||||
let style = &base.style;
|
|
||||||
|
|
||||||
// First compute the clear position required by the 'clear' property.
|
// First compute the clear position required by the 'clear' property.
|
||||||
// The code below may then add extra clearance when the element can't fit
|
// The code below may then add extra clearance when the element can't fit
|
||||||
|
@ -1191,16 +1179,96 @@ impl IndependentNonReplacedContents {
|
||||||
sequential_layout_state.position_without_clearance(&collapsed_margin_block_start)
|
sequential_layout_state.position_without_clearance(&collapsed_margin_block_start)
|
||||||
});
|
});
|
||||||
|
|
||||||
if let AuOrAuto::LengthPercentage(ref inline_size) = content_box_size.inline {
|
// Then compute a tentative block size, only taking extrinsic values into account.
|
||||||
let inline_size = inline_size
|
let margin = pbm.margin.auto_is(Au::zero);
|
||||||
.clamp_between_extremums(content_min_box_size.inline, content_max_box_size.inline);
|
let pbm_sums = pbm.padding + pbm.border + margin;
|
||||||
|
let available_block_size = containing_block
|
||||||
|
.size
|
||||||
|
.block
|
||||||
|
.non_auto()
|
||||||
|
.map(|block_size| Au::zero().max(block_size - pbm_sums.block_sum()));
|
||||||
|
let preferred_block_size = content_box_size
|
||||||
|
.block
|
||||||
|
.maybe_resolve_extrinsic(available_block_size);
|
||||||
|
let min_block_size = content_min_box_size
|
||||||
|
.block
|
||||||
|
.maybe_resolve_extrinsic(available_block_size)
|
||||||
|
.unwrap_or_default();
|
||||||
|
let max_block_size = content_max_box_size
|
||||||
|
.block
|
||||||
|
.maybe_resolve_extrinsic(available_block_size);
|
||||||
|
let tentative_block_size =
|
||||||
|
SizeConstraint::new(preferred_block_size, min_block_size, max_block_size);
|
||||||
|
|
||||||
|
// With the tentative block size we can compute the inline min/max-content sizes.
|
||||||
|
let inline_content_sizes = LazyCell::new(|| {
|
||||||
|
let constraint_space = ConstraintSpace::new(
|
||||||
|
tentative_block_size,
|
||||||
|
style.writing_mode,
|
||||||
|
self.preferred_aspect_ratio(),
|
||||||
|
);
|
||||||
|
base.inline_content_sizes(layout_context, &constraint_space, self)
|
||||||
|
.sizes
|
||||||
|
});
|
||||||
|
|
||||||
|
// The final inline size can depend on the available space, which depends on where
|
||||||
|
// we are placing the box, since floats reduce the available space.
|
||||||
|
// TODO: this logic could be refined further, since even if some of the 3 sizes
|
||||||
|
// depends on the available space, the resulting size might not. For example,
|
||||||
|
// `min-width: 200px; width: 100px; max-width: stretch`.
|
||||||
|
let inline_size_depends_on_available_space = matches!(
|
||||||
|
content_box_size.inline,
|
||||||
|
Size::Initial | Size::Stretch | Size::FitContent
|
||||||
|
) || matches!(
|
||||||
|
content_min_box_size.inline,
|
||||||
|
Size::Stretch | Size::FitContent
|
||||||
|
) || matches!(
|
||||||
|
content_max_box_size.inline,
|
||||||
|
Size::Stretch | Size::FitContent
|
||||||
|
);
|
||||||
|
let compute_inline_size = |stretch_size| {
|
||||||
|
let preferred_inline_size =
|
||||||
|
content_box_size
|
||||||
|
.inline
|
||||||
|
.resolve(Size::Stretch, stretch_size, &inline_content_sizes);
|
||||||
|
let min_inline_size = content_min_box_size
|
||||||
|
.inline
|
||||||
|
.resolve_non_initial(stretch_size, &inline_content_sizes)
|
||||||
|
.unwrap_or_default();
|
||||||
|
let max_inline_size = content_max_box_size
|
||||||
|
.inline
|
||||||
|
.resolve_non_initial(stretch_size, &inline_content_sizes);
|
||||||
|
preferred_inline_size.clamp_between_extremums(min_inline_size, max_inline_size)
|
||||||
|
};
|
||||||
|
|
||||||
|
let compute_block_size = |layout: &IndependentLayout| {
|
||||||
|
let stretch_size = available_block_size.unwrap_or(layout.content_block_size);
|
||||||
|
let block_contents_size = LazyCell::new(|| layout.content_block_size.into());
|
||||||
|
let min_block_size = content_min_box_size
|
||||||
|
.block
|
||||||
|
.resolve_non_initial(stretch_size, &block_contents_size)
|
||||||
|
.unwrap_or_default();
|
||||||
|
let max_block_size = content_max_box_size
|
||||||
|
.block
|
||||||
|
.resolve_non_initial(stretch_size, &block_contents_size);
|
||||||
|
tentative_block_size
|
||||||
|
.to_definite()
|
||||||
|
.unwrap_or(layout.content_block_size)
|
||||||
|
.clamp_between_extremums(min_block_size, max_block_size)
|
||||||
|
};
|
||||||
|
|
||||||
|
if !inline_size_depends_on_available_space {
|
||||||
|
// If the inline size doesn't depend on the available inline space, we can just
|
||||||
|
// compute it with an available inline space of zero. Then, after layout we can
|
||||||
|
// compute the block size, and finally place among floats.
|
||||||
|
let inline_size = compute_inline_size(Au::zero());
|
||||||
layout = self.layout(
|
layout = self.layout(
|
||||||
layout_context,
|
layout_context,
|
||||||
positioning_context,
|
positioning_context,
|
||||||
&ContainingBlock {
|
&ContainingBlock {
|
||||||
size: ContainingBlockSize {
|
size: ContainingBlockSize {
|
||||||
inline: inline_size,
|
inline: inline_size,
|
||||||
block: block_size,
|
block: tentative_block_size.to_auto_or(),
|
||||||
},
|
},
|
||||||
style,
|
style,
|
||||||
},
|
},
|
||||||
|
@ -1214,12 +1282,7 @@ impl IndependentNonReplacedContents {
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
content_size = LogicalVec2 {
|
content_size = LogicalVec2 {
|
||||||
block: block_size.auto_is(|| {
|
block: compute_block_size(&layout),
|
||||||
layout.content_block_size.clamp_between_extremums(
|
|
||||||
content_min_box_size.block,
|
|
||||||
content_max_box_size.block,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
inline: inline_size,
|
inline: inline_size,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1232,10 +1295,24 @@ impl IndependentNonReplacedContents {
|
||||||
);
|
);
|
||||||
placement_rect = placement.place();
|
placement_rect = placement.place();
|
||||||
} else {
|
} else {
|
||||||
// Create a PlacementAmongFloats using the minimum size in all dimensions as the object size.
|
// If the inline size depends on the available space, then we need to iterate
|
||||||
|
// the various placement candidates, resolve both the inline and block sizes
|
||||||
|
// on each one placement area, and then check if the box actually fits it.
|
||||||
|
// As an optimization, we first compute a lower bound of the final box size,
|
||||||
|
// and skip placement candidates where not even the lower bound would fit.
|
||||||
let minimum_size_of_block = LogicalVec2 {
|
let minimum_size_of_block = LogicalVec2 {
|
||||||
inline: content_min_box_size.inline,
|
// For the lower bound of the inline size, simply assume no available space.
|
||||||
block: block_size.auto_is(|| content_min_box_size.block),
|
// TODO: this won't work for things like `calc-size(stretch, 100px - size)`,
|
||||||
|
// which should result in a bigger size when the available space gets smaller.
|
||||||
|
inline: compute_inline_size(Au::zero()),
|
||||||
|
block: match tentative_block_size {
|
||||||
|
// If we were able to resolve the preferred and maximum block sizes,
|
||||||
|
// use the tentative block size (it takes the 3 sizes into account).
|
||||||
|
SizeConstraint::Definite(size) if max_block_size.is_some() => size,
|
||||||
|
// Oherwise the preferred or maximum block size might end up being zero,
|
||||||
|
// so can only rely on the minimum block size.
|
||||||
|
_ => min_block_size,
|
||||||
|
},
|
||||||
} + pbm.padding_border_sums;
|
} + pbm.padding_border_sums;
|
||||||
let mut placement = PlacementAmongFloats::new(
|
let mut placement = PlacementAmongFloats::new(
|
||||||
&sequential_layout_state.floats,
|
&sequential_layout_state.floats,
|
||||||
|
@ -1247,12 +1324,9 @@ impl IndependentNonReplacedContents {
|
||||||
loop {
|
loop {
|
||||||
// First try to place the block using the minimum size as the object size.
|
// First try to place the block using the minimum size as the object size.
|
||||||
placement_rect = placement.place();
|
placement_rect = placement.place();
|
||||||
let proposed_inline_size = (placement_rect.size.inline -
|
let available_inline_size =
|
||||||
pbm.padding_border_sums.inline)
|
placement_rect.size.inline - pbm.padding_border_sums.inline;
|
||||||
.clamp_between_extremums(
|
let proposed_inline_size = compute_inline_size(available_inline_size);
|
||||||
content_min_box_size.inline,
|
|
||||||
content_max_box_size.inline,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Now lay out the block using the inline size we calculated from the placement.
|
// Now lay out the block using the inline size we calculated from the placement.
|
||||||
// Later we'll check to see if the resulting block size is compatible with the
|
// Later we'll check to see if the resulting block size is compatible with the
|
||||||
|
@ -1264,7 +1338,7 @@ impl IndependentNonReplacedContents {
|
||||||
&ContainingBlock {
|
&ContainingBlock {
|
||||||
size: ContainingBlockSize {
|
size: ContainingBlockSize {
|
||||||
inline: proposed_inline_size,
|
inline: proposed_inline_size,
|
||||||
block: block_size,
|
block: tentative_block_size.to_auto_or(),
|
||||||
},
|
},
|
||||||
style,
|
style,
|
||||||
},
|
},
|
||||||
|
@ -1291,12 +1365,7 @@ impl IndependentNonReplacedContents {
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
content_size = LogicalVec2 {
|
content_size = LogicalVec2 {
|
||||||
block: block_size.auto_is(|| {
|
block: compute_block_size(&layout),
|
||||||
layout.content_block_size.clamp_between_extremums(
|
|
||||||
content_min_box_size.block,
|
|
||||||
content_max_box_size.block,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
inline: proposed_inline_size,
|
inline: proposed_inline_size,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
13
tests/wpt/meta/MANIFEST.json
vendored
13
tests/wpt/meta/MANIFEST.json
vendored
|
@ -243225,6 +243225,19 @@
|
||||||
{}
|
{}
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
|
"bfc-next-to-float-2.html": [
|
||||||
|
"9683f92d49c11c2a436a023a0f758ae30c76da54",
|
||||||
|
[
|
||||||
|
null,
|
||||||
|
[
|
||||||
|
[
|
||||||
|
"/css/reference/ref-filled-green-100px-square.xht",
|
||||||
|
"=="
|
||||||
|
]
|
||||||
|
],
|
||||||
|
{}
|
||||||
|
]
|
||||||
|
],
|
||||||
"block-height-003.tentative.html": [
|
"block-height-003.tentative.html": [
|
||||||
"a469f8d5df2968f8e8ebfb68593563447bf11a10",
|
"a469f8d5df2968f8e8ebfb68593563447bf11a10",
|
||||||
[
|
[
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
[grid-container-percentage-001.html]
|
|
||||||
[.grid 3]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[.grid 4]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[.grid 5]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[.grid 8]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[.grid 9]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[.grid 10]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[.grid 13]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[.grid 14]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[.grid 15]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[.grid 18]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[.grid 19]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[.grid 20]
|
|
||||||
expected: FAIL
|
|
41
tests/wpt/tests/css/css-sizing/stretch/bfc-next-to-float-2.html
vendored
Normal file
41
tests/wpt/tests/css/css-sizing/stretch/bfc-next-to-float-2.html
vendored
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<link rel="author" title="Oriol Brufau" href="obrufau@igalia.com">
|
||||||
|
<link rel="help" href="https://drafts.csswg.org/css-sizing-4/#stretch-fit-sizing">
|
||||||
|
<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/4028">
|
||||||
|
<link rel="help" href="https://drafts.csswg.org/css2/#floats">
|
||||||
|
<meta name="assert" content="The border box of a block-level element that
|
||||||
|
establishes an independent formatting context can't overlap the margin box
|
||||||
|
of any float in the same block formatting context.
|
||||||
|
The stretch size needs to respect that.
|
||||||
|
">
|
||||||
|
<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
|
||||||
|
<style>
|
||||||
|
#reference-overlapped-red {
|
||||||
|
position: absolute;
|
||||||
|
background-color: red;
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
.stretch {
|
||||||
|
display: flow-root;
|
||||||
|
height: 25px;
|
||||||
|
background: green;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
|
||||||
|
<div id="reference-overlapped-red"></div>
|
||||||
|
|
||||||
|
<div style="width:200px; margin-left: -100px;">
|
||||||
|
<div style="float: left; width: 100px; height: 75px;"></div>
|
||||||
|
<div class="stretch" style="width: 0; min-width: stretch"></div>
|
||||||
|
<div class="stretch" style="width: 1000px; max-width: stretch"></div>
|
||||||
|
<div class="stretch" style="width: stretch; padding: 0 10px; border: solid green; border-width: 0 10px; margin-left: 10px"></div>
|
||||||
|
</div>
|
||||||
|
<div style="width:250px; margin-left: -150px;">
|
||||||
|
<div style="float: left; clear: left; width: 100px; height: 1px;"></div>
|
||||||
|
<div style="float: left; clear: left; width: 150px; height: 1px;"></div>
|
||||||
|
<div style="float: left; clear: left; width: 125px; height: 1px;"></div>
|
||||||
|
<div class="stretch" style="width: stretch"></div>
|
||||||
|
</div>
|
Loading…
Add table
Add a link
Reference in a new issue