diff --git a/components/layout_2020/flow/float.rs b/components/layout_2020/flow/float.rs index 367c73c59d4..bf2c468e07d 100644 --- a/components/layout_2020/flow/float.rs +++ b/components/layout_2020/flow/float.rs @@ -26,11 +26,11 @@ use crate::context::LayoutContext; use crate::dom::NodeExt; use crate::dom_traversal::{Contents, NodeAndStyleInfo}; use crate::formatting_contexts::IndependentFormattingContext; -use crate::fragment_tree::{BoxFragment, CollapsedBlockMargins, CollapsedMargin}; -use crate::geom::{LogicalRect, LogicalVec2, PhysicalPoint, PhysicalRect, Size, ToLogical}; +use crate::fragment_tree::{BoxFragment, CollapsedMargin}; +use crate::geom::{LogicalRect, LogicalVec2, ToLogical}; use crate::positioned::{relative_adjustement, PositioningContext}; -use crate::style_ext::{Clamp, ComputedValuesExt, DisplayInside, PaddingBorderMargin}; -use crate::{AuOrAuto, ContainingBlock, IndefiniteContainingBlock}; +use crate::style_ext::{DisplayInside, PaddingBorderMargin}; +use crate::ContainingBlock; /// A floating box. #[derive(Debug, Serialize)] @@ -902,145 +902,13 @@ impl FloatBox { containing_block, &style, |positioning_context| { - // Margin is computed this way regardless of whether the element is replaced - // or non-replaced. - let pbm = style.padding_border_margin(containing_block); - let margin = pbm.margin.auto_is(Au::zero); - let pbm_sums = pbm.padding + pbm.border + margin; - - let (content_size, children); - match self.contents { - IndependentFormattingContext::NonReplaced(ref mut non_replaced) => { - // Calculate inline size. - // https://drafts.csswg.org/css2/#float-width - let style = non_replaced.style.clone(); - let box_size = style.content_box_size(containing_block, &pbm); - let max_box_size = style.content_max_box_size(containing_block, &pbm); - let min_box_size = style.content_min_box_size(containing_block, &pbm); - let available_inline_size = - containing_block.inline_size - pbm_sums.inline_sum(); - let available_block_size = 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(|size| { - 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); - size.clamp_between_extremums(min_block_size, max_block_size) - }) - .map_or(AuOrAuto::Auto, AuOrAuto::LengthPercentage); - 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_context, - &containing_block_for_children, - ) - }; - let tentative_inline_size = box_size.inline.resolve( - Size::fit_content, - available_inline_size, - &mut get_content_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); - - // Calculate block size. - // https://drafts.csswg.org/css2/#block-root-margin - // FIXME(pcwalton): Is a tree rank of zero correct here? - let containing_block_for_children = ContainingBlock { - inline_size, - block_size: tentative_block_size, - style: &non_replaced.style, - }; - let independent_layout = non_replaced.layout( - layout_context, - positioning_context, - &containing_block_for_children, - containing_block, - ); - let (block_size, inline_size) = - match independent_layout.content_inline_size_for_table { - Some(inline_size) => { - (independent_layout.content_block_size, inline_size) - }, - None => { - 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); - (block_size, inline_size) - }, - }; - content_size = LogicalVec2 { - inline: inline_size, - block: block_size, - }; - children = independent_layout.fragments; - }, - IndependentFormattingContext::Replaced(ref replaced) => { - // https://drafts.csswg.org/css2/#float-replaced-width - // https://drafts.csswg.org/css2/#inline-replaced-height - content_size = replaced.contents.used_size_as_if_inline_element( - containing_block, - &replaced.style, - &pbm, - ); - children = replaced.contents.make_fragments( - &replaced.style, - containing_block, - content_size.to_physical_size(containing_block.style.writing_mode), - ) - }, - }; - - let containing_block_writing_mode = containing_block.style.writing_mode; - let content_rect = PhysicalRect::new( - PhysicalPoint::zero(), - content_size.to_physical_size(containing_block_writing_mode), - ); - - BoxFragment::new( - self.contents.base_fragment_info(), - style.clone(), - children, - content_rect, - pbm.padding.to_physical(containing_block_writing_mode), - pbm.border.to_physical(containing_block_writing_mode), - margin.to_physical(containing_block_writing_mode), - // Clearance is handled internally by the float placement logic, so there's no need - // to store it explicitly in the fragment. - None, // clearance - CollapsedBlockMargins::zero(), - ) + self.contents + .layout_float_or_atomic_inline( + layout_context, + positioning_context, + containing_block, + ) + .fragment }, ) } diff --git a/components/layout_2020/flow/inline/mod.rs b/components/layout_2020/flow/inline/mod.rs index d2890234da8..1d5627e1d6c 100644 --- a/components/layout_2020/flow/inline/mod.rs +++ b/components/layout_2020/flow/inline/mod.rs @@ -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, diff --git a/components/layout_2020/flow/mod.rs b/components/layout_2020/flow/mod.rs index 3ef8123cc0f..5e9693b63fd 100644 --- a/components/layout_2020/flow/mod.rs +++ b/components/layout_2020/flow/mod.rs @@ -14,7 +14,7 @@ use style::computed_values::clear::T as Clear; use style::computed_values::float::T as Float; use style::properties::ComputedValues; use style::servo::selector_parser::PseudoElement; -use style::values::computed::Size; +use style::values::computed::Size as StyleSize; use style::values::specified::align::AlignFlags; use style::values::specified::{Display, TextAlignKeyword}; use style::Zero; @@ -25,14 +25,15 @@ use crate::flow::float::{ ContainingBlockPositionInfo, FloatBox, PlacementAmongFloats, SequentialLayoutState, }; use crate::formatting_contexts::{ - Baselines, IndependentFormattingContext, IndependentLayout, NonReplacedFormattingContext, + Baselines, IndependentFormattingContext, IndependentLayout, IndependentLayoutResult, + NonReplacedFormattingContext, }; use crate::fragment_tree::{ BaseFragmentInfo, BoxFragment, CollapsedBlockMargins, CollapsedMargin, Fragment, FragmentFlags, }; use crate::geom::{ - AuOrAuto, LogicalRect, LogicalSides, LogicalVec2, PhysicalRect, PhysicalSides, ToLogical, - ToLogicalWithContainingBlock, + AuOrAuto, LogicalRect, LogicalSides, LogicalVec2, PhysicalPoint, PhysicalRect, PhysicalSides, + Size, ToLogical, ToLogicalWithContainingBlock, }; use crate::positioned::{AbsolutelyPositionedBox, PositioningContext, PositioningContextLength}; use crate::replaced::ReplacedContent; @@ -1904,13 +1905,182 @@ impl<'container> PlacementState<'container> { } } -fn block_size_is_zero_or_intrinsic(size: &Size, containing_block: &ContainingBlock) -> bool { +fn block_size_is_zero_or_intrinsic(size: &StyleSize, containing_block: &ContainingBlock) -> bool { match size { - Size::Auto | Size::MinContent | Size::MaxContent | Size::FitContent | Size::Stretch => true, - Size::LengthPercentage(ref lp) => { + StyleSize::Auto | + StyleSize::MinContent | + StyleSize::MaxContent | + StyleSize::FitContent | + StyleSize::Stretch => true, + StyleSize::LengthPercentage(ref lp) => { // TODO: Should this resolve definite percentages? Blink does it, Gecko and WebKit don't. lp.is_definitely_zero() || (lp.0.has_percentage() && containing_block.block_size.is_auto()) }, } } + +impl IndependentFormattingContext { + pub(crate) fn layout_float_or_atomic_inline( + &mut self, + layout_context: &LayoutContext, + child_positioning_context: &mut PositioningContext, + containing_block: &ContainingBlock, + ) -> IndependentLayoutResult { + let style = self.style(); + let container_writing_mode = containing_block.style.writing_mode; + let pbm = style.padding_border_margin(containing_block); + let margin = pbm.margin.auto_is(Au::zero); + let pbm_sums = pbm.padding + pbm.border + margin; + + let (fragments, content_rect, baselines) = match self { + IndependentFormattingContext::Replaced(replaced) => { + // https://drafts.csswg.org/css2/visudet.html#float-replaced-width + // https://drafts.csswg.org/css2/visudet.html#inline-replaced-height + let content_size = replaced + .contents + .used_size_as_if_inline_element(containing_block, &replaced.style, &pbm) + .to_physical_size(container_writing_mode); + let fragments = replaced.contents.make_fragments( + &replaced.style, + containing_block, + content_size, + ); + + let content_rect = PhysicalRect::new(PhysicalPoint::zero(), content_size); + (fragments, content_rect, None) + }, + IndependentFormattingContext::NonReplaced(non_replaced) => { + let style = non_replaced.style.clone(); + let box_size = style.content_box_size(containing_block, &pbm); + let max_box_size = style.content_max_box_size(containing_block, &pbm); + let min_box_size = style.content_min_box_size(containing_block, &pbm); + + let available_inline_size = containing_block.inline_size - pbm_sums.inline_sum(); + let available_block_size = 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(|size| { + 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); + size.clamp_between_extremums(min_block_size, max_block_size) + }) + .map_or(AuOrAuto::Auto, AuOrAuto::LengthPercentage); + + 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_context, &containing_block_for_children) + }; + + // https://drafts.csswg.org/css2/visudet.html#float-width + // 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: &style, + }; + assert_eq!( + container_writing_mode.is_horizontal(), + style.writing_mode.is_horizontal(), + "Mixed horizontal and vertical writing modes are not supported yet" + ); + + let independent_layout = non_replaced.layout( + layout_context, + child_positioning_context, + &containing_block_for_children, + 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_size = LogicalVec2 { + block: block_size, + inline: inline_size, + } + .to_physical_size(container_writing_mode); + let content_rect = PhysicalRect::new(PhysicalPoint::zero(), content_size); + + ( + independent_layout.fragments, + content_rect, + Some(independent_layout.baselines), + ) + }, + }; + + 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), + // Floats can have clearance, but it's handled internally by the float placement logic, + // so there's no need to store it explicitly in the fragment. + // And atomic inlines don't have clearance. + None, /* clearance */ + CollapsedBlockMargins::zero(), + ); + + IndependentLayoutResult { + fragment, + baselines, + pbm_sums, + } + } +} diff --git a/components/layout_2020/formatting_contexts.rs b/components/layout_2020/formatting_contexts.rs index 507092cddcf..7064eb72372 100644 --- a/components/layout_2020/formatting_contexts.rs +++ b/components/layout_2020/formatting_contexts.rs @@ -14,7 +14,8 @@ use crate::dom::NodeExt; use crate::dom_traversal::{Contents, NodeAndStyleInfo}; use crate::flexbox::FlexContainer; use crate::flow::BlockFormattingContext; -use crate::fragment_tree::{BaseFragmentInfo, Fragment, FragmentFlags}; +use crate::fragment_tree::{BaseFragmentInfo, BoxFragment, Fragment, FragmentFlags}; +use crate::geom::LogicalSides; use crate::positioned::PositioningContext; use crate::replaced::ReplacedContent; use crate::sizing::{self, ContentSizes}; @@ -91,6 +92,12 @@ pub(crate) struct IndependentLayout { pub baselines: Baselines, } +pub(crate) struct IndependentLayoutResult { + pub fragment: BoxFragment, + pub baselines: Option, + pub pbm_sums: LogicalSides, +} + impl IndependentFormattingContext { pub fn construct<'dom, Node: NodeExt<'dom>>( context: &LayoutContext,