Auto merge of #29887 - Loirooriol:float-inline-size-complete, r=mrobinson

Handle floats in BlockContainer::inline_content_sizes

Typically, block-level contents are stacked vertically, so this was just taking the maximum size among all contents. However, floats can be stacked horizontally, so we need to sum their sizes.

<!-- Please describe your changes on the following line: -->

---
<!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `___` with appropriate data: -->
- [X] `./mach build -d` does not report any errors
- [X] `./mach test-tidy` does not report any errors
- [X] These changes fix #29874

<!-- Either: -->
- [X] There are tests for these changes OR
- [ ] These changes do not require tests because ___

<!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.-->

<!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->
This commit is contained in:
bors-servo 2023-06-19 16:56:16 +02:00 committed by GitHub
commit 836ae5fa48
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 112 additions and 28 deletions

View file

@ -690,12 +690,7 @@ impl FloatBox {
.floats
.lower_ceiling(sequential_layout_state.current_block_position_including_margins());
let style = match self.contents {
IndependentFormattingContext::Replaced(ref replaced) => replaced.style.clone(),
IndependentFormattingContext::NonReplaced(ref non_replaced) => {
non_replaced.style.clone()
},
};
let style = self.contents.style().clone();
let float_context = &mut sequential_layout_state.floats;
let box_fragment = positioning_context.layout_maybe_position_relative_fragment(
layout_context,

View file

@ -25,6 +25,8 @@ use crate::ContainingBlock;
use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator};
use rayon_croissant::ParallelIteratorExt;
use servo_arc::Arc;
use style::computed_values::clear::T as Clear;
use style::computed_values::float::T as Float;
use style::logical_geometry::WritingMode;
use style::properties::ComputedValues;
use style::values::computed::{Length, LengthOrAuto};
@ -107,6 +109,99 @@ impl BlockFormattingContext {
}
}
/// Finds the min/max-content inline size of the block-level children of a block container.
/// The in-flow boxes will stack vertically, so we only need to consider the maximum size.
/// But floats can flow horizontally depending on 'clear', so we may need to sum their sizes.
/// CSS 2 does not define the exact algorithm, this logic is based on the behavior observed
/// on Gecko and Blink.
fn calculate_inline_content_size_for_block_level_boxes(
boxes: &[ArcRefCell<BlockLevelBox>],
layout_context: &LayoutContext,
writing_mode: WritingMode,
) -> ContentSizes {
let get_box_info = |box_: &ArcRefCell<BlockLevelBox>| {
let size = box_
.borrow_mut()
.inline_content_sizes(layout_context, writing_mode);
if let BlockLevelBox::OutOfFlowFloatBox(ref float_box) = *box_.borrow_mut() {
let style_box = &float_box.contents.style().get_box();
(size, style_box.float, style_box.clear)
} else {
// The element may in fact have clearance, but the logic below ignores it,
// so don't bother retrieving it from the style.
(size, Float::None, Clear::None)
}
};
/// When iterating the block-level boxes to compute the inline content sizes,
/// this struct contains the data accumulated up to the current box.
struct AccumulatedData {
/// The maximum size seen so far, not including trailing uncleared floats.
max_size: ContentSizes,
/// The size of the trailing uncleared floats with 'float: left'.
left_floats: ContentSizes,
/// The size of the trailing uncleared floats with 'float: right'.
right_floats: ContentSizes,
}
impl AccumulatedData {
fn max_size_including_uncleared_floats(&self) -> ContentSizes {
self.max_size.max(self.left_floats.add(&self.right_floats))
}
fn clear_floats(&mut self, clear: Clear) {
match clear {
Clear::Left => {
self.max_size = self.max_size_including_uncleared_floats();
self.left_floats = ContentSizes::zero();
},
Clear::Right => {
self.max_size = self.max_size_including_uncleared_floats();
self.right_floats = ContentSizes::zero();
},
Clear::Both => {
self.max_size = self.max_size_including_uncleared_floats();
self.left_floats = ContentSizes::zero();
self.right_floats = ContentSizes::zero();
},
Clear::None => {},
};
}
}
let accumulate = |mut data: AccumulatedData, (size, float, clear)| {
if float == Float::None {
// TODO: The first BFC root after a sequence of floats should appear next to them
// (if it doesn't have clearance).
data.clear_floats(Clear::Both);
data.max_size = data.max_size.max(size);
} else {
data.clear_floats(clear);
match float {
Float::Left => data.left_floats = data.left_floats.add(&size),
Float::Right => data.right_floats = data.right_floats.add(&size),
Float::None => unreachable!(),
}
}
data
};
let zero = AccumulatedData {
max_size: ContentSizes::zero(),
left_floats: ContentSizes::zero(),
right_floats: ContentSizes::zero(),
};
let data = if layout_context.use_rayon {
boxes
.par_iter()
.map(get_box_info)
.collect::<Vec<_>>()
.into_iter()
.fold(zero, accumulate)
} else {
boxes.iter().map(get_box_info).fold(zero, accumulate)
};
data.max_size_including_uncleared_floats()
}
impl BlockContainer {
fn layout(
&self,
@ -143,21 +238,11 @@ impl BlockContainer {
writing_mode: WritingMode,
) -> ContentSizes {
match &self {
Self::BlockLevelBoxes(boxes) if layout_context.use_rayon => boxes
.par_iter()
.map(|box_| {
box_.borrow_mut()
.inline_content_sizes(layout_context, writing_mode)
})
.reduce(ContentSizes::zero, ContentSizes::max),
Self::BlockLevelBoxes(boxes) => boxes
.iter()
.map(|box_| {
box_.borrow_mut()
.inline_content_sizes(layout_context, writing_mode)
})
.reduce(ContentSizes::max)
.unwrap_or_else(ContentSizes::zero),
Self::BlockLevelBoxes(boxes) => calculate_inline_content_size_for_block_level_boxes(
boxes,
layout_context,
writing_mode,
),
Self::InlineFormattingContext(context) => {
context.inline_content_sizes(layout_context, writing_mode)
},

View file

@ -33,13 +33,20 @@ impl ContentSizes {
}
}
pub fn max(self, other: Self) -> Self {
pub fn max(&self, other: Self) -> Self {
Self {
min_content: self.min_content.max(other.min_content),
max_content: self.max_content.max(other.max_content),
}
}
pub fn add(&self, other: &Self) -> Self {
Self {
min_content: self.min_content.max(other.min_content),
max_content: self.max_content + other.max_content,
}
}
/// Relevant to outer intrinsic inline sizes, for percentages from padding and margin.
pub fn adjust_for_pbm_percentages(&mut self, percentages: Percentage) {
// " Note that this may yield an infinite result, but undefined results

View file

@ -1,4 +1,7 @@
[flex-basis-intrinsics-001.html]
[.flex-item 1]
expected: FAIL
[.flex-item 2]
expected: FAIL
@ -20,11 +23,5 @@
[.flex-item 6]
expected: FAIL
[.flex-item 7]
expected: FAIL
[.flex-item 9]
expected: FAIL
[.flex-item 11]
expected: FAIL