From 8fe37f3ed69d24d7da167c4b871d2e6b7629df6f Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Mon, 2 Dec 2019 17:36:33 +0100 Subject: [PATCH 01/21] Implement "outer min/max-content" (based on a given min/max-content) https://dbaron.org/css/intrinsic/#outer-intrinsic --- components/layout_2020/intrinsic.rs | 69 ++++++++++++++++++++++ components/style/values/computed/length.rs | 10 ++++ components/style/values/generics/length.rs | 2 +- 3 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 components/layout_2020/intrinsic.rs diff --git a/components/layout_2020/intrinsic.rs b/components/layout_2020/intrinsic.rs new file mode 100644 index 00000000000..1b811928f86 --- /dev/null +++ b/components/layout_2020/intrinsic.rs @@ -0,0 +1,69 @@ +use crate::style_ext::ComputedValuesExt; +use style::Zero; +use style::properties::ComputedValues; +use style::values::computed::{Length, LengthPercentage}; + +#[derive(Debug, Clone)] +pub(crate) struct IntrinsicSizes { + pub min_content: Length, + pub max_content: Length, +} + +impl IntrinsicSizes { + pub fn zero() -> Self { + Self { + min_content: Length::zero(), + max_content: Length::zero(), + } + } +} + +/// https://dbaron.org/css/intrinsic/#outer-intrinsic +pub(crate) fn outer_intrinsic_inline_sizes( + style: &ComputedValues, + get_inner_intrinsic_sizes: &dyn Fn() -> IntrinsicSizes, +) -> IntrinsicSizes { + // FIXME: account for 'min-width', 'max-width', 'box-sizing' + + let specified = style.box_size().inline; + // Percentages for 'width' are treated as 'auto' + let specified = specified.map(|lp| lp.as_length()); + // The (inner) min/max-content are only used for 'auto' + let mut outer = match specified.non_auto().flatten() { + None => get_inner_intrinsic_sizes(), + Some(length) => IntrinsicSizes { + min_content: length, + max_content: length, + } + }; + + let mut bpm_lengths = Length::zero(); + let mut bpm_percentages = 0.; + let padding = style.padding(); + let border = style.border_width(); + let margin = style.margin(); + bpm_lengths += border.inline_start; + bpm_lengths += border.inline_end; + let mut add = |x: LengthPercentage| { + bpm_lengths += x.length_component(); + bpm_percentages += x.percentage(); + }; + add(padding.inline_start); + add(padding.inline_end); + margin.inline_start.non_auto().map(&mut add); + margin.inline_end.non_auto().map(&mut add); + + outer.min_content += bpm_lengths; + outer.max_content += bpm_lengths; + // " Note that this may yield an infinite result, but undefined results + // (zero divided by zero) must be treated as zero. " + if outer.max_content.px() == 0. { + // Avoid a potential `NaN`. + // Zero is already the result we want regardless of `denominator`. + } else { + let denominator = (1. - bpm_percentages).max(0.); + outer.max_content = Length::new(outer.max_content.px() / denominator); + } + + outer +} diff --git a/components/style/values/computed/length.rs b/components/style/values/computed/length.rs index 53cf7cbd696..59a3131142c 100644 --- a/components/style/values/computed/length.rs +++ b/components/style/values/computed/length.rs @@ -186,6 +186,16 @@ impl LengthPercentage { } } + /// Returns the length component if this could be represented as a + /// non-calc length. + pub fn as_length(&self) -> Option { + if !self.has_percentage { + Some(self.length_component()) + } else { + None + } + } + /// Returns the percentage component if this could be represented as a /// non-calc percentage. pub fn as_percentage(&self) -> Option { diff --git a/components/style/values/generics/length.rs b/components/style/values/generics/length.rs index d9cc2396f38..4183f40a942 100644 --- a/components/style/values/generics/length.rs +++ b/components/style/values/generics/length.rs @@ -92,7 +92,7 @@ where } /// Maps the length of this value. - pub fn map(&self, f: impl FnOnce(LengthPercentage) -> LengthPercentage) -> Self { + pub fn map(&self, f: impl FnOnce(LengthPercentage) -> T) -> LengthPercentageOrAuto { match self { LengthPercentageOrAuto::LengthPercentage(l) => { LengthPercentageOrAuto::LengthPercentage(f(l.clone())) From 9c5a59504460cd291f21fc5db747bbf8cefe84eb Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Mon, 2 Dec 2019 22:11:54 +0100 Subject: [PATCH 02/21] Add intrinsic size computation for inline formatting contexts --- components/layout_2020/flow/construct.rs | 1 + components/layout_2020/flow/inline.rs | 172 +++++++++++++++--- components/layout_2020/intrinsic.rs | 48 ++--- components/layout_2020/lib.rs | 1 + components/style/values/computed/length.rs | 6 + .../style/values/computed/percentage.rs | 6 + 6 files changed, 188 insertions(+), 46 deletions(-) diff --git a/components/layout_2020/flow/construct.rs b/components/layout_2020/flow/construct.rs index e7599bc843a..5ac85dd47d9 100644 --- a/components/layout_2020/flow/construct.rs +++ b/components/layout_2020/flow/construct.rs @@ -127,6 +127,7 @@ impl BlockContainer { context: &SharedStyleContext<'style>, block_container_style: &Arc, contents: NonReplacedContents>, + // intrinsic_sizes_requested: bool, ) -> (BlockContainer, ContainsFloats) { let mut builder = BlockContainerBuilder { context, diff --git a/components/layout_2020/flow/inline.rs b/components/layout_2020/flow/inline.rs index 33d1d8a0ed9..ccb87ff876a 100644 --- a/components/layout_2020/flow/inline.rs +++ b/components/layout_2020/flow/inline.rs @@ -9,13 +9,17 @@ use crate::formatting_contexts::IndependentFormattingContext; use crate::fragments::CollapsedBlockMargins; use crate::fragments::{AnonymousFragment, BoxFragment, Fragment, TextFragment}; use crate::geom::flow_relative::{Rect, Sides, Vec2}; +use crate::intrinsic::{outer_intrinsic_inline_sizes, IntrinsicSizes}; use crate::positioned::{AbsolutelyPositionedBox, AbsolutelyPositionedFragment}; use crate::style_ext::{ComputedValuesExt, Display, DisplayGeneratingBox, DisplayOutside}; use crate::{relative_adjustement, ContainingBlock}; +use app_units::Au; +use gfx::text::text_run::GlyphRun; use servo_arc::Arc; use style::properties::ComputedValues; -use style::values::computed::Length; +use style::values::computed::{Length, Percentage}; use style::Zero; +use webrender_api::FontInstanceKey; #[derive(Debug, Default)] pub(crate) struct InlineFormattingContext { @@ -77,6 +81,119 @@ struct LinesBoxes { } impl InlineFormattingContext { + // This works on an already-constructed `InlineFormattingContext`, + // Which would have to change if/when + // `BlockContainer::construct` parallelize their construction. + #[allow(unused)] + pub(super) fn intrinsic_sizes(&self, layout_context: &LayoutContext) -> IntrinsicSizes { + struct Computation { + paragraph: IntrinsicSizes, + current_line: IntrinsicSizes, + current_line_percentages: Percentage, + } + impl Computation { + fn traverse( + &mut self, + layout_context: &LayoutContext, + inline_level_boxes: &[Arc], + ) { + for inline_level_box in inline_level_boxes { + match &**inline_level_box { + InlineLevelBox::InlineBox(inline_box) => { + let padding = inline_box.style.padding(); + let border = inline_box.style.border_width(); + let margin = inline_box.style.margin(); + macro_rules! add_length { + ($x: expr) => {{ + let length = $x; + self.current_line.min_content += length; + self.current_line.max_content += length; + }}; + } + macro_rules! add_lengthpercentage { + ($x: expr) => {{ + add_length!($x.length_component()); + self.current_line_percentages += $x.percentage_component(); + }}; + } + macro_rules! add { + ($condition: ident, $side: ident) => { + if inline_box.$condition { + add_lengthpercentage!(padding.$side); + add_length!(border.$side); + margin.$side.non_auto().map(|x| add_lengthpercentage!(x)); + } + }; + } + + add!(first_fragment, inline_start); + self.traverse(layout_context, &inline_box.children); + add!(last_fragment, inline_end); + }, + InlineLevelBox::TextRun(text_run) => { + let (_, _, _, runs, break_at_start) = + text_run.break_and_shape(layout_context); + if break_at_start { + self.line_break_opportunity() + } + for run in &runs { + let advance = Length::from(run.glyph_store.total_advance()); + if run.glyph_store.is_whitespace() { + self.line_break_opportunity() + } else { + self.current_line.min_content += advance + } + self.current_line.max_content += advance + } + }, + InlineLevelBox::Atomic(atomic) => { + let inner = || { + // atomic + // .intrinsic_inline_sizes + // .as_ref() + // .expect("Accessing intrinsic size that was not requested") + // .clone() + todo!() + }; + let (outer, pc) = outer_intrinsic_inline_sizes(&atomic.style, &inner); + self.current_line.min_content += outer.min_content; + self.current_line.max_content += outer.max_content; + self.current_line_percentages += pc; + }, + InlineLevelBox::OutOfFlowFloatBox(_) | + InlineLevelBox::OutOfFlowAbsolutelyPositionedBox(_) => {}, + } + } + } + + fn line_break_opportunity(&mut self) { + self.paragraph + .min_content + .max_assign(take(&mut self.current_line.min_content)); + } + + fn forced_line_break(&mut self) { + self.line_break_opportunity(); + self.current_line + .adjust_for_pbm_percentages(take(&mut self.current_line_percentages)); + self.paragraph + .max_content + .max_assign(take(&mut self.current_line.max_content)); + } + } + fn take(x: &mut T) -> T { + std::mem::replace(x, T::zero()) + } + let mut computation = Computation { + paragraph: IntrinsicSizes::zero(), + current_line: IntrinsicSizes::zero(), + current_line_percentages: Percentage::zero(), + }; + computation.traverse(layout_context, &self.inline_level_boxes); + computation.forced_line_break(); + computation.paragraph + } + pub(super) fn layout<'a>( &'a self, layout_context: &LayoutContext, @@ -283,11 +400,13 @@ impl<'box_tree> PartialInlineBoxFragment<'box_tree> { } impl TextRun { - fn layout(&self, layout_context: &LayoutContext, ifc: &mut InlineFormattingContextState) { + fn break_and_shape( + &self, + layout_context: &LayoutContext, + ) -> (Au, Au, FontInstanceKey, Vec, bool) { use gfx::font::ShapingFlags; use style::computed_values::text_rendering::T as TextRendering; use style::computed_values::word_break::T as WordBreak; - use style::values::generics::text::LineHeight; let font_style = self.parent_style.clone_font(); let inherited_text_style = self.parent_style.get_inherited_text(); @@ -316,30 +435,35 @@ impl TextRun { flags, }; - let (font_ascent, font_line_gap, font_key, runs) = - crate::context::with_thread_local_font_context(layout_context, |font_context| { - let font_group = font_context.font_group(font_style); - let font = font_group - .borrow_mut() - .first(font_context) - .expect("could not find font"); - let mut font = font.borrow_mut(); + crate::context::with_thread_local_font_context(layout_context, |font_context| { + let font_group = font_context.font_group(font_style); + let font = font_group + .borrow_mut() + .first(font_context) + .expect("could not find font"); + let mut font = font.borrow_mut(); - let (runs, _break_at_start) = gfx::text::text_run::TextRun::break_and_shape( - &mut font, - &self.text, - &shaping_options, - &mut None, - ); + let (runs, break_at_start) = gfx::text::text_run::TextRun::break_and_shape( + &mut font, + &self.text, + &shaping_options, + &mut None, + ); - ( - font.metrics.ascent, - font.metrics.line_gap, - font.font_key, - runs, - ) - }); + ( + font.metrics.ascent, + font.metrics.line_gap, + font.font_key, + runs, + break_at_start, + ) + }) + } + fn layout(&self, layout_context: &LayoutContext, ifc: &mut InlineFormattingContextState) { + use style::values::generics::text::LineHeight; + + let (font_ascent, font_line_gap, font_key, runs, _) = self.break_and_shape(layout_context); let font_size = self.parent_style.get_font().font_size.size.0; let mut runs = runs.iter(); loop { diff --git a/components/layout_2020/intrinsic.rs b/components/layout_2020/intrinsic.rs index 1b811928f86..e8df63ba630 100644 --- a/components/layout_2020/intrinsic.rs +++ b/components/layout_2020/intrinsic.rs @@ -1,7 +1,7 @@ use crate::style_ext::ComputedValuesExt; -use style::Zero; use style::properties::ComputedValues; -use style::values::computed::{Length, LengthPercentage}; +use style::values::computed::{Length, LengthPercentage, Percentage}; +use style::Zero; #[derive(Debug, Clone)] pub(crate) struct IntrinsicSizes { @@ -16,13 +16,26 @@ impl IntrinsicSizes { max_content: Length::zero(), } } + + /// 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 + // (zero divided by zero) must be treated as zero. " + if self.max_content.px() == 0. { + // Avoid a potential `NaN`. + // Zero is already the result we want regardless of `denominator`. + } else { + let denominator = (1. - percentages.0).max(0.); + self.max_content = Length::new(self.max_content.px() / denominator); + } + } } /// https://dbaron.org/css/intrinsic/#outer-intrinsic pub(crate) fn outer_intrinsic_inline_sizes( style: &ComputedValues, get_inner_intrinsic_sizes: &dyn Fn() -> IntrinsicSizes, -) -> IntrinsicSizes { +) -> (IntrinsicSizes, Percentage) { // FIXME: account for 'min-width', 'max-width', 'box-sizing' let specified = style.box_size().inline; @@ -34,36 +47,27 @@ pub(crate) fn outer_intrinsic_inline_sizes( Some(length) => IntrinsicSizes { min_content: length, max_content: length, - } + }, }; - let mut bpm_lengths = Length::zero(); - let mut bpm_percentages = 0.; + let mut pbm_lengths = Length::zero(); + let mut pbm_percentages = Percentage::zero(); let padding = style.padding(); let border = style.border_width(); let margin = style.margin(); - bpm_lengths += border.inline_start; - bpm_lengths += border.inline_end; + pbm_lengths += border.inline_start; + pbm_lengths += border.inline_end; let mut add = |x: LengthPercentage| { - bpm_lengths += x.length_component(); - bpm_percentages += x.percentage(); + pbm_lengths += x.length_component(); + pbm_percentages += x.percentage_component(); }; add(padding.inline_start); add(padding.inline_end); margin.inline_start.non_auto().map(&mut add); margin.inline_end.non_auto().map(&mut add); - outer.min_content += bpm_lengths; - outer.max_content += bpm_lengths; - // " Note that this may yield an infinite result, but undefined results - // (zero divided by zero) must be treated as zero. " - if outer.max_content.px() == 0. { - // Avoid a potential `NaN`. - // Zero is already the result we want regardless of `denominator`. - } else { - let denominator = (1. - bpm_percentages).max(0.); - outer.max_content = Length::new(outer.max_content.px() / denominator); - } + outer.min_content += pbm_lengths; + outer.max_content += pbm_lengths; - outer + (outer, pbm_percentages) } diff --git a/components/layout_2020/lib.rs b/components/layout_2020/lib.rs index 80254421e66..cb0b75a59f1 100644 --- a/components/layout_2020/lib.rs +++ b/components/layout_2020/lib.rs @@ -14,6 +14,7 @@ mod flow; mod formatting_contexts; mod fragments; mod geom; +mod intrinsic; mod opaque_node; mod positioned; pub mod query; diff --git a/components/style/values/computed/length.rs b/components/style/values/computed/length.rs index 59a3131142c..06631a354df 100644 --- a/components/style/values/computed/length.rs +++ b/components/style/values/computed/length.rs @@ -170,6 +170,12 @@ impl LengthPercentage { self.length } + /// Returns the percentage component of this `calc()` + #[inline] + pub fn percentage_component(&self) -> Percentage { + Percentage(self.clamping_mode.clamp(self.percentage.0)) + } + /// Return the percentage value as CSSFloat. #[inline] pub fn percentage(&self) -> CSSFloat { diff --git a/components/style/values/computed/percentage.rs b/components/style/values/computed/percentage.rs index b5a734367b2..7430d82d471 100644 --- a/components/style/values/computed/percentage.rs +++ b/components/style/values/computed/percentage.rs @@ -64,6 +64,12 @@ impl Zero for Percentage { } } +impl std::ops::AddAssign for Percentage { + fn add_assign(&mut self, other: Self) { + self.0 += other.0 + } +} + impl ToCss for Percentage { fn to_css(&self, dest: &mut CssWriter) -> fmt::Result where From 4e6e31a76cd3cae986039fe27da15a9036b1d0ed Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Mon, 2 Dec 2019 22:26:49 +0100 Subject: [PATCH 03/21] Pass a LayoutContext through box construction --- components/layout_2020/dom_traversal.rs | 25 ++++++++++--------- components/layout_2020/flow/construct.rs | 23 +++++++++-------- components/layout_2020/flow/root.rs | 5 ++-- components/layout_2020/formatting_contexts.rs | 5 ++-- components/layout_2020/traversal.rs | 4 +++ components/layout_thread_2020/lib.rs | 3 +-- 6 files changed, 34 insertions(+), 31 deletions(-) diff --git a/components/layout_2020/dom_traversal.rs b/components/layout_2020/dom_traversal.rs index 1a3ec7f1c98..0038627f88c 100644 --- a/components/layout_2020/dom_traversal.rs +++ b/components/layout_2020/dom_traversal.rs @@ -2,6 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +use crate::context::LayoutContext; use crate::element_data::{LayoutBox, LayoutDataForElement}; use crate::geom::physical::Vec2; use crate::replaced::ReplacedContent; @@ -13,7 +14,6 @@ use script_layout_interface::wrapper_traits::{LayoutNode, ThreadSafeLayoutNode}; use servo_arc::Arc as ServoArc; use std::marker::PhantomData as marker; use std::sync::Arc; -use style::context::SharedStyleContext; use style::dom::TNode; use style::properties::ComputedValues; use style::selector_parser::PseudoElement; @@ -66,7 +66,7 @@ where fn traverse_children_of<'dom, Node>( parent_element: Node, - context: &SharedStyleContext, + context: &LayoutContext, handler: &mut impl TraversalHandler<'dom, Node>, ) where Node: NodeExt<'dom>, @@ -88,7 +88,7 @@ fn traverse_children_of<'dom, Node>( fn traverse_element<'dom, Node>( element: Node, - context: &SharedStyleContext, + context: &LayoutContext, handler: &mut impl TraversalHandler<'dom, Node>, ) where Node: NodeExt<'dom>, @@ -121,7 +121,7 @@ fn traverse_element<'dom, Node>( fn traverse_pseudo_element<'dom, Node>( which: WhichPseudoElement, element: Node, - context: &SharedStyleContext, + context: &LayoutContext, handler: &mut impl TraversalHandler<'dom, Node>, ) where Node: NodeExt<'dom>, @@ -146,7 +146,7 @@ fn traverse_pseudo_element<'dom, Node>( fn traverse_pseudo_element_contents<'dom, Node>( pseudo_element_style: &ServoArc, - context: &SharedStyleContext, + context: &LayoutContext, handler: &mut impl TraversalHandler<'dom, Node>, items: Vec, ) where @@ -159,9 +159,10 @@ fn traverse_pseudo_element_contents<'dom, Node>( PseudoElementContentItem::Replaced(contents) => { let item_style = anonymous_style.get_or_insert_with(|| { context + .shared_context() .stylist .style_for_anonymous::( - &context.guards, + &context.shared_context().guards, &PseudoElement::ServoText, &pseudo_element_style, ) @@ -215,7 +216,7 @@ where pub(crate) fn traverse( self, inherited_style: &ServoArc, - context: &SharedStyleContext, + context: &LayoutContext, handler: &mut impl TraversalHandler<'dom, Node>, ) { match self { @@ -230,7 +231,7 @@ where fn pseudo_element_style<'dom, Node>( _which: WhichPseudoElement, _element: Node, - _context: &SharedStyleContext, + _context: &LayoutContext, ) -> Option> where Node: NodeExt<'dom>, @@ -243,7 +244,7 @@ where fn generate_pseudo_element_content<'dom, Node>( _pseudo_element_style: &ComputedValues, _element: Node, - _context: &SharedStyleContext, + _context: &LayoutContext, ) -> Vec where Node: NodeExt<'dom>, @@ -292,7 +293,7 @@ pub(crate) trait NodeExt<'dom>: 'dom + Copy + LayoutNode + Send + Sync { fn first_child(self) -> Option; fn next_sibling(self) -> Option; fn parent_node(self) -> Option; - fn style(self, context: &SharedStyleContext) -> ServoArc; + fn style(self, context: &LayoutContext) -> ServoArc; fn layout_data_mut(&self) -> AtomicRefMut; fn element_box_slot(&self) -> BoxSlot<'dom>; @@ -349,8 +350,8 @@ where TNode::parent_node(&self) } - fn style(self, context: &SharedStyleContext) -> ServoArc { - self.to_threadsafe().style(context) + fn style(self, context: &LayoutContext) -> ServoArc { + self.to_threadsafe().style(context.shared_context()) } fn layout_data_mut(&self) -> AtomicRefMut { diff --git a/components/layout_2020/flow/construct.rs b/components/layout_2020/flow/construct.rs index 5ac85dd47d9..79c44911f47 100644 --- a/components/layout_2020/flow/construct.rs +++ b/components/layout_2020/flow/construct.rs @@ -2,6 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +use crate::context::LayoutContext; use crate::dom_traversal::{BoxSlot, Contents, NodeExt, NonReplacedContents, TraversalHandler}; use crate::element_data::LayoutBox; use crate::flow::float::FloatBox; @@ -14,13 +15,12 @@ use rayon::iter::{IntoParallelIterator, ParallelIterator}; use rayon_croissant::ParallelIteratorExt; use servo_arc::Arc; use std::convert::TryInto; -use style::context::SharedStyleContext; use style::properties::ComputedValues; use style::selector_parser::PseudoElement; impl BlockFormattingContext { pub fn construct<'dom>( - context: &SharedStyleContext<'_>, + context: &LayoutContext, style: &Arc, contents: NonReplacedContents>, ) -> Self { @@ -71,7 +71,7 @@ enum IntermediateBlockContainer { /// This builder starts from the first child of a given DOM node /// and does a preorder traversal of all of its inclusive siblings. struct BlockContainerBuilder<'dom, 'style, Node> { - context: &'style SharedStyleContext<'style>, + context: &'style LayoutContext<'style>, block_container_style: &'style Arc, @@ -123,11 +123,11 @@ struct BlockContainerBuilder<'dom, 'style, Node> { } impl BlockContainer { - pub fn construct<'dom, 'style>( - context: &SharedStyleContext<'style>, + pub fn construct<'dom>( + context: &LayoutContext, block_container_style: &Arc, contents: NonReplacedContents>, - // intrinsic_sizes_requested: bool, + //intrinsic_sizes_requested: bool, ) -> (BlockContainer, ContainsFloats) { let mut builder = BlockContainerBuilder { context, @@ -510,9 +510,10 @@ where let block_container_style = self.block_container_style; let anonymous_style = self.anonymous_style.get_or_insert_with(|| { context + .shared_context() .stylist .style_for_anonymous::( - &context.guards, + &context.shared_context().guards, &PseudoElement::ServoText, &block_container_style, ) @@ -547,9 +548,9 @@ impl<'dom, Node> IntermediateBlockLevelBox where Node: NodeExt<'dom>, { - fn finish<'style>( + fn finish( self, - context: &SharedStyleContext<'style>, + context: &LayoutContext, ) -> (Arc, ContainsFloats) { match self { IntermediateBlockLevelBox::SameFormattingContextBlock { style, contents } => { @@ -614,9 +615,9 @@ impl<'dom, Node> IntermediateBlockContainer where Node: NodeExt<'dom>, { - fn finish<'style>( + fn finish( self, - context: &SharedStyleContext<'style>, + context: &LayoutContext, style: &Arc, ) -> (BlockContainer, ContainsFloats) { match self { diff --git a/components/layout_2020/flow/root.rs b/components/layout_2020/flow/root.rs index 7c062f12f47..e5681f21f0c 100644 --- a/components/layout_2020/flow/root.rs +++ b/components/layout_2020/flow/root.rs @@ -19,7 +19,6 @@ use crate::{ContainingBlock, DefiniteContainingBlock}; use rayon::iter::{IntoParallelRefIterator, ParallelExtend, ParallelIterator}; use script_layout_interface::wrapper_traits::LayoutNode; use servo_arc::Arc; -use style::context::SharedStyleContext; use style::values::computed::{Length, LengthOrAuto}; use style::Zero; use style_traits::CSSPixel; @@ -28,7 +27,7 @@ pub struct BoxTreeRoot(BlockFormattingContext); pub struct FragmentTreeRoot(Vec); impl BoxTreeRoot { - pub fn construct<'dom, Node>(context: &SharedStyleContext<'_>, root_element: Node) -> Self + pub fn construct<'dom, Node>(context: &LayoutContext, root_element: Node) -> Self where Node: 'dom + Copy + LayoutNode + Send + Sync, { @@ -41,7 +40,7 @@ impl BoxTreeRoot { } fn construct_for_root_element<'dom>( - context: &SharedStyleContext<'_>, + context: &LayoutContext, root_element: impl NodeExt<'dom>, ) -> (ContainsFloats, Vec>) { let style = root_element.style(context); diff --git a/components/layout_2020/formatting_contexts.rs b/components/layout_2020/formatting_contexts.rs index 0517f599852..aa643c6e9c8 100644 --- a/components/layout_2020/formatting_contexts.rs +++ b/components/layout_2020/formatting_contexts.rs @@ -12,7 +12,6 @@ use crate::style_ext::DisplayInside; use crate::ContainingBlock; use servo_arc::Arc; use std::convert::TryInto; -use style::context::SharedStyleContext; use style::properties::ComputedValues; use style::values::computed::Length; @@ -46,8 +45,8 @@ enum NonReplacedIFCKind<'a> { } impl IndependentFormattingContext { - pub fn construct<'dom, 'style>( - context: &SharedStyleContext<'style>, + pub fn construct<'dom>( + context: &LayoutContext, style: Arc, display_inside: DisplayInside, contents: Contents>, diff --git a/components/layout_2020/traversal.rs b/components/layout_2020/traversal.rs index bdc2c095026..97a9874a20b 100644 --- a/components/layout_2020/traversal.rs +++ b/components/layout_2020/traversal.rs @@ -20,6 +20,10 @@ impl<'a> RecalcStyle<'a> { RecalcStyle { context: context } } + pub fn context(&self) -> &LayoutContext<'a> { + &self.context + } + pub fn destroy(self) -> LayoutContext<'a> { self.context } diff --git a/components/layout_thread_2020/lib.rs b/components/layout_thread_2020/lib.rs index 77e89950d57..f9cdbac7943 100644 --- a/components/layout_thread_2020/lib.rs +++ b/components/layout_thread_2020/lib.rs @@ -1081,9 +1081,8 @@ impl LayoutThread { let box_tree = if token.should_traverse() { driver::traverse_dom(&traversal, token, Some(rayon_pool)); - let shared = DomTraversal::::shared_context(&traversal); let root_node = document.root_element().unwrap().as_node(); - let box_tree = rayon_pool.install(|| BoxTreeRoot::construct(shared, root_node)); + let box_tree = rayon_pool.install(|| BoxTreeRoot::construct(traversal.context(), root_node)); Some(box_tree) } else { None From 6a5b8337a137dd917f6b8578581533db7f57198a Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Mon, 2 Dec 2019 22:51:12 +0100 Subject: [PATCH 04/21] Rename IntrinsicSizes to ContentSizes --- components/layout_2020/flow/inline.rs | 18 ++++++++-------- components/layout_2020/lib.rs | 2 +- .../layout_2020/{intrinsic.rs => sizing.rs} | 21 ++++++++++++------- 3 files changed, 24 insertions(+), 17 deletions(-) rename components/layout_2020/{intrinsic.rs => sizing.rs} (80%) diff --git a/components/layout_2020/flow/inline.rs b/components/layout_2020/flow/inline.rs index ccb87ff876a..8623ca7b512 100644 --- a/components/layout_2020/flow/inline.rs +++ b/components/layout_2020/flow/inline.rs @@ -9,7 +9,7 @@ use crate::formatting_contexts::IndependentFormattingContext; use crate::fragments::CollapsedBlockMargins; use crate::fragments::{AnonymousFragment, BoxFragment, Fragment, TextFragment}; use crate::geom::flow_relative::{Rect, Sides, Vec2}; -use crate::intrinsic::{outer_intrinsic_inline_sizes, IntrinsicSizes}; +use crate::sizing::{outer_inline_content_sizes, ContentSizes}; use crate::positioned::{AbsolutelyPositionedBox, AbsolutelyPositionedFragment}; use crate::style_ext::{ComputedValuesExt, Display, DisplayGeneratingBox, DisplayOutside}; use crate::{relative_adjustement, ContainingBlock}; @@ -85,10 +85,10 @@ impl InlineFormattingContext { // Which would have to change if/when // `BlockContainer::construct` parallelize their construction. #[allow(unused)] - pub(super) fn intrinsic_sizes(&self, layout_context: &LayoutContext) -> IntrinsicSizes { + pub(super) fn content_sizes(&self, layout_context: &LayoutContext) -> ContentSizes { struct Computation { - paragraph: IntrinsicSizes, - current_line: IntrinsicSizes, + paragraph: ContentSizes, + current_line: ContentSizes, current_line_percentages: Percentage, } impl Computation { @@ -149,13 +149,13 @@ impl InlineFormattingContext { InlineLevelBox::Atomic(atomic) => { let inner = || { // atomic - // .intrinsic_inline_sizes + // .inline_content_sizes // .as_ref() - // .expect("Accessing intrinsic size that was not requested") + // .expect("Accessing content size that was not requested") // .clone() todo!() }; - let (outer, pc) = outer_intrinsic_inline_sizes(&atomic.style, &inner); + let (outer, pc) = outer_inline_content_sizes(&atomic.style, &inner); self.current_line.min_content += outer.min_content; self.current_line.max_content += outer.max_content; self.current_line_percentages += pc; @@ -185,8 +185,8 @@ impl InlineFormattingContext { std::mem::replace(x, T::zero()) } let mut computation = Computation { - paragraph: IntrinsicSizes::zero(), - current_line: IntrinsicSizes::zero(), + paragraph: ContentSizes::zero(), + current_line: ContentSizes::zero(), current_line_percentages: Percentage::zero(), }; computation.traverse(layout_context, &self.inline_level_boxes); diff --git a/components/layout_2020/lib.rs b/components/layout_2020/lib.rs index cb0b75a59f1..09013578308 100644 --- a/components/layout_2020/lib.rs +++ b/components/layout_2020/lib.rs @@ -14,11 +14,11 @@ mod flow; mod formatting_contexts; mod fragments; mod geom; -mod intrinsic; mod opaque_node; mod positioned; pub mod query; mod replaced; +mod sizing; mod style_ext; pub mod traversal; pub mod wrapper; diff --git a/components/layout_2020/intrinsic.rs b/components/layout_2020/sizing.rs similarity index 80% rename from components/layout_2020/intrinsic.rs rename to components/layout_2020/sizing.rs index e8df63ba630..bf109c31b04 100644 --- a/components/layout_2020/intrinsic.rs +++ b/components/layout_2020/sizing.rs @@ -1,15 +1,22 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +//! https://drafts.csswg.org/css-sizing/ + use crate::style_ext::ComputedValuesExt; use style::properties::ComputedValues; use style::values::computed::{Length, LengthPercentage, Percentage}; use style::Zero; -#[derive(Debug, Clone)] -pub(crate) struct IntrinsicSizes { +#[derive(Clone, Debug)] +pub(crate) struct ContentSizes { pub min_content: Length, pub max_content: Length, } -impl IntrinsicSizes { +/// https://drafts.csswg.org/css-sizing/#intrinsic-sizes +impl ContentSizes { pub fn zero() -> Self { Self { min_content: Length::zero(), @@ -32,10 +39,10 @@ impl IntrinsicSizes { } /// https://dbaron.org/css/intrinsic/#outer-intrinsic -pub(crate) fn outer_intrinsic_inline_sizes( +pub(crate) fn outer_inline_content_sizes( style: &ComputedValues, - get_inner_intrinsic_sizes: &dyn Fn() -> IntrinsicSizes, -) -> (IntrinsicSizes, Percentage) { + get_inner_intrinsic_sizes: &dyn Fn() -> ContentSizes, +) -> (ContentSizes, Percentage) { // FIXME: account for 'min-width', 'max-width', 'box-sizing' let specified = style.box_size().inline; @@ -44,7 +51,7 @@ pub(crate) fn outer_intrinsic_inline_sizes( // The (inner) min/max-content are only used for 'auto' let mut outer = match specified.non_auto().flatten() { None => get_inner_intrinsic_sizes(), - Some(length) => IntrinsicSizes { + Some(length) => ContentSizes { min_content: length, max_content: length, }, From 2c124b9d0b32a53cdffc00bcee2ff127ff4ae343 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Mon, 2 Dec 2019 23:49:39 +0100 Subject: [PATCH 05/21] Add inline_size_is_auto --- components/layout_2020/lib.rs | 1 + components/layout_2020/style_ext.rs | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/components/layout_2020/lib.rs b/components/layout_2020/lib.rs index 09013578308..dd589e1819b 100644 --- a/components/layout_2020/lib.rs +++ b/components/layout_2020/lib.rs @@ -4,6 +4,7 @@ #![deny(unsafe_code)] #![feature(exact_size_is_empty)] +#![feature(matches_macro)] pub mod context; pub mod data; diff --git a/components/layout_2020/style_ext.rs b/components/layout_2020/style_ext.rs index e6d4537f47c..6bd2e91a2f9 100644 --- a/components/layout_2020/style_ext.rs +++ b/components/layout_2020/style_ext.rs @@ -43,6 +43,8 @@ pub(crate) enum DisplayInside { pub(crate) trait ComputedValuesExt { fn writing_mode(&self) -> (WritingMode, Direction); + fn writing_mode_is_horizontal(&self) -> bool; + fn inline_size_is_auto(&self) -> bool; fn box_offsets(&self) -> flow_relative::Sides; fn box_size(&self) -> flow_relative::Vec2; fn padding(&self) -> flow_relative::Sides; @@ -58,6 +60,23 @@ impl ComputedValuesExt for ComputedValues { (writing_mode, direction) } + fn writing_mode_is_horizontal(&self) -> bool { + match self.get_inherited_box().writing_mode { + WritingMode::HorizontalTb => true, + WritingMode::VerticalLr | WritingMode::VerticalRl => false, + } + } + + fn inline_size_is_auto(&self) -> bool { + let position = self.get_position(); + let size = if self.writing_mode_is_horizontal() { + position.width + } else { + position.height + }; + matches!(size, Size::Auto) + } + #[inline] fn box_offsets(&self) -> flow_relative::Sides { let position = self.get_position(); From cfdd23ac16e3923cc388c96bbf5aab7501967f5e Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Tue, 3 Dec 2019 00:45:29 +0100 Subject: [PATCH 06/21] Add a `request_content_sizes` parameter to `IndependentFormattingContext::construct` --- components/layout_2020/flow/construct.rs | 53 +++++++------------ components/layout_2020/flow/float.rs | 25 +++++++++ components/layout_2020/flow/inline.rs | 2 +- components/layout_2020/flow/root.rs | 31 +++++------ components/layout_2020/formatting_contexts.rs | 1 + components/layout_2020/positioned.rs | 24 ++++++++- components/layout_2020/style_ext.rs | 14 +++++ components/layout_thread_2020/lib.rs | 3 +- 8 files changed, 101 insertions(+), 52 deletions(-) diff --git a/components/layout_2020/flow/construct.rs b/components/layout_2020/flow/construct.rs index 79c44911f47..0ddd4a41f88 100644 --- a/components/layout_2020/flow/construct.rs +++ b/components/layout_2020/flow/construct.rs @@ -10,7 +10,7 @@ use crate::flow::inline::{InlineBox, InlineFormattingContext, InlineLevelBox, Te use crate::flow::{BlockContainer, BlockFormattingContext, BlockLevelBox}; use crate::formatting_contexts::IndependentFormattingContext; use crate::positioned::AbsolutelyPositionedBox; -use crate::style_ext::{DisplayGeneratingBox, DisplayInside, DisplayOutside}; +use crate::style_ext::{ComputedValuesExt, DisplayGeneratingBox, DisplayInside, DisplayOutside}; use rayon::iter::{IntoParallelIterator, ParallelIterator}; use rayon_croissant::ParallelIteratorExt; use servo_arc::Arc; @@ -158,6 +158,7 @@ impl BlockContainer { } let mut contains_floats = builder.contains_floats; + let request_content_sizes = false; // FIXME let container = BlockContainer::BlockLevelBoxes( builder .block_level_boxes @@ -165,7 +166,7 @@ impl BlockContainer { .mapfold_reduce_into( &mut contains_floats, |contains_floats, (intermediate, box_slot): (IntermediateBlockLevelBox<_>, BoxSlot<'_>)| { - let (block_level_box, box_contains_floats) = intermediate.finish(context); + let (block_level_box, box_contains_floats) = intermediate.finish(context, request_content_sizes); *contains_floats |= box_contains_floats; box_slot.set(LayoutBox::BlockLevel(block_level_box.clone())); block_level_box @@ -332,6 +333,7 @@ where style.clone(), display_inside, >::Replaced(replaced), + false, // ignored ), )), Ok(non_replaced) => match display_inside { @@ -452,14 +454,7 @@ where self.block_level_boxes.push((box_, box_slot)); } else { let box_ = Arc::new(InlineLevelBox::OutOfFlowAbsolutelyPositionedBox( - AbsolutelyPositionedBox { - contents: IndependentFormattingContext::construct( - self.context, - style, - display_inside, - contents, - ), - }, + AbsolutelyPositionedBox::construct(self.context, style, display_inside, contents), )); self.current_inline_level_boxes().push(box_.clone()); box_slot.set(LayoutBox::InlineLevel(box_)) @@ -483,14 +478,12 @@ where }; self.block_level_boxes.push((box_, box_slot)); } else { - let box_ = Arc::new(InlineLevelBox::OutOfFlowFloatBox(FloatBox { - contents: IndependentFormattingContext::construct( - self.context, - style, - display_inside, - contents, - ), - })); + let box_ = Arc::new(InlineLevelBox::OutOfFlowFloatBox(FloatBox::construct( + self.context, + style, + display_inside, + contents, + ))); self.current_inline_level_boxes().push(box_.clone()); box_slot.set(LayoutBox::InlineLevel(box_)) } @@ -551,6 +544,7 @@ where fn finish( self, context: &LayoutContext, + parent_requests_outer_content_sizes: bool, ) -> (Arc, ContainsFloats) { match self { IntermediateBlockLevelBox::SameFormattingContextBlock { style, contents } => { @@ -564,11 +558,14 @@ where display_inside, contents, } => { + let request_content_sizes = + parent_requests_outer_content_sizes && style.inline_size_is_auto(); let contents = IndependentFormattingContext::construct( context, style, display_inside, contents, + request_content_sizes, ); ( Arc::new(BlockLevelBox::Independent(contents)), @@ -581,14 +578,7 @@ where contents, } => { let block_level_box = Arc::new(BlockLevelBox::OutOfFlowAbsolutelyPositionedBox( - AbsolutelyPositionedBox { - contents: IndependentFormattingContext::construct( - context, - style, - display_inside, - contents, - ), - }, + AbsolutelyPositionedBox::construct(context, style, display_inside, contents), )); (block_level_box, ContainsFloats::No) }, @@ -597,14 +587,9 @@ where display_inside, contents, } => { - let contents = IndependentFormattingContext::construct( - context, - style, - display_inside, - contents, - ); - let block_level_box = - Arc::new(BlockLevelBox::OutOfFlowFloatBox(FloatBox { contents })); + let block_level_box = Arc::new(BlockLevelBox::OutOfFlowFloatBox( + FloatBox::construct(context, style, display_inside, contents), + )); (block_level_box, ContainsFloats::Yes) }, } diff --git a/components/layout_2020/flow/float.rs b/components/layout_2020/flow/float.rs index f9be366bc97..6fcd06df0b5 100644 --- a/components/layout_2020/flow/float.rs +++ b/components/layout_2020/flow/float.rs @@ -2,7 +2,12 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +use crate::context::LayoutContext; +use crate::dom_traversal::{Contents, NodeExt}; use crate::formatting_contexts::IndependentFormattingContext; +use crate::style_ext::{ComputedValuesExt, DisplayInside}; +use servo_arc::Arc; +use style::properties::ComputedValues; #[derive(Debug)] pub(crate) struct FloatBox { @@ -19,3 +24,23 @@ impl FloatContext { FloatContext {} } } + +impl FloatBox { + pub fn construct<'dom>( + context: &LayoutContext, + style: Arc, + display_inside: DisplayInside, + contents: Contents>, + ) -> Self { + let request_content_sizes = style.inline_size_is_auto(); + Self { + contents: IndependentFormattingContext::construct( + context, + style, + display_inside, + contents, + request_content_sizes, + ), + } + } +} diff --git a/components/layout_2020/flow/inline.rs b/components/layout_2020/flow/inline.rs index 8623ca7b512..ca78fbf1956 100644 --- a/components/layout_2020/flow/inline.rs +++ b/components/layout_2020/flow/inline.rs @@ -9,8 +9,8 @@ use crate::formatting_contexts::IndependentFormattingContext; use crate::fragments::CollapsedBlockMargins; use crate::fragments::{AnonymousFragment, BoxFragment, Fragment, TextFragment}; use crate::geom::flow_relative::{Rect, Sides, Vec2}; -use crate::sizing::{outer_inline_content_sizes, ContentSizes}; use crate::positioned::{AbsolutelyPositionedBox, AbsolutelyPositionedFragment}; +use crate::sizing::{outer_inline_content_sizes, ContentSizes}; use crate::style_ext::{ComputedValuesExt, Display, DisplayGeneratingBox, DisplayOutside}; use crate::{relative_adjustement, ContainingBlock}; use app_units::Au; diff --git a/components/layout_2020/flow/root.rs b/components/layout_2020/flow/root.rs index e5681f21f0c..d5358fd28b5 100644 --- a/components/layout_2020/flow/root.rs +++ b/components/layout_2020/flow/root.rs @@ -59,32 +59,33 @@ fn construct_for_root_element<'dom>( Display::GeneratingBox(DisplayGeneratingBox::OutsideInside { inside, .. }) => inside, }; - let position = box_style.position; - let float = box_style.float; - let contents = IndependentFormattingContext::construct( - context, - style, - display_inside, - replaced.map_or(Contents::OfElement(root_element), Contents::Replaced), - ); - if position.is_absolutely_positioned() { + let contents = replaced.map_or(Contents::OfElement(root_element), Contents::Replaced); + if box_style.position.is_absolutely_positioned() { ( ContainsFloats::No, vec![Arc::new(BlockLevelBox::OutOfFlowAbsolutelyPositionedBox( - AbsolutelyPositionedBox { contents }, + AbsolutelyPositionedBox::construct(context, style, display_inside, contents), ))], ) - } else if float.is_floating() { + } else if box_style.float.is_floating() { ( ContainsFloats::Yes, - vec![Arc::new(BlockLevelBox::OutOfFlowFloatBox(FloatBox { - contents, - }))], + vec![Arc::new(BlockLevelBox::OutOfFlowFloatBox( + FloatBox::construct(context, style, display_inside, contents), + ))], ) } else { ( ContainsFloats::No, - vec![Arc::new(BlockLevelBox::Independent(contents))], + vec![Arc::new(BlockLevelBox::Independent( + IndependentFormattingContext::construct( + context, + style, + display_inside, + contents, + /* request_content_sizes */ false, + ), + ))], ) } } diff --git a/components/layout_2020/formatting_contexts.rs b/components/layout_2020/formatting_contexts.rs index aa643c6e9c8..9b11c28246c 100644 --- a/components/layout_2020/formatting_contexts.rs +++ b/components/layout_2020/formatting_contexts.rs @@ -50,6 +50,7 @@ impl IndependentFormattingContext { style: Arc, display_inside: DisplayInside, contents: Contents>, + _request_content_sizes: bool, // Ignored for replaced content ) -> Self { use self::IndependentFormattingContextContents as Contents; let contents = match contents.try_into() { diff --git a/components/layout_2020/positioned.rs b/components/layout_2020/positioned.rs index 3577a86642b..cd801e7e1c0 100644 --- a/components/layout_2020/positioned.rs +++ b/components/layout_2020/positioned.rs @@ -3,12 +3,15 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use crate::context::LayoutContext; +use crate::dom_traversal::{Contents, NodeExt}; use crate::formatting_contexts::IndependentFormattingContext; use crate::fragments::{AnonymousFragment, BoxFragment, CollapsedBlockMargins, Fragment}; use crate::geom::flow_relative::{Rect, Sides, Vec2}; -use crate::style_ext::{ComputedValuesExt, Direction, WritingMode}; +use crate::style_ext::{ComputedValuesExt, Direction, DisplayInside, WritingMode}; use crate::{ContainingBlock, DefiniteContainingBlock}; use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; +use servo_arc::Arc; +use style::properties::ComputedValues; use style::values::computed::{Length, LengthOrAuto, LengthPercentage, LengthPercentageOrAuto}; use style::Zero; @@ -42,6 +45,25 @@ pub(crate) enum AbsoluteBoxOffsets { } impl AbsolutelyPositionedBox { + pub fn construct<'dom>( + context: &LayoutContext, + style: Arc, + display_inside: DisplayInside, + contents: Contents>, + ) -> Self { + let request_content_sizes = + style.inline_size_is_auto() && !style.inline_box_offsets_are_both_auto(); + Self { + contents: IndependentFormattingContext::construct( + context, + style, + display_inside, + contents, + request_content_sizes, + ), + } + } + pub(crate) fn layout<'a>( &'a self, initial_start_corner: Vec2, diff --git a/components/layout_2020/style_ext.rs b/components/layout_2020/style_ext.rs index 6bd2e91a2f9..436058798d9 100644 --- a/components/layout_2020/style_ext.rs +++ b/components/layout_2020/style_ext.rs @@ -45,6 +45,7 @@ pub(crate) trait ComputedValuesExt { fn writing_mode(&self) -> (WritingMode, Direction); fn writing_mode_is_horizontal(&self) -> bool; fn inline_size_is_auto(&self) -> bool; + fn inline_box_offsets_are_both_auto(&self) -> bool; fn box_offsets(&self) -> flow_relative::Sides; fn box_size(&self) -> flow_relative::Vec2; fn padding(&self) -> flow_relative::Sides; @@ -77,6 +78,19 @@ impl ComputedValuesExt for ComputedValues { matches!(size, Size::Auto) } + fn inline_box_offsets_are_both_auto(&self) -> bool { + let position = self.get_position(); + let offsets = if self.writing_mode_is_horizontal() { + (position.left, position.right) + } else { + (position.top, position.bottom) + }; + matches!( + offsets, + (LengthPercentageOrAuto::Auto, LengthPercentageOrAuto::Auto) + ) + } + #[inline] fn box_offsets(&self) -> flow_relative::Sides { let position = self.get_position(); diff --git a/components/layout_thread_2020/lib.rs b/components/layout_thread_2020/lib.rs index f9cdbac7943..45202d51ad9 100644 --- a/components/layout_thread_2020/lib.rs +++ b/components/layout_thread_2020/lib.rs @@ -1082,7 +1082,8 @@ impl LayoutThread { driver::traverse_dom(&traversal, token, Some(rayon_pool)); let root_node = document.root_element().unwrap().as_node(); - let box_tree = rayon_pool.install(|| BoxTreeRoot::construct(traversal.context(), root_node)); + let box_tree = + rayon_pool.install(|| BoxTreeRoot::construct(traversal.context(), root_node)); Some(box_tree) } else { None From 78bfa45eaba9d9da09a2c198bd63f308f293c11c Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Tue, 3 Dec 2019 01:00:17 +0100 Subject: [PATCH 07/21] Add a `Target` struct for `mapfold_reduce_into` call --- components/layout_2020/flow/construct.rs | 38 +++++++++++++----------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/components/layout_2020/flow/construct.rs b/components/layout_2020/flow/construct.rs index 0ddd4a41f88..d1a57145110 100644 --- a/components/layout_2020/flow/construct.rs +++ b/components/layout_2020/flow/construct.rs @@ -157,25 +157,29 @@ impl BlockContainer { builder.end_ongoing_inline_formatting_context(); } - let mut contains_floats = builder.contains_floats; + type Intermediate = IntermediateBlockLevelBox; + #[derive(Default)] + struct Target { + contains_floats: ContainsFloats, + } + let mut target = Target { + contains_floats: builder.contains_floats, + }; let request_content_sizes = false; // FIXME - let container = BlockContainer::BlockLevelBoxes( - builder - .block_level_boxes - .into_par_iter() - .mapfold_reduce_into( - &mut contains_floats, - |contains_floats, (intermediate, box_slot): (IntermediateBlockLevelBox<_>, BoxSlot<'_>)| { - let (block_level_box, box_contains_floats) = intermediate.finish(context, request_content_sizes); - *contains_floats |= box_contains_floats; - box_slot.set(LayoutBox::BlockLevel(block_level_box.clone())); - block_level_box - }, - |left, right| *left |= right, - ) - .collect(), + let iter = builder.block_level_boxes.into_par_iter(); + let iter = iter.mapfold_reduce_into( + &mut target, + |target, (intermediate, box_slot): (Intermediate<_>, BoxSlot<'_>)| { + let (block_level_box, box_contains_floats) = + intermediate.finish(context, request_content_sizes); + target.contains_floats |= box_contains_floats; + box_slot.set(LayoutBox::BlockLevel(block_level_box.clone())); + block_level_box + }, + |left, right| left.contains_floats |= right.contains_floats, ); - (container, contains_floats) + let container = BlockContainer::BlockLevelBoxes(iter.collect()); + (container, target.contains_floats) } } From 303b36f17b053a64bf62a89efd43977b5115c46f Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Tue, 3 Dec 2019 02:03:51 +0100 Subject: [PATCH 08/21] Add content sizes computation for block containers --- components/layout_2020/flow/construct.rs | 72 +++++++++++++++++------- components/layout_2020/flow/inline.rs | 11 ++-- components/layout_2020/sizing.rs | 19 ++++++- 3 files changed, 73 insertions(+), 29 deletions(-) diff --git a/components/layout_2020/flow/construct.rs b/components/layout_2020/flow/construct.rs index d1a57145110..5e6881ab862 100644 --- a/components/layout_2020/flow/construct.rs +++ b/components/layout_2020/flow/construct.rs @@ -10,6 +10,7 @@ use crate::flow::inline::{InlineBox, InlineFormattingContext, InlineLevelBox, Te use crate::flow::{BlockContainer, BlockFormattingContext, BlockLevelBox}; use crate::formatting_contexts::IndependentFormattingContext; use crate::positioned::AbsolutelyPositionedBox; +use crate::sizing::{outer_inline_content_sizes, ContentSizes}; use crate::style_ext::{ComputedValuesExt, DisplayGeneratingBox, DisplayInside, DisplayOutside}; use rayon::iter::{IntoParallelIterator, ParallelIterator}; use rayon_croissant::ParallelIteratorExt; @@ -25,6 +26,8 @@ impl BlockFormattingContext { contents: NonReplacedContents>, ) -> Self { let (contents, contains_floats) = BlockContainer::construct(context, style, contents); + // FIXME: add contribution to `content_sizes` of floats in this formatting context + // https://dbaron.org/css/intrinsic/#intrinsic Self { contents, contains_floats: contains_floats == ContainsFloats::Yes, @@ -127,16 +130,17 @@ impl BlockContainer { context: &LayoutContext, block_container_style: &Arc, contents: NonReplacedContents>, - //intrinsic_sizes_requested: bool, + //request_content_sizes: bool, ) -> (BlockContainer, ContainsFloats) { + let request_content_sizes = false; // FIXME let mut builder = BlockContainerBuilder { context, block_container_style, - block_level_boxes: Default::default(), - ongoing_inline_formatting_context: Default::default(), - ongoing_inline_boxes_stack: Default::default(), - anonymous_style: Default::default(), - contains_floats: Default::default(), + block_level_boxes: Vec::new(), + ongoing_inline_formatting_context: InlineFormattingContext::default(), + ongoing_inline_boxes_stack: Vec::new(), + anonymous_style: None, + contains_floats: ContainsFloats::No, }; contents.traverse(block_container_style, context, &mut builder); @@ -158,25 +162,46 @@ impl BlockContainer { } type Intermediate = IntermediateBlockLevelBox; - #[derive(Default)] struct Target { contains_floats: ContainsFloats, + outer_content_sizes_of_children: ContentSizes, + } + impl Default for Target { + fn default() -> Self { + Self { + contains_floats: ContainsFloats::No, + outer_content_sizes_of_children: ContentSizes::zero(), + } + } } let mut target = Target { contains_floats: builder.contains_floats, + outer_content_sizes_of_children: ContentSizes::zero(), }; - let request_content_sizes = false; // FIXME + let request_childrens_outer_content_sizes = request_content_sizes; let iter = builder.block_level_boxes.into_par_iter(); let iter = iter.mapfold_reduce_into( &mut target, |target, (intermediate, box_slot): (Intermediate<_>, BoxSlot<'_>)| { - let (block_level_box, box_contains_floats) = - intermediate.finish(context, request_content_sizes); + let (block_level_box, box_contains_floats) = intermediate.finish( + context, + if request_childrens_outer_content_sizes { + Some(&mut target.outer_content_sizes_of_children) + } else { + None + }, + ); target.contains_floats |= box_contains_floats; box_slot.set(LayoutBox::BlockLevel(block_level_box.clone())); block_level_box }, - |left, right| left.contains_floats |= right.contains_floats, + |left, right| { + left.contains_floats |= right.contains_floats; + if request_content_sizes { + left.outer_content_sizes_of_children + .max_assign(&right.outer_content_sizes_of_children) + } + }, ); let container = BlockContainer::BlockLevelBoxes(iter.collect()); (container, target.contains_floats) @@ -548,11 +573,18 @@ where fn finish( self, context: &LayoutContext, - parent_requests_outer_content_sizes: bool, + max_assign_in_flow_outer_content_sizes_to: Option<&mut ContentSizes>, ) -> (Arc, ContainsFloats) { match self { IntermediateBlockLevelBox::SameFormattingContextBlock { style, contents } => { - let (contents, contains_floats) = contents.finish(context, &style); + let request_content_sizes = + max_assign_in_flow_outer_content_sizes_to.is_some() && + style.inline_size_is_auto(); + let (contents, contains_floats) = contents.finish(context, &style, request_content_sizes); + if let Some(to) = max_assign_in_flow_outer_content_sizes_to { + let get_content_size = || todo!(); + to.max_assign(&outer_inline_content_sizes(&style, get_content_size())) + } let block_level_box = Arc::new(BlockLevelBox::SameFormattingContextBlock { contents, style }); (block_level_box, contains_floats) @@ -563,7 +595,8 @@ where contents, } => { let request_content_sizes = - parent_requests_outer_content_sizes && style.inline_size_is_auto(); + max_assign_in_flow_outer_content_sizes_to.is_some() && + style.inline_size_is_auto(); let contents = IndependentFormattingContext::construct( context, style, @@ -571,6 +604,10 @@ where contents, request_content_sizes, ); + if let Some(to) = max_assign_in_flow_outer_content_sizes_to { + let get_content_size = || todo!(); + to.max_assign(&outer_inline_content_sizes(&contents.style, get_content_size())) + } ( Arc::new(BlockLevelBox::Independent(contents)), ContainsFloats::No, @@ -608,6 +645,7 @@ where self, context: &LayoutContext, style: &Arc, + _request_content_sizes: bool, ) -> (BlockContainer, ContainsFloats) { match self { IntermediateBlockContainer::Deferred { contents } => { @@ -639,9 +677,3 @@ impl std::ops::BitOrAssign for ContainsFloats { } } } - -impl Default for ContainsFloats { - fn default() -> Self { - ContainsFloats::No - } -} diff --git a/components/layout_2020/flow/inline.rs b/components/layout_2020/flow/inline.rs index ca78fbf1956..605b87e3699 100644 --- a/components/layout_2020/flow/inline.rs +++ b/components/layout_2020/flow/inline.rs @@ -10,7 +10,7 @@ use crate::fragments::CollapsedBlockMargins; use crate::fragments::{AnonymousFragment, BoxFragment, Fragment, TextFragment}; use crate::geom::flow_relative::{Rect, Sides, Vec2}; use crate::positioned::{AbsolutelyPositionedBox, AbsolutelyPositionedFragment}; -use crate::sizing::{outer_inline_content_sizes, ContentSizes}; +use crate::sizing::{outer_inline_content_sizes_and_percentages, ContentSizes}; use crate::style_ext::{ComputedValuesExt, Display, DisplayGeneratingBox, DisplayOutside}; use crate::{relative_adjustement, ContainingBlock}; use app_units::Au; @@ -148,14 +148,11 @@ impl InlineFormattingContext { }, InlineLevelBox::Atomic(atomic) => { let inner = || { - // atomic - // .inline_content_sizes - // .as_ref() - // .expect("Accessing content size that was not requested") - // .clone() + // &atomic.inline_content_sizes todo!() }; - let (outer, pc) = outer_inline_content_sizes(&atomic.style, &inner); + let (outer, pc) = outer_inline_content_sizes_and_percentages( + &atomic.style, inner()); self.current_line.min_content += outer.min_content; self.current_line.max_content += outer.max_content; self.current_line_percentages += pc; diff --git a/components/layout_2020/sizing.rs b/components/layout_2020/sizing.rs index bf109c31b04..998c2e2b419 100644 --- a/components/layout_2020/sizing.rs +++ b/components/layout_2020/sizing.rs @@ -24,6 +24,11 @@ impl ContentSizes { } } + pub fn max_assign(&mut self, other: &Self) { + self.min_content.max_assign(other.min_content); + self.max_content.max_assign(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 @@ -41,7 +46,17 @@ impl ContentSizes { /// https://dbaron.org/css/intrinsic/#outer-intrinsic pub(crate) fn outer_inline_content_sizes( style: &ComputedValues, - get_inner_intrinsic_sizes: &dyn Fn() -> ContentSizes, + inner_content_sizes: &Option, +) -> ContentSizes { + let (mut outer, percentages) = + outer_inline_content_sizes_and_percentages(style, inner_content_sizes); + outer.adjust_for_pbm_percentages(percentages); + outer +} + +pub(crate) fn outer_inline_content_sizes_and_percentages( + style: &ComputedValues, + inner_content_sizes: &Option, ) -> (ContentSizes, Percentage) { // FIXME: account for 'min-width', 'max-width', 'box-sizing' @@ -50,7 +65,7 @@ pub(crate) fn outer_inline_content_sizes( let specified = specified.map(|lp| lp.as_length()); // The (inner) min/max-content are only used for 'auto' let mut outer = match specified.non_auto().flatten() { - None => get_inner_intrinsic_sizes(), + None => inner_content_sizes.as_ref().expect("Accessing content size that was not requested").clone(), Some(length) => ContentSizes { min_content: length, max_content: length, From da36fcddb05da8a4370c006f937ab43682d057c0 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Tue, 3 Dec 2019 11:01:58 +0100 Subject: [PATCH 09/21] =?UTF-8?q?Add=20box=20construction=20for=20'inline-?= =?UTF-8?q?block'=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit … and other atomic inline-level boxes. --- components/layout_2020/dom_traversal.rs | 11 +++++ components/layout_2020/flow/construct.rs | 56 ++++++++++++------------ 2 files changed, 38 insertions(+), 29 deletions(-) diff --git a/components/layout_2020/dom_traversal.rs b/components/layout_2020/dom_traversal.rs index 0038627f88c..b71395cf1f4 100644 --- a/components/layout_2020/dom_traversal.rs +++ b/components/layout_2020/dom_traversal.rs @@ -188,6 +188,17 @@ fn traverse_pseudo_element_contents<'dom, Node>( } } +impl Contents { + /// Returns true iff the `try_from` impl below would return `Err(_)` + pub fn is_replaced(&self) -> bool { + match self { + Contents::OfElement(_) | + Contents::OfPseudoElement(_) => false, + Contents::Replaced(_) => true, + } + } +} + impl std::convert::TryFrom> for NonReplacedContents { type Error = ReplacedContent; diff --git a/components/layout_2020/flow/construct.rs b/components/layout_2020/flow/construct.rs index 5e6881ab862..f7490f4c605 100644 --- a/components/layout_2020/flow/construct.rs +++ b/components/layout_2020/flow/construct.rs @@ -15,7 +15,7 @@ use crate::style_ext::{ComputedValuesExt, DisplayGeneratingBox, DisplayInside, D use rayon::iter::{IntoParallelIterator, ParallelIterator}; use rayon_croissant::ParallelIteratorExt; use servo_arc::Arc; -use std::convert::TryInto; +use std::convert::{TryFrom, TryInto}; use style::properties::ComputedValues; use style::selector_parser::PseudoElement; @@ -355,39 +355,37 @@ where display_inside: DisplayInside, contents: Contents, ) -> Arc { - let box_ = match contents.try_into() { - Err(replaced) => Arc::new(InlineLevelBox::Atomic( + let box_ = if display_inside == DisplayInside::Flow && !contents.is_replaced() { + // We found un inline box. + // Whatever happened before, all we need to do before recurring + // is to remember this ongoing inline level box. + self.ongoing_inline_boxes_stack.push(InlineBox { + style: style.clone(), + first_fragment: true, + last_fragment: false, + children: vec![], + }); + + // `unwrap` doesn’t panic here because `is_replaced` returned `false`. + NonReplacedContents::try_from(contents).unwrap().traverse(&style, self.context, self); + + let mut inline_box = self + .ongoing_inline_boxes_stack + .pop() + .expect("no ongoing inline level box found"); + inline_box.last_fragment = true; + Arc::new(InlineLevelBox::InlineBox(inline_box)) + } else { + let request_content_sizes = style.inline_size_is_auto(); + Arc::new(InlineLevelBox::Atomic( IndependentFormattingContext::construct( self.context, style.clone(), display_inside, - >::Replaced(replaced), - false, // ignored + contents, + request_content_sizes, ), - )), - Ok(non_replaced) => match display_inside { - DisplayInside::Flow | - // TODO: Properly implement display: inline-block. - DisplayInside::FlowRoot => { - // Whatever happened before, we just found an inline level element, so - // all we need to do is to remember this ongoing inline level box. - self.ongoing_inline_boxes_stack.push(InlineBox { - style: style.clone(), - first_fragment: true, - last_fragment: false, - children: vec![], - }); - - NonReplacedContents::traverse(non_replaced, &style, self.context, self); - - let mut inline_box = self - .ongoing_inline_boxes_stack - .pop() - .expect("no ongoing inline level box found"); - inline_box.last_fragment = true; - Arc::new(InlineLevelBox::InlineBox(inline_box)) - }, - }, + )) }; self.current_inline_level_boxes().push(box_.clone()); box_ From c056e5b6b0dcd3a0226c02b6ffbe544a085da567 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Tue, 3 Dec 2019 02:19:48 +0100 Subject: [PATCH 10/21] Finish plumbing intrinsic min/max-content through box construction --- components/layout_2020/dom_traversal.rs | 3 +- components/layout_2020/flow/construct.rs | 68 +++++++++++++------ components/layout_2020/flow/inline.rs | 11 ++- components/layout_2020/formatting_contexts.rs | 32 +++++++-- components/layout_2020/sizing.rs | 5 +- 5 files changed, 80 insertions(+), 39 deletions(-) diff --git a/components/layout_2020/dom_traversal.rs b/components/layout_2020/dom_traversal.rs index b71395cf1f4..056103d7205 100644 --- a/components/layout_2020/dom_traversal.rs +++ b/components/layout_2020/dom_traversal.rs @@ -192,8 +192,7 @@ impl Contents { /// Returns true iff the `try_from` impl below would return `Err(_)` pub fn is_replaced(&self) -> bool { match self { - Contents::OfElement(_) | - Contents::OfPseudoElement(_) => false, + Contents::OfElement(_) | Contents::OfPseudoElement(_) => false, Contents::Replaced(_) => true, } } diff --git a/components/layout_2020/flow/construct.rs b/components/layout_2020/flow/construct.rs index f7490f4c605..80f44b2ff81 100644 --- a/components/layout_2020/flow/construct.rs +++ b/components/layout_2020/flow/construct.rs @@ -24,14 +24,17 @@ impl BlockFormattingContext { context: &LayoutContext, style: &Arc, contents: NonReplacedContents>, - ) -> Self { - let (contents, contains_floats) = BlockContainer::construct(context, style, contents); + request_content_sizes: bool, + ) -> (Self, Option) { + let (contents, contains_floats, content_sizes) = + BlockContainer::construct(context, style, contents, request_content_sizes); // FIXME: add contribution to `content_sizes` of floats in this formatting context // https://dbaron.org/css/intrinsic/#intrinsic - Self { + let bfc = Self { contents, contains_floats: contains_floats == ContainsFloats::Yes, - } + }; + (bfc, content_sizes) } } @@ -130,9 +133,8 @@ impl BlockContainer { context: &LayoutContext, block_container_style: &Arc, contents: NonReplacedContents>, - //request_content_sizes: bool, - ) -> (BlockContainer, ContainsFloats) { - let request_content_sizes = false; // FIXME + request_content_sizes: bool, + ) -> (BlockContainer, ContainsFloats, Option) { let mut builder = BlockContainerBuilder { context, block_container_style, @@ -153,10 +155,19 @@ impl BlockContainer { .is_empty() { if builder.block_level_boxes.is_empty() { + let content_sizes = if request_content_sizes { + Some( + builder + .ongoing_inline_formatting_context + .inline_content_sizes(context), + ) + } else { + None + }; let container = BlockContainer::InlineFormattingContext( builder.ongoing_inline_formatting_context, ); - return (container, builder.contains_floats); + return (container, builder.contains_floats, content_sizes); } builder.end_ongoing_inline_formatting_context(); } @@ -204,7 +215,12 @@ impl BlockContainer { }, ); let container = BlockContainer::BlockLevelBoxes(iter.collect()); - (container, target.contains_floats) + let content_sizes = if request_content_sizes { + Some(target.outer_content_sizes_of_children) + } else { + None + }; + (container, target.contains_floats, content_sizes) } } @@ -367,7 +383,9 @@ where }); // `unwrap` doesn’t panic here because `is_replaced` returned `false`. - NonReplacedContents::try_from(contents).unwrap().traverse(&style, self.context, self); + NonReplacedContents::try_from(contents) + .unwrap() + .traverse(&style, self.context, self); let mut inline_box = self .ongoing_inline_boxes_stack @@ -575,13 +593,12 @@ where ) -> (Arc, ContainsFloats) { match self { IntermediateBlockLevelBox::SameFormattingContextBlock { style, contents } => { - let request_content_sizes = - max_assign_in_flow_outer_content_sizes_to.is_some() && + let request_content_sizes = max_assign_in_flow_outer_content_sizes_to.is_some() && style.inline_size_is_auto(); - let (contents, contains_floats) = contents.finish(context, &style, request_content_sizes); + let (contents, contains_floats, content_sizes) = + contents.finish(context, &style, request_content_sizes); if let Some(to) = max_assign_in_flow_outer_content_sizes_to { - let get_content_size = || todo!(); - to.max_assign(&outer_inline_content_sizes(&style, get_content_size())) + to.max_assign(&outer_inline_content_sizes(&style, &content_sizes)) } let block_level_box = Arc::new(BlockLevelBox::SameFormattingContextBlock { contents, style }); @@ -592,8 +609,7 @@ where display_inside, contents, } => { - let request_content_sizes = - max_assign_in_flow_outer_content_sizes_to.is_some() && + let request_content_sizes = max_assign_in_flow_outer_content_sizes_to.is_some() && style.inline_size_is_auto(); let contents = IndependentFormattingContext::construct( context, @@ -603,8 +619,10 @@ where request_content_sizes, ); if let Some(to) = max_assign_in_flow_outer_content_sizes_to { - let get_content_size = || todo!(); - to.max_assign(&outer_inline_content_sizes(&contents.style, get_content_size())) + to.max_assign(&outer_inline_content_sizes( + &contents.style, + &contents.inline_content_sizes, + )) } ( Arc::new(BlockLevelBox::Independent(contents)), @@ -643,19 +661,25 @@ where self, context: &LayoutContext, style: &Arc, - _request_content_sizes: bool, - ) -> (BlockContainer, ContainsFloats) { + request_content_sizes: bool, + ) -> (BlockContainer, ContainsFloats, Option) { match self { IntermediateBlockContainer::Deferred { contents } => { - BlockContainer::construct(context, style, contents) + BlockContainer::construct(context, style, contents, request_content_sizes) }, IntermediateBlockContainer::InlineFormattingContext(ifc) => { + let content_sizes = if request_content_sizes { + Some(ifc.inline_content_sizes(context)) + } else { + None + }; // If that inline formatting context contained any float, those // were already taken into account during the first phase of // box construction. ( BlockContainer::InlineFormattingContext(ifc), ContainsFloats::No, + content_sizes, ) }, } diff --git a/components/layout_2020/flow/inline.rs b/components/layout_2020/flow/inline.rs index 605b87e3699..53cc3ad2ec2 100644 --- a/components/layout_2020/flow/inline.rs +++ b/components/layout_2020/flow/inline.rs @@ -84,8 +84,7 @@ impl InlineFormattingContext { // This works on an already-constructed `InlineFormattingContext`, // Which would have to change if/when // `BlockContainer::construct` parallelize their construction. - #[allow(unused)] - pub(super) fn content_sizes(&self, layout_context: &LayoutContext) -> ContentSizes { + pub(super) fn inline_content_sizes(&self, layout_context: &LayoutContext) -> ContentSizes { struct Computation { paragraph: ContentSizes, current_line: ContentSizes, @@ -147,12 +146,10 @@ impl InlineFormattingContext { } }, InlineLevelBox::Atomic(atomic) => { - let inner = || { - // &atomic.inline_content_sizes - todo!() - }; let (outer, pc) = outer_inline_content_sizes_and_percentages( - &atomic.style, inner()); + &atomic.style, + &atomic.inline_content_sizes, + ); self.current_line.min_content += outer.min_content; self.current_line.max_content += outer.max_content; self.current_line_percentages += pc; diff --git a/components/layout_2020/formatting_contexts.rs b/components/layout_2020/formatting_contexts.rs index 9b11c28246c..c6e4611db56 100644 --- a/components/layout_2020/formatting_contexts.rs +++ b/components/layout_2020/formatting_contexts.rs @@ -8,6 +8,7 @@ use crate::flow::BlockFormattingContext; use crate::fragments::Fragment; use crate::positioned::AbsolutelyPositionedFragment; use crate::replaced::ReplacedContent; +use crate::sizing::ContentSizes; use crate::style_ext::DisplayInside; use crate::ContainingBlock; use servo_arc::Arc; @@ -19,6 +20,10 @@ use style::values::computed::Length; #[derive(Debug)] pub(crate) struct IndependentFormattingContext { pub style: Arc, + + /// If it was requested during construction + pub inline_content_sizes: Option, + contents: IndependentFormattingContextContents, } @@ -50,18 +55,31 @@ impl IndependentFormattingContext { style: Arc, display_inside: DisplayInside, contents: Contents>, - _request_content_sizes: bool, // Ignored for replaced content + request_content_sizes: bool, ) -> Self { use self::IndependentFormattingContextContents as Contents; - let contents = match contents.try_into() { + let (contents, inline_content_sizes) = match contents.try_into() { Ok(non_replaced) => match display_inside { - DisplayInside::Flow | DisplayInside::FlowRoot => Contents::Flow( - BlockFormattingContext::construct(context, &style, non_replaced), - ), + DisplayInside::Flow | DisplayInside::FlowRoot => { + let (bfc, content_sizes) = BlockFormattingContext::construct( + context, + &style, + non_replaced, + request_content_sizes, + ); + (Contents::Flow(bfc), content_sizes) + }, + }, + Err(replaced) => { + let content_sizes = None; // Unused by layout code + (Contents::Replaced(replaced), content_sizes) }, - Err(replaced) => Contents::Replaced(replaced), }; - Self { style, contents } + Self { + style, + contents, + inline_content_sizes, + } } pub fn as_replaced(&self) -> Result<&ReplacedContent, NonReplacedIFC> { diff --git a/components/layout_2020/sizing.rs b/components/layout_2020/sizing.rs index 998c2e2b419..a58602d23fd 100644 --- a/components/layout_2020/sizing.rs +++ b/components/layout_2020/sizing.rs @@ -65,7 +65,10 @@ pub(crate) fn outer_inline_content_sizes_and_percentages( let specified = specified.map(|lp| lp.as_length()); // The (inner) min/max-content are only used for 'auto' let mut outer = match specified.non_auto().flatten() { - None => inner_content_sizes.as_ref().expect("Accessing content size that was not requested").clone(), + None => inner_content_sizes + .as_ref() + .expect("Accessing content size that was not requested") + .clone(), Some(length) => ContentSizes { min_content: length, max_content: length, From efa1885e1b6f87622b6e86445af9044c080422f1 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Tue, 3 Dec 2019 11:33:44 +0100 Subject: [PATCH 11/21] Remove IndependentFormattingContext::layout Callers should handle replaced v.s. not separately --- components/layout_2020/formatting_contexts.rs | 18 ------------- components/layout_2020/positioned.rs | 25 +++++++++++++------ 2 files changed, 17 insertions(+), 26 deletions(-) diff --git a/components/layout_2020/formatting_contexts.rs b/components/layout_2020/formatting_contexts.rs index c6e4611db56..0527d8eda38 100644 --- a/components/layout_2020/formatting_contexts.rs +++ b/components/layout_2020/formatting_contexts.rs @@ -91,24 +91,6 @@ impl IndependentFormattingContext { Contents::Flow(f) => Err(NR(Kind::Flow(f))), } } - - pub fn layout<'a>( - &'a self, - layout_context: &LayoutContext, - containing_block: &ContainingBlock, - tree_rank: usize, - absolutely_positioned_fragments: &mut Vec>, - ) -> IndependentLayout { - match self.as_replaced() { - Ok(replaced) => replaced.layout(&self.style, containing_block), - Err(ifc) => ifc.layout( - layout_context, - containing_block, - tree_rank, - absolutely_positioned_fragments, - ), - } - } } impl<'a> NonReplacedIFC<'a> { diff --git a/components/layout_2020/positioned.rs b/components/layout_2020/positioned.rs index cd801e7e1c0..c7b27bb27fc 100644 --- a/components/layout_2020/positioned.rs +++ b/components/layout_2020/positioned.rs @@ -292,14 +292,23 @@ impl<'a> AbsolutelyPositionedFragment<'a> { containing_block.mode, containing_block_for_children.mode, "Mixed writing modes are not supported yet" ); - let dummy_tree_rank = 0; - let mut absolutely_positioned_fragments = vec![]; - let mut independent_layout = self.absolutely_positioned_box.contents.layout( - layout_context, - &containing_block_for_children, - dummy_tree_rank, - &mut absolutely_positioned_fragments, - ); + let mut absolutely_positioned_fragments = Vec::new(); + let mut independent_layout = match self.absolutely_positioned_box.contents.as_replaced() { + // FIXME: implement https://drafts.csswg.org/css2/visudet.html#abs-replaced-width + Ok(replaced) => replaced.layout( + &self.absolutely_positioned_box.contents.style, + &containing_block_for_children, + ), + Err(non_replaced) => { + let dummy_tree_rank = 0; + non_replaced.layout( + layout_context, + &containing_block_for_children, + dummy_tree_rank, + &mut absolutely_positioned_fragments, + ) + }, + }; let inline_start = match inline_anchor { Anchor::Start(start) => start + pb.inline_start + margin.inline_start, From 46f0f7d7e2d099bac74db75e7cec2c242cdebead Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Tue, 3 Dec 2019 13:12:40 +0100 Subject: [PATCH 12/21] Implement shrink-to-fit for abspos --- components/layout_2020/positioned.rs | 28 ++++++++++++++++--- components/layout_2020/sizing.rs | 22 ++++++++++++--- components/layout_2020/style_ext.rs | 9 ++++-- .../box-display/containing-block-008.xht.ini | 2 -- .../box-display/containing-block-010.xht.ini | 2 -- 5 files changed, 48 insertions(+), 15 deletions(-) delete mode 100644 tests/wpt/metadata-layout-2020/css/CSS2/box-display/containing-block-008.xht.ini delete mode 100644 tests/wpt/metadata-layout-2020/css/CSS2/box-display/containing-block-010.xht.ini diff --git a/components/layout_2020/positioned.rs b/components/layout_2020/positioned.rs index c7b27bb27fc..7fcc7cdb254 100644 --- a/components/layout_2020/positioned.rs +++ b/components/layout_2020/positioned.rs @@ -7,6 +7,7 @@ use crate::dom_traversal::{Contents, NodeExt}; use crate::formatting_contexts::IndependentFormattingContext; use crate::fragments::{AnonymousFragment, BoxFragment, CollapsedBlockMargins, Fragment}; use crate::geom::flow_relative::{Rect, Sides, Vec2}; +use crate::sizing::shrink_to_fit; use crate::style_ext::{ComputedValuesExt, Direction, DisplayInside, WritingMode}; use crate::{ContainingBlock, DefiniteContainingBlock}; use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; @@ -51,8 +52,15 @@ impl AbsolutelyPositionedBox { display_inside: DisplayInside, contents: Contents>, ) -> Self { - let request_content_sizes = - style.inline_size_is_auto() && !style.inline_box_offsets_are_both_auto(); + // "Shrink-to-fit" in https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-width + let request_content_sizes = { + // If inline-size is non-auto, that value is used without shrink-to-fit + style.inline_size_is_auto() && + // If it is, then the only case where shrink-to-fit is *not* used is + // if both offsets are non-auto, leaving inline-size as the only variable + // in the constraint equation. + !style.inline_box_offsets_are_both_non_auto() + }; Self { contents: IndependentFormattingContext::construct( context, @@ -278,8 +286,20 @@ impl<'a> AbsolutelyPositionedFragment<'a> { Anchor::End(end) => cbis - end - pb.inline_sum() - margin.inline_sum(), }; - // FIXME(nox): shrink-to-fit. - available_size + if self + .absolutely_positioned_box + .contents + .as_replaced() + .is_ok() + { + // FIXME: implement https://drafts.csswg.org/css2/visudet.html#abs-replaced-width + available_size + } else { + shrink_to_fit( + &self.absolutely_positioned_box.contents.inline_content_sizes, + available_size, + ) + } }); let containing_block_for_children = ContainingBlock { diff --git a/components/layout_2020/sizing.rs b/components/layout_2020/sizing.rs index a58602d23fd..01e28c7a064 100644 --- a/components/layout_2020/sizing.rs +++ b/components/layout_2020/sizing.rs @@ -65,10 +65,7 @@ pub(crate) fn outer_inline_content_sizes_and_percentages( let specified = specified.map(|lp| lp.as_length()); // The (inner) min/max-content are only used for 'auto' let mut outer = match specified.non_auto().flatten() { - None => inner_content_sizes - .as_ref() - .expect("Accessing content size that was not requested") - .clone(), + None => expect(inner_content_sizes).clone(), Some(length) => ContentSizes { min_content: length, max_content: length, @@ -96,3 +93,20 @@ pub(crate) fn outer_inline_content_sizes_and_percentages( (outer, pbm_percentages) } + +/// https://drafts.csswg.org/css2/visudet.html#shrink-to-fit-float +pub(crate) fn shrink_to_fit( + content_sizes: &Option, + available_size: Length, +) -> Length { + let content_sizes = expect(content_sizes); + available_size + .max(content_sizes.min_content) + .min(content_sizes.max_content) +} + +fn expect(content_sizes: &Option) -> &ContentSizes { + content_sizes + .as_ref() + .expect("Accessing content size that was not requested") +} diff --git a/components/layout_2020/style_ext.rs b/components/layout_2020/style_ext.rs index 436058798d9..ebdf217e81a 100644 --- a/components/layout_2020/style_ext.rs +++ b/components/layout_2020/style_ext.rs @@ -45,7 +45,7 @@ pub(crate) trait ComputedValuesExt { fn writing_mode(&self) -> (WritingMode, Direction); fn writing_mode_is_horizontal(&self) -> bool; fn inline_size_is_auto(&self) -> bool; - fn inline_box_offsets_are_both_auto(&self) -> bool; + fn inline_box_offsets_are_both_non_auto(&self) -> bool; fn box_offsets(&self) -> flow_relative::Sides; fn box_size(&self) -> flow_relative::Vec2; fn padding(&self) -> flow_relative::Sides; @@ -78,7 +78,7 @@ impl ComputedValuesExt for ComputedValues { matches!(size, Size::Auto) } - fn inline_box_offsets_are_both_auto(&self) -> bool { + fn inline_box_offsets_are_both_non_auto(&self) -> bool { let position = self.get_position(); let offsets = if self.writing_mode_is_horizontal() { (position.left, position.right) @@ -87,7 +87,10 @@ impl ComputedValuesExt for ComputedValues { }; matches!( offsets, - (LengthPercentageOrAuto::Auto, LengthPercentageOrAuto::Auto) + ( + LengthPercentageOrAuto::LengthPercentage(_), + LengthPercentageOrAuto::LengthPercentage(_), + ) ) } diff --git a/tests/wpt/metadata-layout-2020/css/CSS2/box-display/containing-block-008.xht.ini b/tests/wpt/metadata-layout-2020/css/CSS2/box-display/containing-block-008.xht.ini deleted file mode 100644 index dd630be0f54..00000000000 --- a/tests/wpt/metadata-layout-2020/css/CSS2/box-display/containing-block-008.xht.ini +++ /dev/null @@ -1,2 +0,0 @@ -[containing-block-008.xht] - expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/CSS2/box-display/containing-block-010.xht.ini b/tests/wpt/metadata-layout-2020/css/CSS2/box-display/containing-block-010.xht.ini deleted file mode 100644 index af40eef0a5d..00000000000 --- a/tests/wpt/metadata-layout-2020/css/CSS2/box-display/containing-block-010.xht.ini +++ /dev/null @@ -1,2 +0,0 @@ -[containing-block-010.xht] - expected: FAIL From b8db9459bc7d33b7a735b031bfd9bcc559c6fe03 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Tue, 3 Dec 2019 13:50:26 +0100 Subject: [PATCH 13/21] Add layout for 'inline-block' and other atomic inline-level boxes --- components/layout_2020/flow/inline.rs | 130 ++++++++++++++++++++++++-- 1 file changed, 120 insertions(+), 10 deletions(-) diff --git a/components/layout_2020/flow/inline.rs b/components/layout_2020/flow/inline.rs index 53cc3ad2ec2..4da0c7018cc 100644 --- a/components/layout_2020/flow/inline.rs +++ b/components/layout_2020/flow/inline.rs @@ -10,14 +10,14 @@ use crate::fragments::CollapsedBlockMargins; use crate::fragments::{AnonymousFragment, BoxFragment, Fragment, TextFragment}; use crate::geom::flow_relative::{Rect, Sides, Vec2}; use crate::positioned::{AbsolutelyPositionedBox, AbsolutelyPositionedFragment}; -use crate::sizing::{outer_inline_content_sizes_and_percentages, ContentSizes}; +use crate::sizing::{outer_inline_content_sizes_and_percentages, shrink_to_fit, ContentSizes}; use crate::style_ext::{ComputedValuesExt, Display, DisplayGeneratingBox, DisplayOutside}; use crate::{relative_adjustement, ContainingBlock}; use app_units::Au; use gfx::text::text_run::GlyphRun; use servo_arc::Arc; use style::properties::ComputedValues; -use style::values::computed::{Length, Percentage}; +use style::values::computed::{Length, LengthOrAuto, Percentage}; use style::Zero; use webrender_api::FontInstanceKey; @@ -67,8 +67,9 @@ struct PartialInlineBoxFragment<'box_tree> { parent_nesting_level: InlineNestingLevelState<'box_tree>, } -struct InlineFormattingContextState<'box_tree, 'cb> { - containing_block: &'cb ContainingBlock, +struct InlineFormattingContextState<'box_tree, 'a> { + absolutely_positioned_fragments: &'a mut Vec>, + containing_block: &'a ContainingBlock, line_boxes: LinesBoxes, inline_position: Length, partial_inline_boxes_stack: Vec>, @@ -196,6 +197,7 @@ impl InlineFormattingContext { absolutely_positioned_fragments: &mut Vec>, ) -> FlowLayout { let mut ifc = InlineFormattingContextState { + absolutely_positioned_fragments, containing_block, partial_inline_boxes_stack: Vec::new(), line_boxes: LinesBoxes { @@ -218,10 +220,7 @@ impl InlineFormattingContext { ifc.partial_inline_boxes_stack.push(partial) }, InlineLevelBox::TextRun(run) => run.layout(layout_context, &mut ifc), - InlineLevelBox::Atomic(_independent) => { - // TODO - continue; - }, + InlineLevelBox::Atomic(a) => layout_atomic(layout_context, &mut ifc, a), InlineLevelBox::OutOfFlowAbsolutelyPositionedBox(box_) => { let initial_start_corner = match Display::from(box_.contents.style.get_box().original_display) { @@ -242,12 +241,11 @@ impl InlineFormattingContext { panic!("display:none does not generate an abspos box") }, }; - absolutely_positioned_fragments + ifc.absolutely_positioned_fragments .push(box_.layout(initial_start_corner, tree_rank)); }, InlineLevelBox::OutOfFlowFloatBox(_box_) => { // TODO - continue; }, } } else @@ -393,6 +391,118 @@ impl<'box_tree> PartialInlineBoxFragment<'box_tree> { } } +fn layout_atomic<'box_tree>( + layout_context: &LayoutContext, + ifc: &mut InlineFormattingContextState<'box_tree, '_>, + atomic: &'box_tree IndependentFormattingContext, +) { + let cbis = ifc.containing_block.inline_size; + let padding = atomic.style.padding().percentages_relative_to(cbis); + let border = atomic.style.border_width(); + let margin = atomic + .style + .margin() + .percentages_relative_to(cbis) + .auto_is(Length::zero); + ifc.inline_position += padding.inline_start + border.inline_start + margin.inline_start; + let mut start_corner = Vec2 { + block: padding.block_start + border.block_start + margin.block_start, + inline: ifc.inline_position - ifc.current_nesting_level.inline_start, + }; + start_corner += &relative_adjustement( + &atomic.style, + ifc.containing_block.inline_size, + ifc.containing_block.block_size, + ); + + let fragment = match atomic.as_replaced() { + Ok(replaced) => { + // FIXME: implement https://drafts.csswg.org/css2/visudet.html#inline-replaced-width + let inline_size = Length::zero(); + let block_size = Length::zero(); + let containing_block_for_children = ContainingBlock { + inline_size, + block_size: LengthOrAuto::LengthPercentage(block_size), + mode: atomic.style.writing_mode(), + }; + // https://drafts.csswg.org/css-writing-modes/#orthogonal-flows + assert_eq!( + ifc.containing_block.mode, containing_block_for_children.mode, + "Mixed writing modes are not supported yet" + ); + let independent_layout = replaced.layout(&atomic.style, &containing_block_for_children); + let content_rect = Rect { + start_corner, + size: Vec2 { + block: independent_layout.content_block_size, + inline: inline_size, + }, + }; + BoxFragment { + style: atomic.style.clone(), + children: independent_layout.fragments, + content_rect, + padding, + border, + margin, + block_margins_collapsed_with_children: CollapsedBlockMargins::zero(), + } + }, + Err(non_replaced) => { + let box_size = atomic.style.box_size(); + let inline_size = box_size.inline.percentage_relative_to(cbis).auto_is(|| { + let available_size = + cbis - padding.inline_sum() - border.inline_sum() - margin.inline_sum(); + shrink_to_fit(&atomic.inline_content_sizes, available_size) + }); + let block_size = box_size + .block + .maybe_percentage_relative_to(ifc.containing_block.block_size.non_auto()); + let containing_block_for_children = ContainingBlock { + inline_size, + block_size, + mode: atomic.style.writing_mode(), + }; + assert_eq!( + ifc.containing_block.mode, containing_block_for_children.mode, + "Mixed writing modes are not supported yet" + ); + // FIXME is this correct? + let dummy_tree_rank = 0; + // FIXME: Do we need to call `adjust_static_positions` somewhere near here? + let independent_layout = non_replaced.layout( + layout_context, + &containing_block_for_children, + dummy_tree_rank, + ifc.absolutely_positioned_fragments, + ); + let block_size = block_size.auto_is(|| independent_layout.content_block_size); + let content_rect = Rect { + start_corner, + size: Vec2 { + block: block_size, + inline: inline_size, + }, + }; + BoxFragment { + style: atomic.style.clone(), + children: independent_layout.fragments, + content_rect, + padding, + border, + margin, + block_margins_collapsed_with_children: CollapsedBlockMargins::zero(), + } + }, + }; + + ifc.inline_position += + fragment.padding.inline_end + fragment.border.inline_end + fragment.margin.inline_end; + ifc.current_nesting_level + .fragments_so_far + .push(Fragment::Box(fragment)); +} + impl TextRun { fn break_and_shape( &self, From 34e8cda801910fc4f9c561bb834e23bd26302ce2 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Tue, 3 Dec 2019 15:09:03 +0100 Subject: [PATCH 14/21] Rename ReplacedContent::layout to make_fragments and simplify its API --- components/layout_2020/flow/inline.rs | 28 ++++----------- components/layout_2020/flow/mod.rs | 21 ++++------- components/layout_2020/positioned.rs | 43 ++++++++++++++--------- components/layout_2020/replaced.rs | 50 ++++++++++----------------- 4 files changed, 57 insertions(+), 85 deletions(-) diff --git a/components/layout_2020/flow/inline.rs b/components/layout_2020/flow/inline.rs index 4da0c7018cc..8a3088b8b76 100644 --- a/components/layout_2020/flow/inline.rs +++ b/components/layout_2020/flow/inline.rs @@ -17,7 +17,7 @@ use app_units::Au; use gfx::text::text_run::GlyphRun; use servo_arc::Arc; use style::properties::ComputedValues; -use style::values::computed::{Length, LengthOrAuto, Percentage}; +use style::values::computed::{Length, Percentage}; use style::Zero; use webrender_api::FontInstanceKey; @@ -418,29 +418,13 @@ fn layout_atomic<'box_tree>( let fragment = match atomic.as_replaced() { Ok(replaced) => { // FIXME: implement https://drafts.csswg.org/css2/visudet.html#inline-replaced-width - let inline_size = Length::zero(); - let block_size = Length::zero(); - let containing_block_for_children = ContainingBlock { - inline_size, - block_size: LengthOrAuto::LengthPercentage(block_size), - mode: atomic.style.writing_mode(), - }; - // https://drafts.csswg.org/css-writing-modes/#orthogonal-flows - assert_eq!( - ifc.containing_block.mode, containing_block_for_children.mode, - "Mixed writing modes are not supported yet" - ); - let independent_layout = replaced.layout(&atomic.style, &containing_block_for_children); - let content_rect = Rect { - start_corner, - size: Vec2 { - block: independent_layout.content_block_size, - inline: inline_size, - }, - }; + // and https://drafts.csswg.org/css2/visudet.html#inline-replaced-height + let size = Vec2::zero(); + let fragments = replaced.make_fragments(&atomic.style, size.clone()); + let content_rect = Rect { start_corner, size }; BoxFragment { style: atomic.style.clone(), - children: independent_layout.fragments, + children: fragments, content_rect, padding, border, diff --git a/components/layout_2020/flow/mod.rs b/components/layout_2020/flow/mod.rs index 8bda0c69d24..195b1458ce7 100644 --- a/components/layout_2020/flow/mod.rs +++ b/components/layout_2020/flow/mod.rs @@ -516,17 +516,11 @@ fn layout_in_flow_replaced_block_level<'a>( block_start: computed_margin.block_start.auto_is(Length::zero), block_end: computed_margin.block_end.auto_is(Length::zero), }; - let containing_block_for_children = ContainingBlock { - inline_size, - block_size: LengthOrAuto::LengthPercentage(block_size), - mode, + let size = Vec2 { + block: block_size, + inline: inline_size, }; - // https://drafts.csswg.org/css-writing-modes/#orthogonal-flows - assert_eq!( - containing_block.mode, containing_block_for_children.mode, - "Mixed writing modes are not supported yet" - ); - let independent_layout = replaced.layout(style, &containing_block_for_children); + let fragments = replaced.make_fragments(style, size.clone()); let relative_adjustement = relative_adjustement( style, inline_size, @@ -537,14 +531,11 @@ fn layout_in_flow_replaced_block_level<'a>( block: pb.block_start + relative_adjustement.block, inline: pb.inline_start + relative_adjustement.inline + margin.inline_start, }, - size: Vec2 { - block: block_size, - inline: inline_size, - }, + size, }; BoxFragment { style: style.clone(), - children: independent_layout.fragments, + children: fragments, content_rect, padding, border, diff --git a/components/layout_2020/positioned.rs b/components/layout_2020/positioned.rs index 7fcc7cdb254..7d61916b2c1 100644 --- a/components/layout_2020/positioned.rs +++ b/components/layout_2020/positioned.rs @@ -302,24 +302,35 @@ impl<'a> AbsolutelyPositionedFragment<'a> { } }); - let containing_block_for_children = ContainingBlock { - inline_size, - block_size, - mode: style.writing_mode(), - }; - // https://drafts.csswg.org/css-writing-modes/#orthogonal-flows - assert_eq!( - containing_block.mode, containing_block_for_children.mode, - "Mixed writing modes are not supported yet" - ); let mut absolutely_positioned_fragments = Vec::new(); let mut independent_layout = match self.absolutely_positioned_box.contents.as_replaced() { - // FIXME: implement https://drafts.csswg.org/css2/visudet.html#abs-replaced-width - Ok(replaced) => replaced.layout( - &self.absolutely_positioned_box.contents.style, - &containing_block_for_children, - ), + Ok(replaced) => { + // FIXME: implement https://drafts.csswg.org/css2/visudet.html#abs-replaced-width + // and https://drafts.csswg.org/css2/visudet.html#abs-replaced-height + let block_size = block_size.auto_is(|| Length::zero()); + let fragments = replaced.make_fragments( + &self.absolutely_positioned_box.contents.style, + Vec2 { + inline: inline_size, + block: block_size, + }, + ); + crate::formatting_contexts::IndependentLayout { + fragments, + content_block_size: block_size, + } + }, Err(non_replaced) => { + let containing_block_for_children = ContainingBlock { + inline_size, + block_size, + mode: style.writing_mode(), + }; + // https://drafts.csswg.org/css-writing-modes/#orthogonal-flows + assert_eq!( + containing_block.mode, containing_block_for_children.mode, + "Mixed writing modes are not supported yet" + ); let dummy_tree_rank = 0; non_replaced.layout( layout_context, @@ -358,7 +369,7 @@ impl<'a> AbsolutelyPositionedFragment<'a> { &mut independent_layout.fragments, &content_rect.size, &padding, - containing_block_for_children.mode, + style.writing_mode(), ); Fragment::Box(BoxFragment { diff --git a/components/layout_2020/replaced.rs b/components/layout_2020/replaced.rs index ecb8d2433e3..8cb77d267bd 100644 --- a/components/layout_2020/replaced.rs +++ b/components/layout_2020/replaced.rs @@ -3,10 +3,8 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use crate::dom_traversal::NodeExt; -use crate::formatting_contexts::IndependentLayout; use crate::fragments::{Fragment, ImageFragment}; use crate::geom::{flow_relative, physical}; -use crate::ContainingBlock; use net_traits::image::base::Image; use servo_arc::Arc as ServoArc; use std::sync::Arc; @@ -35,39 +33,27 @@ impl ReplacedContent { None } - pub fn layout<'a>( + pub fn make_fragments<'a>( &'a self, style: &ServoArc, - containing_block: &ContainingBlock, - ) -> IndependentLayout { - let (fragments, content_block_size) = match self.kind { - ReplacedContentKind::Image(ref image) => { - // FIXME(nox): We should not assume block size is known. - let block_size = containing_block.block_size.non_auto().unwrap(); - let fragments = image - .as_ref() - .and_then(|image| image.id) - .map(|image_key| { - Fragment::Image(ImageFragment { - style: style.clone(), - content_rect: flow_relative::Rect { - start_corner: flow_relative::Vec2::zero(), - size: flow_relative::Vec2 { - inline: containing_block.inline_size, - block: block_size, - }, - }, - image_key, - }) + size: flow_relative::Vec2, + ) -> Vec { + match &self.kind { + ReplacedContentKind::Image(image) => image + .as_ref() + .and_then(|image| image.id) + .map(|image_key| { + Fragment::Image(ImageFragment { + style: style.clone(), + content_rect: flow_relative::Rect { + start_corner: flow_relative::Vec2::zero(), + size, + }, + image_key, }) - .into_iter() - .collect::>(); - (fragments, block_size) - }, - }; - IndependentLayout { - fragments, - content_block_size, + }) + .into_iter() + .collect(), } } } From 77d2c45991570b5fd3432db1807e76e838900b0d Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Wed, 4 Dec 2019 13:37:20 +0100 Subject: [PATCH 15/21] Fix incorrect variable name This was not the specified value --- components/layout_2020/sizing.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/layout_2020/sizing.rs b/components/layout_2020/sizing.rs index 01e28c7a064..65917d9b404 100644 --- a/components/layout_2020/sizing.rs +++ b/components/layout_2020/sizing.rs @@ -60,9 +60,9 @@ pub(crate) fn outer_inline_content_sizes_and_percentages( ) -> (ContentSizes, Percentage) { // FIXME: account for 'min-width', 'max-width', 'box-sizing' - let specified = style.box_size().inline; + let inline_size = style.box_size().inline; // Percentages for 'width' are treated as 'auto' - let specified = specified.map(|lp| lp.as_length()); + let inline_size = specified.map(|lp| lp.as_length()); // The (inner) min/max-content are only used for 'auto' let mut outer = match specified.non_auto().flatten() { None => expect(inner_content_sizes).clone(), From 01034b10e1467746241739a6737c2da08fd8de0b Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Wed, 4 Dec 2019 13:39:04 +0100 Subject: [PATCH 16/21] Use `Sides::inline_sum` method --- components/layout_2020/sizing.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/components/layout_2020/sizing.rs b/components/layout_2020/sizing.rs index 65917d9b404..0fdb36d7600 100644 --- a/components/layout_2020/sizing.rs +++ b/components/layout_2020/sizing.rs @@ -62,9 +62,9 @@ pub(crate) fn outer_inline_content_sizes_and_percentages( let inline_size = style.box_size().inline; // Percentages for 'width' are treated as 'auto' - let inline_size = specified.map(|lp| lp.as_length()); + let inline_size = inline_size.map(|lp| lp.as_length()); // The (inner) min/max-content are only used for 'auto' - let mut outer = match specified.non_auto().flatten() { + let mut outer = match inline_size.non_auto().flatten() { None => expect(inner_content_sizes).clone(), Some(length) => ContentSizes { min_content: length, @@ -77,8 +77,7 @@ pub(crate) fn outer_inline_content_sizes_and_percentages( let padding = style.padding(); let border = style.border_width(); let margin = style.margin(); - pbm_lengths += border.inline_start; - pbm_lengths += border.inline_end; + pbm_lengths += border.inline_sum(); let mut add = |x: LengthPercentage| { pbm_lengths += x.length_component(); pbm_percentages += x.percentage_component(); From 038f83fbc539f2ae6e4932ec5c8b52229f85b06b Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Wed, 4 Dec 2019 13:47:59 +0100 Subject: [PATCH 17/21] Replace a pair of macros by methods --- components/layout_2020/flow/inline.rs | 33 +++++++++++++-------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/components/layout_2020/flow/inline.rs b/components/layout_2020/flow/inline.rs index 8a3088b8b76..eaf271c2d99 100644 --- a/components/layout_2020/flow/inline.rs +++ b/components/layout_2020/flow/inline.rs @@ -17,7 +17,7 @@ use app_units::Au; use gfx::text::text_run::GlyphRun; use servo_arc::Arc; use style::properties::ComputedValues; -use style::values::computed::{Length, Percentage}; +use style::values::computed::{Length, LengthPercentage, Percentage}; use style::Zero; use webrender_api::FontInstanceKey; @@ -103,25 +103,14 @@ impl InlineFormattingContext { let padding = inline_box.style.padding(); let border = inline_box.style.border_width(); let margin = inline_box.style.margin(); - macro_rules! add_length { - ($x: expr) => {{ - let length = $x; - self.current_line.min_content += length; - self.current_line.max_content += length; - }}; - } - macro_rules! add_lengthpercentage { - ($x: expr) => {{ - add_length!($x.length_component()); - self.current_line_percentages += $x.percentage_component(); - }}; - } macro_rules! add { ($condition: ident, $side: ident) => { if inline_box.$condition { - add_lengthpercentage!(padding.$side); - add_length!(border.$side); - margin.$side.non_auto().map(|x| add_lengthpercentage!(x)); + self.add_lengthpercentage(padding.$side); + self.add_length(border.$side); + if let Some(lp) = margin.$side.non_auto() { + self.add_lengthpercentage(lp) + } } }; } @@ -161,6 +150,16 @@ impl InlineFormattingContext { } } + fn add_lengthpercentage(&mut self, lp: LengthPercentage) { + self.add_length(lp.length_component()); + self.current_line_percentages += lp.percentage_component(); + } + + fn add_length(&mut self, l: Length) { + self.current_line.min_content += l; + self.current_line.max_content += l; + } + fn line_break_opportunity(&mut self) { self.paragraph .min_content From 607df04849407ba08ace0f987baf2d5f75de466c Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Wed, 4 Dec 2019 13:56:42 +0100 Subject: [PATCH 18/21] Replace a large tuple return type with a new struct --- components/layout_2020/flow/inline.rs | 38 ++++++++++++++++++--------- 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/components/layout_2020/flow/inline.rs b/components/layout_2020/flow/inline.rs index eaf271c2d99..5fb01c0fa53 100644 --- a/components/layout_2020/flow/inline.rs +++ b/components/layout_2020/flow/inline.rs @@ -120,8 +120,11 @@ impl InlineFormattingContext { add!(last_fragment, inline_end); }, InlineLevelBox::TextRun(text_run) => { - let (_, _, _, runs, break_at_start) = - text_run.break_and_shape(layout_context); + let BreakAndShapeResult { + runs, + break_at_start, + .. + } = text_run.break_and_shape(layout_context); if break_at_start { self.line_break_opportunity() } @@ -486,11 +489,16 @@ fn layout_atomic<'box_tree>( .push(Fragment::Box(fragment)); } +struct BreakAndShapeResult { + font_ascent: Au, + font_line_gap: Au, + font_key: FontInstanceKey, + runs: Vec, + break_at_start: bool, +} + impl TextRun { - fn break_and_shape( - &self, - layout_context: &LayoutContext, - ) -> (Au, Au, FontInstanceKey, Vec, bool) { + fn break_and_shape(&self, layout_context: &LayoutContext) -> BreakAndShapeResult { use gfx::font::ShapingFlags; use style::computed_values::text_rendering::T as TextRendering; use style::computed_values::word_break::T as WordBreak; @@ -537,20 +545,26 @@ impl TextRun { &mut None, ); - ( - font.metrics.ascent, - font.metrics.line_gap, - font.font_key, + BreakAndShapeResult { + font_ascent: font.metrics.ascent, + font_line_gap: font.metrics.line_gap, + font_key: font.font_key, runs, break_at_start, - ) + } }) } fn layout(&self, layout_context: &LayoutContext, ifc: &mut InlineFormattingContextState) { use style::values::generics::text::LineHeight; - let (font_ascent, font_line_gap, font_key, runs, _) = self.break_and_shape(layout_context); + let BreakAndShapeResult { + font_ascent, + font_line_gap, + font_key, + runs, + break_at_start: _, + } = self.break_and_shape(layout_context); let font_size = self.parent_style.get_font().font_size.size.0; let mut runs = runs.iter(); loop { From 6763e7e4aea076015e30223fb35731fd7826c0a2 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Wed, 4 Dec 2019 14:18:25 +0100 Subject: [PATCH 19/21] Review nits --- components/layout_2020/flow/construct.rs | 3 +-- components/layout_2020/flow/inline.rs | 11 +++++------ components/layout_2020/positioned.rs | 2 +- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/components/layout_2020/flow/construct.rs b/components/layout_2020/flow/construct.rs index 80f44b2ff81..5d9cd433434 100644 --- a/components/layout_2020/flow/construct.rs +++ b/components/layout_2020/flow/construct.rs @@ -189,14 +189,13 @@ impl BlockContainer { contains_floats: builder.contains_floats, outer_content_sizes_of_children: ContentSizes::zero(), }; - let request_childrens_outer_content_sizes = request_content_sizes; let iter = builder.block_level_boxes.into_par_iter(); let iter = iter.mapfold_reduce_into( &mut target, |target, (intermediate, box_slot): (Intermediate<_>, BoxSlot<'_>)| { let (block_level_box, box_contains_floats) = intermediate.finish( context, - if request_childrens_outer_content_sizes { + if request_content_sizes { Some(&mut target.outer_content_sizes_of_children) } else { None diff --git a/components/layout_2020/flow/inline.rs b/components/layout_2020/flow/inline.rs index 5fb01c0fa53..fe66d486e9a 100644 --- a/components/layout_2020/flow/inline.rs +++ b/components/layout_2020/flow/inline.rs @@ -406,9 +406,10 @@ fn layout_atomic<'box_tree>( .margin() .percentages_relative_to(cbis) .auto_is(Length::zero); - ifc.inline_position += padding.inline_start + border.inline_start + margin.inline_start; + let pbm = &(&padding + &border) + &margin; + ifc.inline_position += pbm.inline_start; let mut start_corner = Vec2 { - block: padding.block_start + border.block_start + margin.block_start, + block: pbm.block_start, inline: ifc.inline_position - ifc.current_nesting_level.inline_start, }; start_corner += &relative_adjustement( @@ -437,8 +438,7 @@ fn layout_atomic<'box_tree>( Err(non_replaced) => { let box_size = atomic.style.box_size(); let inline_size = box_size.inline.percentage_relative_to(cbis).auto_is(|| { - let available_size = - cbis - padding.inline_sum() - border.inline_sum() - margin.inline_sum(); + let available_size = cbis - pbm.inline_sum(); shrink_to_fit(&atomic.inline_content_sizes, available_size) }); let block_size = box_size @@ -482,8 +482,7 @@ fn layout_atomic<'box_tree>( }, }; - ifc.inline_position += - fragment.padding.inline_end + fragment.border.inline_end + fragment.margin.inline_end; + ifc.inline_position += pbm.inline_end; ifc.current_nesting_level .fragments_so_far .push(Fragment::Box(fragment)); diff --git a/components/layout_2020/positioned.rs b/components/layout_2020/positioned.rs index 7d61916b2c1..b99b6b8293f 100644 --- a/components/layout_2020/positioned.rs +++ b/components/layout_2020/positioned.rs @@ -307,7 +307,7 @@ impl<'a> AbsolutelyPositionedFragment<'a> { Ok(replaced) => { // FIXME: implement https://drafts.csswg.org/css2/visudet.html#abs-replaced-width // and https://drafts.csswg.org/css2/visudet.html#abs-replaced-height - let block_size = block_size.auto_is(|| Length::zero()); + let block_size = block_size.auto_is(Length::zero); let fragments = replaced.make_fragments( &self.absolutely_positioned_box.contents.style, Vec2 { From 38e8fd1e99de482d1e662b46f5d8d8454ac71c5a Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Wed, 4 Dec 2019 15:10:11 +0100 Subject: [PATCH 20/21] Replace boolean parameters by a new `ContentSizesRequest` enum --- components/layout_2020/flow/construct.rs | 91 +++++++++---------- components/layout_2020/flow/float.rs | 5 +- components/layout_2020/flow/root.rs | 3 +- components/layout_2020/formatting_contexts.rs | 14 +-- components/layout_2020/positioned.rs | 10 +- components/layout_2020/sizing.rs | 31 +++++++ 6 files changed, 92 insertions(+), 62 deletions(-) diff --git a/components/layout_2020/flow/construct.rs b/components/layout_2020/flow/construct.rs index 5d9cd433434..606628d5744 100644 --- a/components/layout_2020/flow/construct.rs +++ b/components/layout_2020/flow/construct.rs @@ -10,7 +10,7 @@ use crate::flow::inline::{InlineBox, InlineFormattingContext, InlineLevelBox, Te use crate::flow::{BlockContainer, BlockFormattingContext, BlockLevelBox}; use crate::formatting_contexts::IndependentFormattingContext; use crate::positioned::AbsolutelyPositionedBox; -use crate::sizing::{outer_inline_content_sizes, ContentSizes}; +use crate::sizing::{outer_inline_content_sizes, ContentSizes, ContentSizesRequest}; use crate::style_ext::{ComputedValuesExt, DisplayGeneratingBox, DisplayInside, DisplayOutside}; use rayon::iter::{IntoParallelIterator, ParallelIterator}; use rayon_croissant::ParallelIteratorExt; @@ -24,17 +24,17 @@ impl BlockFormattingContext { context: &LayoutContext, style: &Arc, contents: NonReplacedContents>, - request_content_sizes: bool, + content_sizes: ContentSizesRequest, ) -> (Self, Option) { - let (contents, contains_floats, content_sizes) = - BlockContainer::construct(context, style, contents, request_content_sizes); - // FIXME: add contribution to `content_sizes` of floats in this formatting context + let (contents, contains_floats, inline_content_sizes) = + BlockContainer::construct(context, style, contents, content_sizes); + // FIXME: add contribution to `inline_content_sizes` of floats in this formatting context // https://dbaron.org/css/intrinsic/#intrinsic let bfc = Self { contents, contains_floats: contains_floats == ContainsFloats::Yes, }; - (bfc, content_sizes) + (bfc, inline_content_sizes) } } @@ -133,7 +133,7 @@ impl BlockContainer { context: &LayoutContext, block_container_style: &Arc, contents: NonReplacedContents>, - request_content_sizes: bool, + content_sizes: ContentSizesRequest, ) -> (BlockContainer, ContainsFloats, Option) { let mut builder = BlockContainerBuilder { context, @@ -155,19 +155,15 @@ impl BlockContainer { .is_empty() { if builder.block_level_boxes.is_empty() { - let content_sizes = if request_content_sizes { - Some( - builder - .ongoing_inline_formatting_context - .inline_content_sizes(context), - ) - } else { - None - }; + let inline_content_sizes = content_sizes.if_requests_inline(|| { + builder + .ongoing_inline_formatting_context + .inline_content_sizes(context) + }); let container = BlockContainer::InlineFormattingContext( builder.ongoing_inline_formatting_context, ); - return (container, builder.contains_floats, content_sizes); + return (container, builder.contains_floats, inline_content_sizes); } builder.end_ongoing_inline_formatting_context(); } @@ -195,11 +191,8 @@ impl BlockContainer { |target, (intermediate, box_slot): (Intermediate<_>, BoxSlot<'_>)| { let (block_level_box, box_contains_floats) = intermediate.finish( context, - if request_content_sizes { - Some(&mut target.outer_content_sizes_of_children) - } else { - None - }, + content_sizes + .if_requests_inline(|| &mut target.outer_content_sizes_of_children), ); target.contains_floats |= box_contains_floats; box_slot.set(LayoutBox::BlockLevel(block_level_box.clone())); @@ -207,19 +200,21 @@ impl BlockContainer { }, |left, right| { left.contains_floats |= right.contains_floats; - if request_content_sizes { + if content_sizes.requests_inline() { left.outer_content_sizes_of_children .max_assign(&right.outer_content_sizes_of_children) } }, ); let container = BlockContainer::BlockLevelBoxes(iter.collect()); - let content_sizes = if request_content_sizes { - Some(target.outer_content_sizes_of_children) - } else { - None - }; - (container, target.contains_floats, content_sizes) + + let Target { + contains_floats, + outer_content_sizes_of_children, + } = target; + let inline_content_sizes = + content_sizes.if_requests_inline(|| outer_content_sizes_of_children); + (container, contains_floats, inline_content_sizes) } } @@ -393,14 +388,13 @@ where inline_box.last_fragment = true; Arc::new(InlineLevelBox::InlineBox(inline_box)) } else { - let request_content_sizes = style.inline_size_is_auto(); Arc::new(InlineLevelBox::Atomic( IndependentFormattingContext::construct( self.context, style.clone(), display_inside, contents, - request_content_sizes, + ContentSizesRequest::inline_if(style.inline_size_is_auto()), ), )) }; @@ -592,12 +586,16 @@ where ) -> (Arc, ContainsFloats) { match self { IntermediateBlockLevelBox::SameFormattingContextBlock { style, contents } => { - let request_content_sizes = max_assign_in_flow_outer_content_sizes_to.is_some() && - style.inline_size_is_auto(); - let (contents, contains_floats, content_sizes) = - contents.finish(context, &style, request_content_sizes); + let (contents, contains_floats, inline_content_sizes) = contents.finish( + context, + &style, + ContentSizesRequest::inline_if( + max_assign_in_flow_outer_content_sizes_to.is_some() && + style.inline_size_is_auto(), + ), + ); if let Some(to) = max_assign_in_flow_outer_content_sizes_to { - to.max_assign(&outer_inline_content_sizes(&style, &content_sizes)) + to.max_assign(&outer_inline_content_sizes(&style, &inline_content_sizes)) } let block_level_box = Arc::new(BlockLevelBox::SameFormattingContextBlock { contents, style }); @@ -608,14 +606,16 @@ where display_inside, contents, } => { - let request_content_sizes = max_assign_in_flow_outer_content_sizes_to.is_some() && - style.inline_size_is_auto(); + let content_sizes = ContentSizesRequest::inline_if( + max_assign_in_flow_outer_content_sizes_to.is_some() && + style.inline_size_is_auto(), + ); let contents = IndependentFormattingContext::construct( context, style, display_inside, contents, - request_content_sizes, + content_sizes, ); if let Some(to) = max_assign_in_flow_outer_content_sizes_to { to.max_assign(&outer_inline_content_sizes( @@ -660,25 +660,22 @@ where self, context: &LayoutContext, style: &Arc, - request_content_sizes: bool, + content_sizes: ContentSizesRequest, ) -> (BlockContainer, ContainsFloats, Option) { match self { IntermediateBlockContainer::Deferred { contents } => { - BlockContainer::construct(context, style, contents, request_content_sizes) + BlockContainer::construct(context, style, contents, content_sizes) }, IntermediateBlockContainer::InlineFormattingContext(ifc) => { - let content_sizes = if request_content_sizes { - Some(ifc.inline_content_sizes(context)) - } else { - None - }; + let inline_content_sizes = + content_sizes.if_requests_inline(|| ifc.inline_content_sizes(context)); // If that inline formatting context contained any float, those // were already taken into account during the first phase of // box construction. ( BlockContainer::InlineFormattingContext(ifc), ContainsFloats::No, - content_sizes, + inline_content_sizes, ) }, } diff --git a/components/layout_2020/flow/float.rs b/components/layout_2020/flow/float.rs index 6fcd06df0b5..2acc2095004 100644 --- a/components/layout_2020/flow/float.rs +++ b/components/layout_2020/flow/float.rs @@ -5,6 +5,7 @@ use crate::context::LayoutContext; use crate::dom_traversal::{Contents, NodeExt}; use crate::formatting_contexts::IndependentFormattingContext; +use crate::sizing::ContentSizesRequest; use crate::style_ext::{ComputedValuesExt, DisplayInside}; use servo_arc::Arc; use style::properties::ComputedValues; @@ -32,14 +33,14 @@ impl FloatBox { display_inside: DisplayInside, contents: Contents>, ) -> Self { - let request_content_sizes = style.inline_size_is_auto(); + let content_sizes = ContentSizesRequest::inline_if(style.inline_size_is_auto()); Self { contents: IndependentFormattingContext::construct( context, style, display_inside, contents, - request_content_sizes, + content_sizes, ), } } diff --git a/components/layout_2020/flow/root.rs b/components/layout_2020/flow/root.rs index d5358fd28b5..6396b4dd658 100644 --- a/components/layout_2020/flow/root.rs +++ b/components/layout_2020/flow/root.rs @@ -14,6 +14,7 @@ use crate::geom; use crate::geom::flow_relative::Vec2; use crate::positioned::AbsolutelyPositionedBox; use crate::replaced::ReplacedContent; +use crate::sizing::ContentSizesRequest; use crate::style_ext::{Direction, Display, DisplayGeneratingBox, DisplayInside, WritingMode}; use crate::{ContainingBlock, DefiniteContainingBlock}; use rayon::iter::{IntoParallelRefIterator, ParallelExtend, ParallelIterator}; @@ -83,7 +84,7 @@ fn construct_for_root_element<'dom>( style, display_inside, contents, - /* request_content_sizes */ false, + ContentSizesRequest::None, ), ))], ) diff --git a/components/layout_2020/formatting_contexts.rs b/components/layout_2020/formatting_contexts.rs index 0527d8eda38..8f62d3d8d6b 100644 --- a/components/layout_2020/formatting_contexts.rs +++ b/components/layout_2020/formatting_contexts.rs @@ -8,7 +8,7 @@ use crate::flow::BlockFormattingContext; use crate::fragments::Fragment; use crate::positioned::AbsolutelyPositionedFragment; use crate::replaced::ReplacedContent; -use crate::sizing::ContentSizes; +use crate::sizing::{ContentSizes, ContentSizesRequest}; use crate::style_ext::DisplayInside; use crate::ContainingBlock; use servo_arc::Arc; @@ -55,24 +55,24 @@ impl IndependentFormattingContext { style: Arc, display_inside: DisplayInside, contents: Contents>, - request_content_sizes: bool, + content_sizes: ContentSizesRequest, ) -> Self { use self::IndependentFormattingContextContents as Contents; let (contents, inline_content_sizes) = match contents.try_into() { Ok(non_replaced) => match display_inside { DisplayInside::Flow | DisplayInside::FlowRoot => { - let (bfc, content_sizes) = BlockFormattingContext::construct( + let (bfc, inline_content_sizes) = BlockFormattingContext::construct( context, &style, non_replaced, - request_content_sizes, + content_sizes, ); - (Contents::Flow(bfc), content_sizes) + (Contents::Flow(bfc), inline_content_sizes) }, }, Err(replaced) => { - let content_sizes = None; // Unused by layout code - (Contents::Replaced(replaced), content_sizes) + let inline_content_sizes = None; // Unused by layout code + (Contents::Replaced(replaced), inline_content_sizes) }, }; Self { diff --git a/components/layout_2020/positioned.rs b/components/layout_2020/positioned.rs index b99b6b8293f..f2a6551f6c1 100644 --- a/components/layout_2020/positioned.rs +++ b/components/layout_2020/positioned.rs @@ -7,7 +7,7 @@ use crate::dom_traversal::{Contents, NodeExt}; use crate::formatting_contexts::IndependentFormattingContext; use crate::fragments::{AnonymousFragment, BoxFragment, CollapsedBlockMargins, Fragment}; use crate::geom::flow_relative::{Rect, Sides, Vec2}; -use crate::sizing::shrink_to_fit; +use crate::sizing::{shrink_to_fit, ContentSizesRequest}; use crate::style_ext::{ComputedValuesExt, Direction, DisplayInside, WritingMode}; use crate::{ContainingBlock, DefiniteContainingBlock}; use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; @@ -53,21 +53,21 @@ impl AbsolutelyPositionedBox { contents: Contents>, ) -> Self { // "Shrink-to-fit" in https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-width - let request_content_sizes = { + let content_sizes = ContentSizesRequest::inline_if( // If inline-size is non-auto, that value is used without shrink-to-fit style.inline_size_is_auto() && // If it is, then the only case where shrink-to-fit is *not* used is // if both offsets are non-auto, leaving inline-size as the only variable // in the constraint equation. - !style.inline_box_offsets_are_both_non_auto() - }; + !style.inline_box_offsets_are_both_non_auto(), + ); Self { contents: IndependentFormattingContext::construct( context, style, display_inside, contents, - request_content_sizes, + content_sizes, ), } } diff --git a/components/layout_2020/sizing.rs b/components/layout_2020/sizing.rs index 0fdb36d7600..1a036dbd083 100644 --- a/components/layout_2020/sizing.rs +++ b/components/layout_2020/sizing.rs @@ -9,6 +9,37 @@ use style::properties::ComputedValues; use style::values::computed::{Length, LengthPercentage, Percentage}; use style::Zero; +/// Which min/max-content values should be computed during box construction +#[derive(Clone, Copy, Debug)] +pub(crate) enum ContentSizesRequest { + Inline, + None, +} + +impl ContentSizesRequest { + pub fn inline_if(condition: bool) -> Self { + if condition { + Self::Inline + } else { + Self::None + } + } + + pub fn requests_inline(self) -> bool { + match self { + Self::Inline => true, + Self::None => false, + } + } + + pub fn if_requests_inline(self, f: impl FnOnce() -> T) -> Option { + match self { + Self::Inline => Some(f()), + Self::None => None, + } + } +} + #[derive(Clone, Debug)] pub(crate) struct ContentSizes { pub min_content: Length, From dd9dfc66e324748ffce14be5df9ac61258941653 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Wed, 4 Dec 2019 15:36:05 +0100 Subject: [PATCH 21/21] Use a new `BoxContentSizes` enum instead of `Option` --- components/layout_2020/flow/construct.rs | 31 ++--- components/layout_2020/flow/inline.rs | 11 +- components/layout_2020/formatting_contexts.rs | 19 +-- components/layout_2020/positioned.rs | 10 +- components/layout_2020/sizing.rs | 128 ++++++++++-------- 5 files changed, 103 insertions(+), 96 deletions(-) diff --git a/components/layout_2020/flow/construct.rs b/components/layout_2020/flow/construct.rs index 606628d5744..c02dcd08fd7 100644 --- a/components/layout_2020/flow/construct.rs +++ b/components/layout_2020/flow/construct.rs @@ -10,7 +10,7 @@ use crate::flow::inline::{InlineBox, InlineFormattingContext, InlineLevelBox, Te use crate::flow::{BlockContainer, BlockFormattingContext, BlockLevelBox}; use crate::formatting_contexts::IndependentFormattingContext; use crate::positioned::AbsolutelyPositionedBox; -use crate::sizing::{outer_inline_content_sizes, ContentSizes, ContentSizesRequest}; +use crate::sizing::{BoxContentSizes, ContentSizes, ContentSizesRequest}; use crate::style_ext::{ComputedValuesExt, DisplayGeneratingBox, DisplayInside, DisplayOutside}; use rayon::iter::{IntoParallelIterator, ParallelIterator}; use rayon_croissant::ParallelIteratorExt; @@ -25,7 +25,7 @@ impl BlockFormattingContext { style: &Arc, contents: NonReplacedContents>, content_sizes: ContentSizesRequest, - ) -> (Self, Option) { + ) -> (Self, BoxContentSizes) { let (contents, contains_floats, inline_content_sizes) = BlockContainer::construct(context, style, contents, content_sizes); // FIXME: add contribution to `inline_content_sizes` of floats in this formatting context @@ -134,7 +134,7 @@ impl BlockContainer { block_container_style: &Arc, contents: NonReplacedContents>, content_sizes: ContentSizesRequest, - ) -> (BlockContainer, ContainsFloats, Option) { + ) -> (BlockContainer, ContainsFloats, BoxContentSizes) { let mut builder = BlockContainerBuilder { context, block_container_style, @@ -155,7 +155,7 @@ impl BlockContainer { .is_empty() { if builder.block_level_boxes.is_empty() { - let inline_content_sizes = content_sizes.if_requests_inline(|| { + let content_sizes = content_sizes.compute(|| { builder .ongoing_inline_formatting_context .inline_content_sizes(context) @@ -163,7 +163,7 @@ impl BlockContainer { let container = BlockContainer::InlineFormattingContext( builder.ongoing_inline_formatting_context, ); - return (container, builder.contains_floats, inline_content_sizes); + return (container, builder.contains_floats, content_sizes); } builder.end_ongoing_inline_formatting_context(); } @@ -212,9 +212,8 @@ impl BlockContainer { contains_floats, outer_content_sizes_of_children, } = target; - let inline_content_sizes = - content_sizes.if_requests_inline(|| outer_content_sizes_of_children); - (container, contains_floats, inline_content_sizes) + let content_sizes = content_sizes.compute(|| outer_content_sizes_of_children); + (container, contains_floats, content_sizes) } } @@ -586,7 +585,7 @@ where ) -> (Arc, ContainsFloats) { match self { IntermediateBlockLevelBox::SameFormattingContextBlock { style, contents } => { - let (contents, contains_floats, inline_content_sizes) = contents.finish( + let (contents, contains_floats, box_content_sizes) = contents.finish( context, &style, ContentSizesRequest::inline_if( @@ -595,7 +594,7 @@ where ), ); if let Some(to) = max_assign_in_flow_outer_content_sizes_to { - to.max_assign(&outer_inline_content_sizes(&style, &inline_content_sizes)) + to.max_assign(&box_content_sizes.outer_inline(&style)) } let block_level_box = Arc::new(BlockLevelBox::SameFormattingContextBlock { contents, style }); @@ -618,10 +617,7 @@ where content_sizes, ); if let Some(to) = max_assign_in_flow_outer_content_sizes_to { - to.max_assign(&outer_inline_content_sizes( - &contents.style, - &contents.inline_content_sizes, - )) + to.max_assign(&contents.content_sizes.outer_inline(&contents.style)) } ( Arc::new(BlockLevelBox::Independent(contents)), @@ -661,21 +657,20 @@ where context: &LayoutContext, style: &Arc, content_sizes: ContentSizesRequest, - ) -> (BlockContainer, ContainsFloats, Option) { + ) -> (BlockContainer, ContainsFloats, BoxContentSizes) { match self { IntermediateBlockContainer::Deferred { contents } => { BlockContainer::construct(context, style, contents, content_sizes) }, IntermediateBlockContainer::InlineFormattingContext(ifc) => { - let inline_content_sizes = - content_sizes.if_requests_inline(|| ifc.inline_content_sizes(context)); + let content_sizes = content_sizes.compute(|| ifc.inline_content_sizes(context)); // If that inline formatting context contained any float, those // were already taken into account during the first phase of // box construction. ( BlockContainer::InlineFormattingContext(ifc), ContainsFloats::No, - inline_content_sizes, + content_sizes, ) }, } diff --git a/components/layout_2020/flow/inline.rs b/components/layout_2020/flow/inline.rs index fe66d486e9a..4c6e364a8fa 100644 --- a/components/layout_2020/flow/inline.rs +++ b/components/layout_2020/flow/inline.rs @@ -10,7 +10,7 @@ use crate::fragments::CollapsedBlockMargins; use crate::fragments::{AnonymousFragment, BoxFragment, Fragment, TextFragment}; use crate::geom::flow_relative::{Rect, Sides, Vec2}; use crate::positioned::{AbsolutelyPositionedBox, AbsolutelyPositionedFragment}; -use crate::sizing::{outer_inline_content_sizes_and_percentages, shrink_to_fit, ContentSizes}; +use crate::sizing::ContentSizes; use crate::style_ext::{ComputedValuesExt, Display, DisplayGeneratingBox, DisplayOutside}; use crate::{relative_adjustement, ContainingBlock}; use app_units::Au; @@ -139,10 +139,9 @@ impl InlineFormattingContext { } }, InlineLevelBox::Atomic(atomic) => { - let (outer, pc) = outer_inline_content_sizes_and_percentages( - &atomic.style, - &atomic.inline_content_sizes, - ); + let (outer, pc) = atomic + .content_sizes + .outer_inline_and_percentages(&atomic.style); self.current_line.min_content += outer.min_content; self.current_line.max_content += outer.max_content; self.current_line_percentages += pc; @@ -439,7 +438,7 @@ fn layout_atomic<'box_tree>( let box_size = atomic.style.box_size(); let inline_size = box_size.inline.percentage_relative_to(cbis).auto_is(|| { let available_size = cbis - pbm.inline_sum(); - shrink_to_fit(&atomic.inline_content_sizes, available_size) + atomic.content_sizes.shrink_to_fit(available_size) }); let block_size = box_size .block diff --git a/components/layout_2020/formatting_contexts.rs b/components/layout_2020/formatting_contexts.rs index 8f62d3d8d6b..d7477182437 100644 --- a/components/layout_2020/formatting_contexts.rs +++ b/components/layout_2020/formatting_contexts.rs @@ -8,7 +8,7 @@ use crate::flow::BlockFormattingContext; use crate::fragments::Fragment; use crate::positioned::AbsolutelyPositionedFragment; use crate::replaced::ReplacedContent; -use crate::sizing::{ContentSizes, ContentSizesRequest}; +use crate::sizing::{BoxContentSizes, ContentSizesRequest}; use crate::style_ext::DisplayInside; use crate::ContainingBlock; use servo_arc::Arc; @@ -22,7 +22,7 @@ pub(crate) struct IndependentFormattingContext { pub style: Arc, /// If it was requested during construction - pub inline_content_sizes: Option, + pub content_sizes: BoxContentSizes, contents: IndependentFormattingContextContents, } @@ -58,27 +58,30 @@ impl IndependentFormattingContext { content_sizes: ContentSizesRequest, ) -> Self { use self::IndependentFormattingContextContents as Contents; - let (contents, inline_content_sizes) = match contents.try_into() { + let (contents, content_sizes) = match contents.try_into() { Ok(non_replaced) => match display_inside { DisplayInside::Flow | DisplayInside::FlowRoot => { - let (bfc, inline_content_sizes) = BlockFormattingContext::construct( + let (bfc, box_content_sizes) = BlockFormattingContext::construct( context, &style, non_replaced, content_sizes, ); - (Contents::Flow(bfc), inline_content_sizes) + (Contents::Flow(bfc), box_content_sizes) }, }, Err(replaced) => { - let inline_content_sizes = None; // Unused by layout code - (Contents::Replaced(replaced), inline_content_sizes) + // The `content_sizes` field is not used by layout code: + ( + Contents::Replaced(replaced), + BoxContentSizes::NoneWereRequested, + ) }, }; Self { style, contents, - inline_content_sizes, + content_sizes, } } diff --git a/components/layout_2020/positioned.rs b/components/layout_2020/positioned.rs index f2a6551f6c1..06d88709165 100644 --- a/components/layout_2020/positioned.rs +++ b/components/layout_2020/positioned.rs @@ -7,7 +7,7 @@ use crate::dom_traversal::{Contents, NodeExt}; use crate::formatting_contexts::IndependentFormattingContext; use crate::fragments::{AnonymousFragment, BoxFragment, CollapsedBlockMargins, Fragment}; use crate::geom::flow_relative::{Rect, Sides, Vec2}; -use crate::sizing::{shrink_to_fit, ContentSizesRequest}; +use crate::sizing::ContentSizesRequest; use crate::style_ext::{ComputedValuesExt, Direction, DisplayInside, WritingMode}; use crate::{ContainingBlock, DefiniteContainingBlock}; use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; @@ -295,10 +295,10 @@ impl<'a> AbsolutelyPositionedFragment<'a> { // FIXME: implement https://drafts.csswg.org/css2/visudet.html#abs-replaced-width available_size } else { - shrink_to_fit( - &self.absolutely_positioned_box.contents.inline_content_sizes, - available_size, - ) + self.absolutely_positioned_box + .contents + .content_sizes + .shrink_to_fit(available_size) } }); diff --git a/components/layout_2020/sizing.rs b/components/layout_2020/sizing.rs index 1a036dbd083..75c13b1dcb5 100644 --- a/components/layout_2020/sizing.rs +++ b/components/layout_2020/sizing.rs @@ -38,6 +38,13 @@ impl ContentSizesRequest { Self::None => None, } } + + pub fn compute(self, compute_inline: impl FnOnce() -> ContentSizes) -> BoxContentSizes { + match self { + Self::Inline => BoxContentSizes::Inline(compute_inline()), + Self::None => BoxContentSizes::NoneWereRequested, + } + } } #[derive(Clone, Debug)] @@ -74,69 +81,72 @@ impl ContentSizes { } } -/// https://dbaron.org/css/intrinsic/#outer-intrinsic -pub(crate) fn outer_inline_content_sizes( - style: &ComputedValues, - inner_content_sizes: &Option, -) -> ContentSizes { - let (mut outer, percentages) = - outer_inline_content_sizes_and_percentages(style, inner_content_sizes); - outer.adjust_for_pbm_percentages(percentages); - outer +/// Optional min/max-content for storage in the box tree +#[derive(Debug)] +pub(crate) enum BoxContentSizes { + NoneWereRequested, // … during box construction + Inline(ContentSizes), } -pub(crate) fn outer_inline_content_sizes_and_percentages( - style: &ComputedValues, - inner_content_sizes: &Option, -) -> (ContentSizes, Percentage) { - // FIXME: account for 'min-width', 'max-width', 'box-sizing' +impl BoxContentSizes { + fn expect_inline(&self) -> &ContentSizes { + match self { + Self::NoneWereRequested => panic!("Accessing content size that was not requested"), + Self::Inline(s) => s, + } + } - let inline_size = style.box_size().inline; - // Percentages for 'width' are treated as 'auto' - let inline_size = inline_size.map(|lp| lp.as_length()); - // The (inner) min/max-content are only used for 'auto' - let mut outer = match inline_size.non_auto().flatten() { - None => expect(inner_content_sizes).clone(), - Some(length) => ContentSizes { - min_content: length, - max_content: length, - }, - }; + /// https://dbaron.org/css/intrinsic/#outer-intrinsic + pub fn outer_inline(&self, style: &ComputedValues) -> ContentSizes { + let (mut outer, percentages) = self.outer_inline_and_percentages(style); + outer.adjust_for_pbm_percentages(percentages); + outer + } - let mut pbm_lengths = Length::zero(); - let mut pbm_percentages = Percentage::zero(); - let padding = style.padding(); - let border = style.border_width(); - let margin = style.margin(); - pbm_lengths += border.inline_sum(); - let mut add = |x: LengthPercentage| { - pbm_lengths += x.length_component(); - pbm_percentages += x.percentage_component(); - }; - add(padding.inline_start); - add(padding.inline_end); - margin.inline_start.non_auto().map(&mut add); - margin.inline_end.non_auto().map(&mut add); + pub(crate) fn outer_inline_and_percentages( + &self, + style: &ComputedValues, + ) -> (ContentSizes, Percentage) { + // FIXME: account for 'min-width', 'max-width', 'box-sizing' - outer.min_content += pbm_lengths; - outer.max_content += pbm_lengths; + let inline_size = style.box_size().inline; + // Percentages for 'width' are treated as 'auto' + let inline_size = inline_size.map(|lp| lp.as_length()); + // The (inner) min/max-content are only used for 'auto' + let mut outer = match inline_size.non_auto().flatten() { + None => self.expect_inline().clone(), + Some(length) => ContentSizes { + min_content: length, + max_content: length, + }, + }; - (outer, pbm_percentages) -} - -/// https://drafts.csswg.org/css2/visudet.html#shrink-to-fit-float -pub(crate) fn shrink_to_fit( - content_sizes: &Option, - available_size: Length, -) -> Length { - let content_sizes = expect(content_sizes); - available_size - .max(content_sizes.min_content) - .min(content_sizes.max_content) -} - -fn expect(content_sizes: &Option) -> &ContentSizes { - content_sizes - .as_ref() - .expect("Accessing content size that was not requested") + let mut pbm_lengths = Length::zero(); + let mut pbm_percentages = Percentage::zero(); + let padding = style.padding(); + let border = style.border_width(); + let margin = style.margin(); + pbm_lengths += border.inline_sum(); + let mut add = |x: LengthPercentage| { + pbm_lengths += x.length_component(); + pbm_percentages += x.percentage_component(); + }; + add(padding.inline_start); + add(padding.inline_end); + margin.inline_start.non_auto().map(&mut add); + margin.inline_end.non_auto().map(&mut add); + + outer.min_content += pbm_lengths; + outer.max_content += pbm_lengths; + + (outer, pbm_percentages) + } + + /// https://drafts.csswg.org/css2/visudet.html#shrink-to-fit-float + pub(crate) fn shrink_to_fit(&self, available_size: Length) -> Length { + let inline = self.expect_inline(); + available_size + .max(inline.min_content) + .min(inline.max_content) + } }