mirror of
https://github.com/servo/servo.git
synced 2025-07-23 07:13:52 +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::replaced::ReplacedContents;
|
||||
use crate::sizing::{self, ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult};
|
||||
use crate::style_ext::{
|
||||
Clamp, ComputedValuesExt, ContentBoxSizesAndPBM, ContentBoxSizesAndPBMDeprecated,
|
||||
PaddingBorderMargin,
|
||||
};
|
||||
use crate::style_ext::{Clamp, ComputedValuesExt, ContentBoxSizesAndPBM, PaddingBorderMargin};
|
||||
use crate::{
|
||||
ConstraintSpace, ContainingBlock, ContainingBlockSize, IndefiniteContainingBlock,
|
||||
SizeConstraint,
|
||||
|
@ -1143,23 +1140,15 @@ impl IndependentNonReplacedContents {
|
|||
containing_block: &ContainingBlock<'_>,
|
||||
sequential_layout_state: &mut SequentialLayoutState,
|
||||
) -> BoxFragment {
|
||||
let style = &base.style;
|
||||
let containing_block_writing_mode = containing_block.style.writing_mode;
|
||||
let ContentBoxSizesAndPBMDeprecated {
|
||||
let ContentBoxSizesAndPBM {
|
||||
content_box_size,
|
||||
content_min_box_size,
|
||||
content_max_box_size,
|
||||
pbm,
|
||||
depends_on_block_constraints,
|
||||
} = base
|
||||
.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)
|
||||
});
|
||||
} = style.content_box_sizes_and_padding_border_margin(&containing_block.into());
|
||||
|
||||
let (margin_block_start, margin_block_end) =
|
||||
solve_block_margins_for_in_flow_block_level(&pbm);
|
||||
|
@ -1178,7 +1167,6 @@ impl IndependentNonReplacedContents {
|
|||
let mut content_size;
|
||||
let mut layout;
|
||||
let mut placement_rect;
|
||||
let style = &base.style;
|
||||
|
||||
// First compute the clear position required by the 'clear' property.
|
||||
// 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)
|
||||
});
|
||||
|
||||
if let AuOrAuto::LengthPercentage(ref inline_size) = content_box_size.inline {
|
||||
let inline_size = inline_size
|
||||
.clamp_between_extremums(content_min_box_size.inline, content_max_box_size.inline);
|
||||
// Then compute a tentative block size, only taking extrinsic values into account.
|
||||
let margin = pbm.margin.auto_is(Au::zero);
|
||||
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_context,
|
||||
positioning_context,
|
||||
&ContainingBlock {
|
||||
size: ContainingBlockSize {
|
||||
inline: inline_size,
|
||||
block: block_size,
|
||||
block: tentative_block_size.to_auto_or(),
|
||||
},
|
||||
style,
|
||||
},
|
||||
|
@ -1214,12 +1282,7 @@ impl IndependentNonReplacedContents {
|
|||
};
|
||||
} else {
|
||||
content_size = LogicalVec2 {
|
||||
block: block_size.auto_is(|| {
|
||||
layout.content_block_size.clamp_between_extremums(
|
||||
content_min_box_size.block,
|
||||
content_max_box_size.block,
|
||||
)
|
||||
}),
|
||||
block: compute_block_size(&layout),
|
||||
inline: inline_size,
|
||||
};
|
||||
}
|
||||
|
@ -1232,10 +1295,24 @@ impl IndependentNonReplacedContents {
|
|||
);
|
||||
placement_rect = placement.place();
|
||||
} 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 {
|
||||
inline: content_min_box_size.inline,
|
||||
block: block_size.auto_is(|| content_min_box_size.block),
|
||||
// For the lower bound of the inline size, simply assume no available space.
|
||||
// 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;
|
||||
let mut placement = PlacementAmongFloats::new(
|
||||
&sequential_layout_state.floats,
|
||||
|
@ -1247,12 +1324,9 @@ impl IndependentNonReplacedContents {
|
|||
loop {
|
||||
// First try to place the block using the minimum size as the object size.
|
||||
placement_rect = placement.place();
|
||||
let proposed_inline_size = (placement_rect.size.inline -
|
||||
pbm.padding_border_sums.inline)
|
||||
.clamp_between_extremums(
|
||||
content_min_box_size.inline,
|
||||
content_max_box_size.inline,
|
||||
);
|
||||
let available_inline_size =
|
||||
placement_rect.size.inline - pbm.padding_border_sums.inline;
|
||||
let proposed_inline_size = compute_inline_size(available_inline_size);
|
||||
|
||||
// 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
|
||||
|
@ -1264,7 +1338,7 @@ impl IndependentNonReplacedContents {
|
|||
&ContainingBlock {
|
||||
size: ContainingBlockSize {
|
||||
inline: proposed_inline_size,
|
||||
block: block_size,
|
||||
block: tentative_block_size.to_auto_or(),
|
||||
},
|
||||
style,
|
||||
},
|
||||
|
@ -1291,12 +1365,7 @@ impl IndependentNonReplacedContents {
|
|||
};
|
||||
} else {
|
||||
content_size = LogicalVec2 {
|
||||
block: block_size.auto_is(|| {
|
||||
layout.content_block_size.clamp_between_extremums(
|
||||
content_min_box_size.block,
|
||||
content_max_box_size.block,
|
||||
)
|
||||
}),
|
||||
block: compute_block_size(&layout),
|
||||
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": [
|
||||
"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