mirror of
https://github.com/servo/servo.git
synced 2025-07-22 23:03:42 +01:00
layout: Collect both start and end baselines for fragments (#31230)
This change starts collecting the starting baseline set for fragments, which is necessary for some layout modes (flex and tables, namely) as well as being important for the implementation of `align-items`. In addition, it converts baseline measurement to use `Au` everywhere. Co-authored-by: Oriol Brufau <obrufau@igalia.com>
This commit is contained in:
parent
28bde741ed
commit
7f13316f24
10 changed files with 112 additions and 101 deletions
|
@ -25,7 +25,7 @@ use super::geom::{
|
||||||
use super::{FlexContainer, FlexLevelBox};
|
use super::{FlexContainer, FlexLevelBox};
|
||||||
use crate::cell::ArcRefCell;
|
use crate::cell::ArcRefCell;
|
||||||
use crate::context::LayoutContext;
|
use crate::context::LayoutContext;
|
||||||
use crate::formatting_contexts::{IndependentFormattingContext, IndependentLayout};
|
use crate::formatting_contexts::{Baselines, IndependentFormattingContext, IndependentLayout};
|
||||||
use crate::fragment_tree::{BoxFragment, CollapsedBlockMargins, Fragment};
|
use crate::fragment_tree::{BoxFragment, CollapsedBlockMargins, Fragment};
|
||||||
use crate::geom::{AuOrAuto, LengthOrAuto, LogicalRect, LogicalSides, LogicalVec2};
|
use crate::geom::{AuOrAuto, LengthOrAuto, LogicalRect, LogicalSides, LogicalVec2};
|
||||||
use crate::positioned::{AbsolutelyPositionedBox, PositioningContext, PositioningContextLength};
|
use crate::positioned::{AbsolutelyPositionedBox, PositioningContext, PositioningContextLength};
|
||||||
|
@ -410,7 +410,7 @@ impl FlexContainer {
|
||||||
IndependentLayout {
|
IndependentLayout {
|
||||||
fragments,
|
fragments,
|
||||||
content_block_size: content_block_size.into(),
|
content_block_size: content_block_size.into(),
|
||||||
last_inflow_baseline_offset: None,
|
baselines: Baselines::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -848,6 +848,7 @@ impl FlexLine<'_> {
|
||||||
let margin = flex_context.sides_to_flow_relative(*margin);
|
let margin = flex_context.sides_to_flow_relative(*margin);
|
||||||
let collapsed_margin = CollapsedBlockMargins::from_margin(&margin);
|
let collapsed_margin = CollapsedBlockMargins::from_margin(&margin);
|
||||||
(
|
(
|
||||||
|
// TODO: We should likely propagate baselines from `display: flex`.
|
||||||
BoxFragment::new(
|
BoxFragment::new(
|
||||||
item.box_.base_fragment_info(),
|
item.box_.base_fragment_info(),
|
||||||
item.box_.style().clone(),
|
item.box_.style().clone(),
|
||||||
|
@ -857,8 +858,6 @@ impl FlexLine<'_> {
|
||||||
flex_context.sides_to_flow_relative(item.border.map(|t| (*t).into())),
|
flex_context.sides_to_flow_relative(item.border.map(|t| (*t).into())),
|
||||||
margin,
|
margin,
|
||||||
None, /* clearance */
|
None, /* clearance */
|
||||||
// TODO: We should likely propagate baselines from `display: flex`.
|
|
||||||
None, /* last_inflow_baseline_offset */
|
|
||||||
collapsed_margin,
|
collapsed_margin,
|
||||||
),
|
),
|
||||||
item_result.positioning_context,
|
item_result.positioning_context,
|
||||||
|
|
|
@ -982,7 +982,6 @@ impl FloatBox {
|
||||||
// Clearance is handled internally by the float placement logic, so there's no need
|
// Clearance is handled internally by the float placement logic, so there's no need
|
||||||
// to store it explicitly in the fragment.
|
// to store it explicitly in the fragment.
|
||||||
None, // clearance
|
None, // clearance
|
||||||
None, // last_inflow_baseline_offset
|
|
||||||
CollapsedBlockMargins::zero(),
|
CollapsedBlockMargins::zero(),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
|
@ -34,7 +34,7 @@ use crate::cell::ArcRefCell;
|
||||||
use crate::context::LayoutContext;
|
use crate::context::LayoutContext;
|
||||||
use crate::flow::float::{FloatBox, SequentialLayoutState};
|
use crate::flow::float::{FloatBox, SequentialLayoutState};
|
||||||
use crate::flow::FlowLayout;
|
use crate::flow::FlowLayout;
|
||||||
use crate::formatting_contexts::IndependentFormattingContext;
|
use crate::formatting_contexts::{Baselines, IndependentFormattingContext};
|
||||||
use crate::fragment_tree::{
|
use crate::fragment_tree::{
|
||||||
AnonymousFragment, BaseFragmentInfo, BoxFragment, CollapsedBlockMargins, CollapsedMargin,
|
AnonymousFragment, BaseFragmentInfo, BoxFragment, CollapsedBlockMargins, CollapsedMargin,
|
||||||
Fragment,
|
Fragment,
|
||||||
|
@ -246,9 +246,9 @@ impl LineBlockSizes {
|
||||||
let height_from_ascent_and_descent = self
|
let height_from_ascent_and_descent = self
|
||||||
.baseline_relative_size_for_line_height
|
.baseline_relative_size_for_line_height
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|size| Length::from((size.ascent + size.descent).abs()))
|
.map(|size| (size.ascent + size.descent).abs())
|
||||||
.unwrap_or_else(Length::zero);
|
.unwrap_or_else(Au::zero);
|
||||||
self.line_height.max(height_from_ascent_and_descent)
|
self.line_height.max(height_from_ascent_and_descent.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn max(&self, other: &LineBlockSizes) -> LineBlockSizes {
|
fn max(&self, other: &LineBlockSizes) -> LineBlockSizes {
|
||||||
|
@ -286,17 +286,16 @@ impl LineBlockSizes {
|
||||||
/// > to minimize the line box height. If such boxes are tall enough, there are multiple
|
/// > to minimize the line box height. If such boxes are tall enough, there are multiple
|
||||||
/// > solutions and CSS 2 does not define the position of the line box's baseline (i.e.,
|
/// > solutions and CSS 2 does not define the position of the line box's baseline (i.e.,
|
||||||
/// > the position of the strut, see below).
|
/// > the position of the strut, see below).
|
||||||
fn find_baseline_offset(&self) -> Length {
|
fn find_baseline_offset(&self) -> Au {
|
||||||
match self.baseline_relative_size_for_line_height.as_ref() {
|
match self.baseline_relative_size_for_line_height.as_ref() {
|
||||||
Some(size) => size.ascent.into(),
|
Some(size) => size.ascent,
|
||||||
None => {
|
None => {
|
||||||
// This is the case mentinoned above where there are multiple solutions.
|
// This is the case mentinoned above where there are multiple solutions.
|
||||||
// This code is putting the baseline roughly in the middle of the line.
|
// This code is putting the baseline roughly in the middle of the line.
|
||||||
let leading = self.resolve() -
|
let leading = Au::from(self.resolve()) -
|
||||||
(self.size_for_baseline_positioning.ascent +
|
(self.size_for_baseline_positioning.ascent +
|
||||||
self.size_for_baseline_positioning.descent)
|
self.size_for_baseline_positioning.descent);
|
||||||
.into();
|
leading.scale_by(0.5) + self.size_for_baseline_positioning.ascent
|
||||||
leading.scale_by(0.5) + self.size_for_baseline_positioning.ascent.into()
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -573,10 +572,10 @@ pub(super) struct InlineFormattingContextState<'a, 'b> {
|
||||||
/// common ancestor is used.
|
/// common ancestor is used.
|
||||||
white_space: WhiteSpace,
|
white_space: WhiteSpace,
|
||||||
|
|
||||||
/// The offset of the last baseline in the inline formatting context that we
|
/// The offset of the first and last baselines in the inline formatting context that we
|
||||||
/// are laying out. This is used to propagate baselines to the ancestors of
|
/// are laying out. This is used to propagate baselines to the ancestors of
|
||||||
/// `display: inline-block` elements.
|
/// `display: inline-block` elements and table content.
|
||||||
last_baseline_offset: Option<Length>,
|
baselines: Baselines,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b> InlineFormattingContextState<'a, 'b> {
|
impl<'a, 'b> InlineFormattingContextState<'a, 'b> {
|
||||||
|
@ -760,7 +759,10 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.last_baseline_offset = Some(baseline_offset + block_start_position.into());
|
let baseline = baseline_offset + block_start_position;
|
||||||
|
self.baselines.first.get_or_insert(baseline);
|
||||||
|
self.baselines.last = Some(baseline);
|
||||||
|
|
||||||
let line_rect = LogicalRect {
|
let line_rect = LogicalRect {
|
||||||
// The inline part of this start offset was taken into account when determining
|
// The inline part of this start offset was taken into account when determining
|
||||||
// the inline start of the line in `calculate_inline_start_for_current_line` so
|
// the inline start of the line in `calculate_inline_start_for_current_line` so
|
||||||
|
@ -1453,7 +1455,7 @@ impl InlineFormattingContext {
|
||||||
prevent_soft_wrap_opportunity_before_next_atomic: false,
|
prevent_soft_wrap_opportunity_before_next_atomic: false,
|
||||||
had_inflow_content: false,
|
had_inflow_content: false,
|
||||||
white_space: containing_block.style.get_inherited_text().white_space,
|
white_space: containing_block.style.get_inherited_text().white_space,
|
||||||
last_baseline_offset: None,
|
baselines: Baselines::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// FIXME(pcwalton): This assumes that margins never collapse through inline formatting
|
// FIXME(pcwalton): This assumes that margins never collapse through inline formatting
|
||||||
|
@ -1506,7 +1508,7 @@ impl InlineFormattingContext {
|
||||||
fragments: ifc.fragments,
|
fragments: ifc.fragments,
|
||||||
content_block_size,
|
content_block_size,
|
||||||
collapsible_margins_in_children,
|
collapsible_margins_in_children,
|
||||||
last_inflow_baseline_offset: ifc.last_baseline_offset,
|
baselines: ifc.baselines,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1791,7 +1793,6 @@ impl IndependentFormattingContext {
|
||||||
pbm.border,
|
pbm.border,
|
||||||
margin,
|
margin,
|
||||||
None, /* clearance */
|
None, /* clearance */
|
||||||
None, /* last_inflow_baseline_offset */
|
|
||||||
CollapsedBlockMargins::zero(),
|
CollapsedBlockMargins::zero(),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
@ -1873,11 +1874,9 @@ impl IndependentFormattingContext {
|
||||||
pbm.border,
|
pbm.border,
|
||||||
margin,
|
margin,
|
||||||
None,
|
None,
|
||||||
independent_layout
|
|
||||||
.last_inflow_baseline_offset
|
|
||||||
.map(|t| t.into()),
|
|
||||||
CollapsedBlockMargins::zero(),
|
CollapsedBlockMargins::zero(),
|
||||||
)
|
)
|
||||||
|
.with_baselines(independent_layout.baselines)
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1891,10 +1890,10 @@ impl IndependentFormattingContext {
|
||||||
|
|
||||||
let size = &pbm_sums.sum() + &fragment.content_rect.size;
|
let size = &pbm_sums.sum() + &fragment.content_rect.size;
|
||||||
let baseline_offset = fragment
|
let baseline_offset = fragment
|
||||||
.last_baseline_offset
|
.baselines
|
||||||
.map(|baseline_offset| pbm_sums.block_start + baseline_offset)
|
.last
|
||||||
.unwrap_or(size.block);
|
.map(|baseline| Au::from(pbm_sums.block_start) + baseline)
|
||||||
let baseline_offset = Au::from_f32_px(baseline_offset.px());
|
.unwrap_or(size.block.into());
|
||||||
|
|
||||||
let (block_sizes, baseline_offset_in_parent) =
|
let (block_sizes, baseline_offset_in_parent) =
|
||||||
self.get_block_sizes_and_baseline_offset(ifc, size.block, baseline_offset);
|
self.get_block_sizes_and_baseline_offset(ifc, size.block, baseline_offset);
|
||||||
|
|
|
@ -40,7 +40,7 @@ pub(super) struct LineMetrics {
|
||||||
pub block_size: Length,
|
pub block_size: Length,
|
||||||
|
|
||||||
/// The block offset of this line's baseline from [`Self:block_offset`].
|
/// The block offset of this line's baseline from [`Self:block_offset`].
|
||||||
pub baseline_block_offset: Length,
|
pub baseline_block_offset: Au,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// State used when laying out the [`LineItem`]s collected for the line currently being
|
/// State used when laying out the [`LineItem`]s collected for the line currently being
|
||||||
|
@ -54,7 +54,7 @@ pub(super) struct LineItemLayoutState<'a> {
|
||||||
/// The block offset of the parent's baseline relative to the block start of the line. This
|
/// The block offset of the parent's baseline relative to the block start of the line. This
|
||||||
/// is often the same as [`Self::block_offset_of_parent`], but can be different for the root
|
/// is often the same as [`Self::block_offset_of_parent`], but can be different for the root
|
||||||
/// element.
|
/// element.
|
||||||
pub baseline_offset: Length,
|
pub baseline_offset: Au,
|
||||||
|
|
||||||
pub ifc_containing_block: &'a ContainingBlock<'a>,
|
pub ifc_containing_block: &'a ContainingBlock<'a>,
|
||||||
pub positioning_context: &'a mut PositioningContext,
|
pub positioning_context: &'a mut PositioningContext,
|
||||||
|
@ -255,7 +255,7 @@ impl TextRunLineItem {
|
||||||
// fallback fonts that use baseline relatve alignment, it might be different.
|
// fallback fonts that use baseline relatve alignment, it might be different.
|
||||||
let mut start_corner = &LogicalVec2 {
|
let mut start_corner = &LogicalVec2 {
|
||||||
inline: state.inline_position,
|
inline: state.inline_position,
|
||||||
block: state.baseline_offset - self.font_metrics.ascent.into(),
|
block: (state.baseline_offset - self.font_metrics.ascent).into(),
|
||||||
} - &state.parent_offset;
|
} - &state.parent_offset;
|
||||||
if !is_baseline_relative(
|
if !is_baseline_relative(
|
||||||
self.parent_style
|
self.parent_style
|
||||||
|
@ -350,7 +350,7 @@ impl InlineBoxLineItem {
|
||||||
inline_position: state.inline_position,
|
inline_position: state.inline_position,
|
||||||
parent_offset: LogicalVec2 {
|
parent_offset: LogicalVec2 {
|
||||||
inline: state.inline_position,
|
inline: state.inline_position,
|
||||||
block: block_start_offset,
|
block: block_start_offset.into(),
|
||||||
},
|
},
|
||||||
ifc_containing_block: state.ifc_containing_block,
|
ifc_containing_block: state.ifc_containing_block,
|
||||||
positioning_context: nested_positioning_context,
|
positioning_context: nested_positioning_context,
|
||||||
|
@ -388,7 +388,7 @@ impl InlineBoxLineItem {
|
||||||
let mut content_rect = LogicalRect {
|
let mut content_rect = LogicalRect {
|
||||||
start_corner: LogicalVec2 {
|
start_corner: LogicalVec2 {
|
||||||
inline: state.inline_position,
|
inline: state.inline_position,
|
||||||
block: block_start_offset,
|
block: block_start_offset.into(),
|
||||||
},
|
},
|
||||||
size: LogicalVec2 {
|
size: LogicalVec2 {
|
||||||
inline: nested_state.inline_position - state.inline_position,
|
inline: nested_state.inline_position - state.inline_position,
|
||||||
|
@ -405,6 +405,9 @@ impl InlineBoxLineItem {
|
||||||
content_rect.start_corner += &relative_adjustement(&style, state.ifc_containing_block);
|
content_rect.start_corner += &relative_adjustement(&style, state.ifc_containing_block);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NB: There is no need to set a baseline offset for this BoxFragment, because the
|
||||||
|
// baselines of this InlineFormattingContext is what will propagate to `display:
|
||||||
|
// inline-block` ancestors.
|
||||||
let mut fragment = BoxFragment::new(
|
let mut fragment = BoxFragment::new(
|
||||||
self.base_fragment_info,
|
self.base_fragment_info,
|
||||||
self.style.clone(),
|
self.style.clone(),
|
||||||
|
@ -414,10 +417,6 @@ impl InlineBoxLineItem {
|
||||||
border,
|
border,
|
||||||
margin,
|
margin,
|
||||||
None, /* clearance */
|
None, /* clearance */
|
||||||
// There is no need to set a baseline offset for this BoxFragment, because
|
|
||||||
// the last baseline of this InlineFormattingContext is what will propagate
|
|
||||||
// to `display: inline-block` ancestors.
|
|
||||||
None, /* last_inflow_baseline_offset */
|
|
||||||
CollapsedBlockMargins::zero(),
|
CollapsedBlockMargins::zero(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -446,23 +445,19 @@ impl InlineBoxLineItem {
|
||||||
/// Given our font metrics, calculate the space above the baseline we need for our content.
|
/// Given our font metrics, calculate the space above the baseline we need for our content.
|
||||||
/// Note that this space does not include space for any content in child inline boxes, as
|
/// Note that this space does not include space for any content in child inline boxes, as
|
||||||
/// they are not included in our content rect.
|
/// they are not included in our content rect.
|
||||||
fn calculate_space_above_baseline(&self) -> Length {
|
fn calculate_space_above_baseline(&self) -> Au {
|
||||||
let (ascent, descent, line_gap) = (
|
let (ascent, descent, line_gap) = (
|
||||||
self.font_metrics.ascent,
|
self.font_metrics.ascent,
|
||||||
self.font_metrics.descent,
|
self.font_metrics.descent,
|
||||||
self.font_metrics.line_gap,
|
self.font_metrics.line_gap,
|
||||||
);
|
);
|
||||||
let leading = line_gap - (ascent + descent);
|
let leading = line_gap - (ascent + descent);
|
||||||
(leading.scale_by(0.5) + ascent).into()
|
leading.scale_by(0.5) + ascent
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Given the state for a line item layout and the space above the baseline for this inline
|
/// Given the state for a line item layout and the space above the baseline for this inline
|
||||||
/// box, find the block start position relative to the line block start position.
|
/// box, find the block start position relative to the line block start position.
|
||||||
fn calculate_block_start(
|
fn calculate_block_start(&self, state: &LineItemLayoutState, space_above_baseline: Au) -> Au {
|
||||||
&self,
|
|
||||||
state: &LineItemLayoutState,
|
|
||||||
space_above_baseline: Length,
|
|
||||||
) -> Length {
|
|
||||||
let vertical_align = self.style.effective_vertical_align_for_inline_layout();
|
let vertical_align = self.style.effective_vertical_align_for_inline_layout();
|
||||||
let line_gap = self.font_metrics.line_gap;
|
let line_gap = self.font_metrics.line_gap;
|
||||||
|
|
||||||
|
@ -470,16 +465,16 @@ impl InlineBoxLineItem {
|
||||||
// baseline, so we need to make it relative to the line block start.
|
// baseline, so we need to make it relative to the line block start.
|
||||||
match vertical_align {
|
match vertical_align {
|
||||||
GenericVerticalAlign::Keyword(VerticalAlignKeyword::Top) => {
|
GenericVerticalAlign::Keyword(VerticalAlignKeyword::Top) => {
|
||||||
let line_height = line_height(&self.style, &self.font_metrics);
|
let line_height: Au = line_height(&self.style, &self.font_metrics).into();
|
||||||
(line_height - line_gap.into()).scale_by(0.5)
|
(line_height - line_gap).scale_by(0.5)
|
||||||
},
|
},
|
||||||
GenericVerticalAlign::Keyword(VerticalAlignKeyword::Bottom) => {
|
GenericVerticalAlign::Keyword(VerticalAlignKeyword::Bottom) => {
|
||||||
let line_height = line_height(&self.style, &self.font_metrics);
|
let line_height: Au = line_height(&self.style, &self.font_metrics).into();
|
||||||
let half_leading = (line_height - line_gap.into()).scale_by(0.5);
|
let half_leading = (line_height - line_gap).scale_by(0.5);
|
||||||
state.line_metrics.block_size - line_height + half_leading
|
Au::from(state.line_metrics.block_size) - line_height + half_leading
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
state.line_metrics.baseline_block_offset + Length::from(self.baseline_offset) -
|
state.line_metrics.baseline_block_offset + self.baseline_offset -
|
||||||
space_above_baseline
|
space_above_baseline
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -542,9 +537,8 @@ impl AtomicLineItem {
|
||||||
|
|
||||||
// This covers all baseline-relative vertical alignment.
|
// This covers all baseline-relative vertical alignment.
|
||||||
_ => {
|
_ => {
|
||||||
let baseline = line_metrics.baseline_block_offset +
|
let baseline = line_metrics.baseline_block_offset + self.baseline_offset_in_parent;
|
||||||
Length::from(self.baseline_offset_in_parent);
|
Length::from(baseline - self.baseline_offset_in_item)
|
||||||
baseline - Length::from(self.baseline_offset_in_item)
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ use crate::flow::float::{
|
||||||
};
|
};
|
||||||
use crate::flow::inline::InlineFormattingContext;
|
use crate::flow::inline::InlineFormattingContext;
|
||||||
use crate::formatting_contexts::{
|
use crate::formatting_contexts::{
|
||||||
IndependentFormattingContext, IndependentLayout, NonReplacedFormattingContext,
|
Baselines, IndependentFormattingContext, IndependentLayout, NonReplacedFormattingContext,
|
||||||
};
|
};
|
||||||
use crate::fragment_tree::{
|
use crate::fragment_tree::{
|
||||||
BaseFragmentInfo, BoxFragment, CollapsedBlockMargins, CollapsedMargin, Fragment,
|
BaseFragmentInfo, BoxFragment, CollapsedBlockMargins, CollapsedMargin, Fragment,
|
||||||
|
@ -198,10 +198,10 @@ struct FlowLayout {
|
||||||
pub fragments: Vec<Fragment>,
|
pub fragments: Vec<Fragment>,
|
||||||
pub content_block_size: Length,
|
pub content_block_size: Length,
|
||||||
pub collapsible_margins_in_children: CollapsedBlockMargins,
|
pub collapsible_margins_in_children: CollapsedBlockMargins,
|
||||||
/// The offset of the last inflow baseline in this layout in the content area, if
|
/// The offset of the baselines in this layout in the content area, if there were some. This is
|
||||||
/// there was one. This is used to propagate inflow baselines to the ancestors
|
/// used to propagate inflow baselines to the ancestors of `display: inline-block` elements
|
||||||
/// of `display: inline-block` elements.
|
/// and table content.
|
||||||
pub last_inflow_baseline_offset: Option<Length>,
|
pub baselines: Baselines,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
|
@ -248,7 +248,7 @@ impl BlockFormattingContext {
|
||||||
flow_layout.collapsible_margins_in_children.end.solve() +
|
flow_layout.collapsible_margins_in_children.end.solve() +
|
||||||
clearance.unwrap_or_else(Au::zero).into())
|
clearance.unwrap_or_else(Au::zero).into())
|
||||||
.into(),
|
.into(),
|
||||||
last_inflow_baseline_offset: flow_layout.last_inflow_baseline_offset.map(|t| t.into()),
|
baselines: flow_layout.baselines,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -476,13 +476,12 @@ fn layout_block_level_children_in_parallel(
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let (content_block_size, collapsible_margins_in_children, baseline_offset) =
|
let (content_block_size, collapsible_margins_in_children, baselines) = placement_state.finish();
|
||||||
placement_state.finish();
|
|
||||||
FlowLayout {
|
FlowLayout {
|
||||||
fragments,
|
fragments,
|
||||||
content_block_size,
|
content_block_size,
|
||||||
collapsible_margins_in_children,
|
collapsible_margins_in_children,
|
||||||
last_inflow_baseline_offset: baseline_offset,
|
baselines,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -524,13 +523,12 @@ fn layout_block_level_children_sequentially(
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let (content_block_size, collapsible_margins_in_children, baseline_offset) =
|
let (content_block_size, collapsible_margins_in_children, baselines) = placement_state.finish();
|
||||||
placement_state.finish();
|
|
||||||
FlowLayout {
|
FlowLayout {
|
||||||
fragments,
|
fragments,
|
||||||
content_block_size,
|
content_block_size,
|
||||||
collapsible_margins_in_children,
|
collapsible_margins_in_children,
|
||||||
last_inflow_baseline_offset: baseline_offset,
|
baselines,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -818,9 +816,9 @@ fn layout_in_flow_non_replaced_block_level_same_formatting_context(
|
||||||
pbm.border,
|
pbm.border,
|
||||||
margin,
|
margin,
|
||||||
clearance.map(|t| t.into()),
|
clearance.map(|t| t.into()),
|
||||||
flow_layout.last_inflow_baseline_offset,
|
|
||||||
block_margins_collapsed_with_children,
|
block_margins_collapsed_with_children,
|
||||||
)
|
)
|
||||||
|
.with_baselines(flow_layout.baselines)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NonReplacedFormattingContext {
|
impl NonReplacedFormattingContext {
|
||||||
|
@ -893,9 +891,9 @@ impl NonReplacedFormattingContext {
|
||||||
pbm.border,
|
pbm.border,
|
||||||
margin,
|
margin,
|
||||||
None, /* clearance */
|
None, /* clearance */
|
||||||
layout.last_inflow_baseline_offset.map(|t| t.into()),
|
|
||||||
block_margins_collapsed_with_children,
|
block_margins_collapsed_with_children,
|
||||||
)
|
)
|
||||||
|
.with_baselines(layout.baselines)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Lay out a normal in flow non-replaced block that establishes an independent
|
/// Lay out a normal in flow non-replaced block that establishes an independent
|
||||||
|
@ -1114,9 +1112,9 @@ impl NonReplacedFormattingContext {
|
||||||
pbm.border,
|
pbm.border,
|
||||||
margin,
|
margin,
|
||||||
clearance,
|
clearance,
|
||||||
layout.last_inflow_baseline_offset.map(|t| t.into()),
|
|
||||||
block_margins_collapsed_with_children,
|
block_margins_collapsed_with_children,
|
||||||
)
|
)
|
||||||
|
.with_baselines(layout.baselines)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1211,7 +1209,6 @@ fn layout_in_flow_replaced_block_level<'a>(
|
||||||
pbm.border,
|
pbm.border,
|
||||||
margin,
|
margin,
|
||||||
clearance,
|
clearance,
|
||||||
None, /* last_inflow_base_offset */
|
|
||||||
block_margins_collapsed_with_children,
|
block_margins_collapsed_with_children,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1389,7 +1386,7 @@ struct PlacementState {
|
||||||
start_margin: CollapsedMargin,
|
start_margin: CollapsedMargin,
|
||||||
current_margin: CollapsedMargin,
|
current_margin: CollapsedMargin,
|
||||||
current_block_direction_position: Length,
|
current_block_direction_position: Length,
|
||||||
last_inflow_baseline_offset: Option<Length>,
|
inflow_baselines: Baselines,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PlacementState {
|
impl PlacementState {
|
||||||
|
@ -1403,7 +1400,7 @@ impl PlacementState {
|
||||||
start_margin: CollapsedMargin::zero(),
|
start_margin: CollapsedMargin::zero(),
|
||||||
current_margin: CollapsedMargin::zero(),
|
current_margin: CollapsedMargin::zero(),
|
||||||
current_block_direction_position: Length::zero(),
|
current_block_direction_position: Length::zero(),
|
||||||
last_inflow_baseline_offset: None,
|
inflow_baselines: Baselines::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1414,14 +1411,15 @@ impl PlacementState {
|
||||||
) {
|
) {
|
||||||
self.place_fragment(fragment, sequential_layout_state);
|
self.place_fragment(fragment, sequential_layout_state);
|
||||||
|
|
||||||
match fragment {
|
if let Fragment::Box(box_fragment) = fragment {
|
||||||
Fragment::Box(box_fragment) => {
|
let box_block_offset = box_fragment.content_rect.start_corner.block.into();
|
||||||
if let Some(baseline) = box_fragment.last_baseline_offset {
|
match (self.inflow_baselines.first, box_fragment.baselines.first) {
|
||||||
self.last_inflow_baseline_offset =
|
(None, Some(first)) => self.inflow_baselines.first = Some(first + box_block_offset),
|
||||||
Some(baseline + box_fragment.content_rect.start_corner.block);
|
_ => {},
|
||||||
}
|
}
|
||||||
},
|
if let Some(last) = box_fragment.baselines.last {
|
||||||
_ => {},
|
self.inflow_baselines.last = Some(last + box_block_offset);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1511,7 +1509,7 @@ impl PlacementState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish(mut self) -> (Length, CollapsedBlockMargins, Option<Length>) {
|
fn finish(mut self) -> (Length, CollapsedBlockMargins, Baselines) {
|
||||||
if !self.last_in_flow_margin_collapses_with_parent_end_margin {
|
if !self.last_in_flow_margin_collapses_with_parent_end_margin {
|
||||||
self.current_block_direction_position += self.current_margin.solve();
|
self.current_block_direction_position += self.current_margin.solve();
|
||||||
self.current_margin = CollapsedMargin::zero();
|
self.current_margin = CollapsedMargin::zero();
|
||||||
|
@ -1523,7 +1521,7 @@ impl PlacementState {
|
||||||
start: self.start_margin,
|
start: self.start_margin,
|
||||||
end: self.current_margin,
|
end: self.current_margin,
|
||||||
},
|
},
|
||||||
self.last_inflow_baseline_offset,
|
self.inflow_baselines,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,6 +59,14 @@ pub(crate) enum NonReplacedFormattingContextContents {
|
||||||
// Other layout modes go here
|
// Other layout modes go here
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The baselines of a layout or a [`BoxFragment`]. Some layout uses the first and some layout uses
|
||||||
|
/// the last.
|
||||||
|
#[derive(Default, Serialize)]
|
||||||
|
pub(crate) struct Baselines {
|
||||||
|
pub first: Option<Au>,
|
||||||
|
pub last: Option<Au>,
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) struct IndependentLayout {
|
pub(crate) struct IndependentLayout {
|
||||||
pub fragments: Vec<Fragment>,
|
pub fragments: Vec<Fragment>,
|
||||||
|
|
||||||
|
@ -68,7 +76,7 @@ pub(crate) struct IndependentLayout {
|
||||||
/// The offset of the last inflow baseline of this layout in the content area, if
|
/// The offset of the last inflow baseline of this layout in the content area, if
|
||||||
/// there was one. This is used to propagate baselines to the ancestors of `display:
|
/// there was one. This is used to propagate baselines to the ancestors of `display:
|
||||||
/// inline-block`.
|
/// inline-block`.
|
||||||
pub last_inflow_baseline_offset: Option<Au>,
|
pub baselines: Baselines,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IndependentFormattingContext {
|
impl IndependentFormattingContext {
|
||||||
|
|
|
@ -13,6 +13,7 @@ use style::Zero;
|
||||||
|
|
||||||
use super::{BaseFragment, BaseFragmentInfo, CollapsedBlockMargins, Fragment};
|
use super::{BaseFragment, BaseFragmentInfo, CollapsedBlockMargins, Fragment};
|
||||||
use crate::cell::ArcRefCell;
|
use crate::cell::ArcRefCell;
|
||||||
|
use crate::formatting_contexts::Baselines;
|
||||||
use crate::geom::{
|
use crate::geom::{
|
||||||
LengthOrAuto, LogicalRect, LogicalSides, PhysicalPoint, PhysicalRect, PhysicalSides,
|
LengthOrAuto, LogicalRect, LogicalSides, PhysicalPoint, PhysicalRect, PhysicalSides,
|
||||||
PhysicalSize,
|
PhysicalSize,
|
||||||
|
@ -44,10 +45,10 @@ pub(crate) struct BoxFragment {
|
||||||
/// <https://drafts.csswg.org/css2/#clearance>
|
/// <https://drafts.csswg.org/css2/#clearance>
|
||||||
pub clearance: Option<Length>,
|
pub clearance: Option<Length>,
|
||||||
|
|
||||||
/// When this box contains an inline formatting context, this tracks the baseline
|
/// When this [`BoxFragment`] is for content that has a baseline, this tracks
|
||||||
/// offset of the last inflow line. This offset is used to propagate baselines to
|
/// the first and last baselines of that content. This is used to propagate baselines
|
||||||
/// ancestors of `display: inline-block` ancestors.
|
/// to things such as tables and inline formatting contexts.
|
||||||
pub last_baseline_offset: Option<Length>,
|
pub baselines: Baselines,
|
||||||
|
|
||||||
pub block_margins_collapsed_with_children: CollapsedBlockMargins,
|
pub block_margins_collapsed_with_children: CollapsedBlockMargins,
|
||||||
|
|
||||||
|
@ -73,7 +74,6 @@ impl BoxFragment {
|
||||||
border: LogicalSides<Length>,
|
border: LogicalSides<Length>,
|
||||||
margin: LogicalSides<Length>,
|
margin: LogicalSides<Length>,
|
||||||
clearance: Option<Length>,
|
clearance: Option<Length>,
|
||||||
last_inflow_baseline_offset: Option<Length>,
|
|
||||||
block_margins_collapsed_with_children: CollapsedBlockMargins,
|
block_margins_collapsed_with_children: CollapsedBlockMargins,
|
||||||
) -> BoxFragment {
|
) -> BoxFragment {
|
||||||
let position = style.get_box().position;
|
let position = style.get_box().position;
|
||||||
|
@ -94,7 +94,6 @@ impl BoxFragment {
|
||||||
border,
|
border,
|
||||||
margin,
|
margin,
|
||||||
clearance,
|
clearance,
|
||||||
last_inflow_baseline_offset,
|
|
||||||
block_margins_collapsed_with_children,
|
block_margins_collapsed_with_children,
|
||||||
PhysicalSize::new(width_overconstrained, height_overconstrained),
|
PhysicalSize::new(width_overconstrained, height_overconstrained),
|
||||||
)
|
)
|
||||||
|
@ -109,7 +108,6 @@ impl BoxFragment {
|
||||||
border: LogicalSides<Length>,
|
border: LogicalSides<Length>,
|
||||||
margin: LogicalSides<Length>,
|
margin: LogicalSides<Length>,
|
||||||
clearance: Option<Length>,
|
clearance: Option<Length>,
|
||||||
mut last_inflow_baseline_offset: Option<Length>,
|
|
||||||
block_margins_collapsed_with_children: CollapsedBlockMargins,
|
block_margins_collapsed_with_children: CollapsedBlockMargins,
|
||||||
overconstrained: PhysicalSize<bool>,
|
overconstrained: PhysicalSize<bool>,
|
||||||
) -> BoxFragment {
|
) -> BoxFragment {
|
||||||
|
@ -126,10 +124,15 @@ impl BoxFragment {
|
||||||
// > value) a block-level or inline-level block container that is a scroll container
|
// > value) a block-level or inline-level block container that is a scroll container
|
||||||
// > always has a last baseline set, whose baselines all correspond to its block-end
|
// > always has a last baseline set, whose baselines all correspond to its block-end
|
||||||
// > margin edge.
|
// > margin edge.
|
||||||
|
//
|
||||||
|
// This applies even if there is no baseline set, so we unconditionally set the value here
|
||||||
|
// and ignore anything that is set via [`Self::with_baselines`].
|
||||||
|
let mut baselines = Baselines::default();
|
||||||
if style.establishes_scroll_container() {
|
if style.establishes_scroll_container() {
|
||||||
last_inflow_baseline_offset = Some(
|
baselines.last = Some(
|
||||||
content_rect.size.block + padding.block_end + border.block_end + margin.block_end,
|
(content_rect.size.block + padding.block_end + border.block_end + margin.block_end)
|
||||||
);
|
.into(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
BoxFragment {
|
BoxFragment {
|
||||||
|
@ -141,7 +144,7 @@ impl BoxFragment {
|
||||||
border,
|
border,
|
||||||
margin,
|
margin,
|
||||||
clearance,
|
clearance,
|
||||||
last_baseline_offset: last_inflow_baseline_offset,
|
baselines,
|
||||||
block_margins_collapsed_with_children,
|
block_margins_collapsed_with_children,
|
||||||
scrollable_overflow_from_children,
|
scrollable_overflow_from_children,
|
||||||
overconstrained,
|
overconstrained,
|
||||||
|
@ -149,6 +152,19 @@ impl BoxFragment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn with_baselines(mut self, baselines: Baselines) -> Self {
|
||||||
|
// From the https://drafts.csswg.org/css-align-3/#baseline-export section on "block containers":
|
||||||
|
// > However, for legacy reasons if its baseline-source is auto (the initial
|
||||||
|
// > value) a block-level or inline-level block container that is a scroll container
|
||||||
|
// > always has a last baseline set, whose baselines all correspond to its block-end
|
||||||
|
// > margin edge.
|
||||||
|
if !self.style.establishes_scroll_container() {
|
||||||
|
self.baselines.last = baselines.last;
|
||||||
|
}
|
||||||
|
self.baselines.first = baselines.first;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn scrollable_overflow(
|
pub fn scrollable_overflow(
|
||||||
&self,
|
&self,
|
||||||
containing_block: &PhysicalRect<Length>,
|
containing_block: &PhysicalRect<Length>,
|
||||||
|
|
|
@ -706,7 +706,6 @@ impl HoistedAbsolutelyPositionedBox {
|
||||||
None, /* clearance */
|
None, /* clearance */
|
||||||
// We do not set the baseline offset, because absolutely positioned
|
// We do not set the baseline offset, because absolutely positioned
|
||||||
// elements are not inflow.
|
// elements are not inflow.
|
||||||
None, /* last_inflow_baseline_offset */
|
|
||||||
CollapsedBlockMargins::zero(),
|
CollapsedBlockMargins::zero(),
|
||||||
physical_overconstrained,
|
physical_overconstrained,
|
||||||
)
|
)
|
||||||
|
|
|
@ -12,7 +12,7 @@ use style::values::generics::length::GenericLengthPercentageOrAuto::{Auto, Lengt
|
||||||
|
|
||||||
use super::{Table, TableSlot, TableSlotCell};
|
use super::{Table, TableSlot, TableSlotCell};
|
||||||
use crate::context::LayoutContext;
|
use crate::context::LayoutContext;
|
||||||
use crate::formatting_contexts::IndependentLayout;
|
use crate::formatting_contexts::{Baselines, IndependentLayout};
|
||||||
use crate::fragment_tree::{BoxFragment, CollapsedBlockMargins, Fragment};
|
use crate::fragment_tree::{BoxFragment, CollapsedBlockMargins, Fragment};
|
||||||
use crate::geom::{LogicalRect, LogicalSides, LogicalVec2};
|
use crate::geom::{LogicalRect, LogicalSides, LogicalVec2};
|
||||||
use crate::positioned::{PositioningContext, PositioningContextLength};
|
use crate::positioned::{PositioningContext, PositioningContextLength};
|
||||||
|
@ -1105,7 +1105,7 @@ impl Table {
|
||||||
IndependentLayout {
|
IndependentLayout {
|
||||||
fragments,
|
fragments,
|
||||||
content_block_size,
|
content_block_size,
|
||||||
last_inflow_baseline_offset: None,
|
baselines: Baselines::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1167,11 +1167,8 @@ impl TableSlotCell {
|
||||||
layout.border,
|
layout.border,
|
||||||
LogicalSides::zero(), /* margin */
|
LogicalSides::zero(), /* margin */
|
||||||
None, /* clearance */
|
None, /* clearance */
|
||||||
layout
|
|
||||||
.layout
|
|
||||||
.last_inflow_baseline_offset
|
|
||||||
.map(|baseline| baseline.into()),
|
|
||||||
CollapsedBlockMargins::zero(),
|
CollapsedBlockMargins::zero(),
|
||||||
)
|
)
|
||||||
|
.with_baselines(layout.layout.baselines)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
[position-relative-032.xht]
|
||||||
|
expected: FAIL
|
Loading…
Add table
Add a link
Reference in a new issue