Unify logic for laying out floats and atomic inlines (#33802)

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
This commit is contained in:
Oriol Brufau 2024-10-11 20:26:00 +02:00 committed by GitHub
parent 0eb8d22d88
commit a86dcfc6e7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 224 additions and 320 deletions

View file

@ -117,17 +117,18 @@ use crate::context::LayoutContext;
use crate::flow::float::{FloatBox, SequentialLayoutState};
use crate::flow::{CollapsibleWithParentStartMargin, FlowLayout};
use crate::formatting_contexts::{
Baselines, IndependentFormattingContext, NonReplacedFormattingContextContents,
Baselines, IndependentFormattingContext, IndependentLayoutResult,
NonReplacedFormattingContextContents,
};
use crate::fragment_tree::{
BoxFragment, CollapsedBlockMargins, CollapsedMargin, Fragment, FragmentFlags,
PositioningFragment,
};
use crate::geom::{LogicalRect, LogicalVec2, PhysicalPoint, PhysicalRect, Size, ToLogical};
use crate::geom::{LogicalRect, LogicalVec2, ToLogical};
use crate::positioned::{AbsolutelyPositionedBox, PositioningContext};
use crate::sizing::ContentSizes;
use crate::style_ext::{Clamp, ComputedValuesExt, PaddingBorderMargin};
use crate::{AuOrAuto, ContainingBlock, IndefiniteContainingBlock};
use crate::style_ext::{ComputedValuesExt, PaddingBorderMargin};
use crate::{ContainingBlock, IndefiniteContainingBlock};
// From gfxFontConstants.h in Firefox.
static FONT_SUBSCRIPT_OFFSET_RATIO: f32 = 0.20;
@ -1907,173 +1908,27 @@ impl IndependentFormattingContext {
offset_in_text: usize,
bidi_level: Level,
) {
let style = self.style();
let container_writing_mode = layout.containing_block.style.writing_mode;
let pbm = style.padding_border_margin(layout.containing_block);
let margin = pbm.margin.auto_is(Au::zero);
let pbm_sums = pbm.padding + pbm.border + margin;
// We need to know the inline size of the atomic before deciding whether to do the line break.
let (fragments, content_rect, baselines, mut child_positioning_context) = match self {
IndependentFormattingContext::Replaced(replaced) => {
let size = replaced
.contents
.used_size_as_if_inline_element(layout.containing_block, &replaced.style, &pbm)
.to_physical_size(container_writing_mode);
let fragments = replaced.contents.make_fragments(
&replaced.style,
layout.containing_block,
size,
);
let content_rect = PhysicalRect::new(PhysicalPoint::zero(), size);
(fragments, content_rect, None, None)
},
IndependentFormattingContext::NonReplaced(non_replaced) => {
let box_size = non_replaced
.style
.content_box_size(layout.containing_block, &pbm);
let max_box_size = non_replaced
.style
.content_max_box_size(layout.containing_block, &pbm);
let min_box_size = non_replaced
.style
.content_min_box_size(layout.containing_block, &pbm);
let available_inline_size =
layout.containing_block.inline_size - pbm_sums.inline_sum();
let available_block_size = layout
.containing_block
.block_size
.non_auto()
.map(|block_size| block_size - pbm_sums.block_sum());
let tentative_block_size = box_size
.block
.maybe_resolve_extrinsic(available_block_size)
.map(|v| {
let min_block_size = min_box_size
.block
.maybe_resolve_extrinsic(available_block_size)
.unwrap_or_default();
let max_block_size = max_box_size
.block
.maybe_resolve_extrinsic(available_block_size);
v.clamp_between_extremums(min_block_size, max_block_size)
})
.map_or(AuOrAuto::Auto, AuOrAuto::LengthPercentage);
let style = non_replaced.style.clone();
let mut get_content_size = || {
let containing_block_for_children =
IndefiniteContainingBlock::new_for_style_and_block_size(
&style,
tentative_block_size,
);
non_replaced
.inline_content_sizes(layout.layout_context, &containing_block_for_children)
};
// https://drafts.csswg.org/css2/visudet.html#inlineblock-width
let tentative_inline_size = box_size.inline.resolve(
Size::fit_content,
available_inline_size,
&mut get_content_size,
);
// https://drafts.csswg.org/css2/visudet.html#min-max-widths
// In this case “applying the rules above again” with a non-auto inline-size
// always results in that size.
let min_inline_size = min_box_size
.inline
.resolve_non_initial(available_inline_size, &mut get_content_size)
.unwrap_or_default();
let max_inline_size = max_box_size
.inline
.resolve_non_initial(available_inline_size, &mut get_content_size);
let inline_size =
tentative_inline_size.clamp_between_extremums(min_inline_size, max_inline_size);
let containing_block_for_children = ContainingBlock {
inline_size,
block_size: tentative_block_size,
style: &non_replaced.style,
};
assert_eq!(
layout.containing_block.style.writing_mode.is_horizontal(),
containing_block_for_children
.style
.writing_mode
.is_horizontal(),
"Mixed horizontal and vertical writing modes are not supported yet"
);
let mut positioning_context =
PositioningContext::new_for_style(&non_replaced.style)
.unwrap_or_else(|| PositioningContext::new_for_subtree(true));
let independent_layout = non_replaced.layout(
layout.layout_context,
&mut positioning_context,
&containing_block_for_children,
layout.containing_block,
);
let (inline_size, block_size) = match independent_layout
.content_inline_size_for_table
{
Some(inline) => (inline, independent_layout.content_block_size),
None => {
// https://drafts.csswg.org/css2/visudet.html#block-root-margin
let stretch_size =
available_block_size.unwrap_or(independent_layout.content_block_size);
let mut get_content_size = || independent_layout.content_block_size.into();
let min_block_size = min_box_size
.block
.resolve_non_initial(stretch_size, &mut get_content_size)
.unwrap_or_default();
let max_block_size = max_box_size
.block
.resolve_non_initial(stretch_size, &mut get_content_size);
let block_size = tentative_block_size
.auto_is(|| independent_layout.content_block_size)
.clamp_between_extremums(min_block_size, max_block_size);
(inline_size, block_size)
},
};
let content_rect = PhysicalRect::new(
PhysicalPoint::zero(),
LogicalVec2 {
block: block_size,
inline: inline_size,
}
.to_physical_size(container_writing_mode),
);
(
independent_layout.fragments,
content_rect,
Some(independent_layout.baselines),
Some(positioning_context),
)
},
};
let mut child_positioning_context = PositioningContext::new_for_style(self.style())
.unwrap_or_else(|| PositioningContext::new_for_subtree(true));
let IndependentLayoutResult {
mut fragment,
baselines,
pbm_sums,
} = self.layout_float_or_atomic_inline(
layout.layout_context,
&mut child_positioning_context,
layout.containing_block,
);
// Offset the content rectangle by the physical offset of the padding, border, and margin.
let container_writing_mode = layout.containing_block.style.writing_mode;
let pbm_physical_offset = pbm_sums
.start_offset()
.to_physical_size(container_writing_mode);
let content_rect = content_rect.translate(pbm_physical_offset.to_vector());
let fragment = BoxFragment::new(
self.base_fragment_info(),
self.style().clone(),
fragments,
content_rect,
pbm.padding.to_physical(container_writing_mode),
pbm.border.to_physical(container_writing_mode),
margin.to_physical(container_writing_mode),
None, /* clearance */
CollapsedBlockMargins::zero(),
);
fragment.content_rect = fragment
.content_rect
.translate(pbm_physical_offset.to_vector());
// Apply baselines if necessary.
let mut fragment = match baselines {
@ -2083,14 +1938,18 @@ impl IndependentFormattingContext {
// Lay out absolutely positioned children if this new atomic establishes a containing block
// for absolutes.
if let Some(positioning_context) = child_positioning_context.as_mut() {
let positioning_context = if matches!(self, IndependentFormattingContext::Replaced(_)) {
None
} else {
if fragment
.style
.establishes_containing_block_for_absolute_descendants(fragment.base.flags)
{
positioning_context.layout_collected_children(layout.layout_context, &mut fragment);
child_positioning_context
.layout_collected_children(layout.layout_context, &mut fragment);
}
}
Some(child_positioning_context)
};
if layout.text_wrap_mode == TextWrapMode::Wrap &&
!layout
@ -2122,7 +1981,7 @@ impl IndependentFormattingContext {
AtomicLineItem {
fragment,
size,
positioning_context: child_positioning_context,
positioning_context,
baseline_offset_in_parent,
baseline_offset_in_item: baseline_offset,
bidi_level,