diff options
19 files changed, 824 insertions, 273 deletions
diff --git a/components/layout_2020/dom_traversal.rs b/components/layout_2020/dom_traversal.rs index 1a3ec7f1c98..056103d7205 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<ComputedValues>, - context: &SharedStyleContext, + context: &LayoutContext, handler: &mut impl TraversalHandler<'dom, Node>, items: Vec<PseudoElementContentItem>, ) 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::<Node::ConcreteElement>( - &context.guards, + &context.shared_context().guards, &PseudoElement::ServoText, &pseudo_element_style, ) @@ -187,6 +188,16 @@ fn traverse_pseudo_element_contents<'dom, Node>( } } +impl<Node> Contents<Node> { + /// 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<Node> std::convert::TryFrom<Contents<Node>> for NonReplacedContents<Node> { type Error = ReplacedContent; @@ -215,7 +226,7 @@ where pub(crate) fn traverse( self, inherited_style: &ServoArc<ComputedValues>, - context: &SharedStyleContext, + context: &LayoutContext, handler: &mut impl TraversalHandler<'dom, Node>, ) { match self { @@ -230,7 +241,7 @@ where fn pseudo_element_style<'dom, Node>( _which: WhichPseudoElement, _element: Node, - _context: &SharedStyleContext, + _context: &LayoutContext, ) -> Option<ServoArc<ComputedValues>> where Node: NodeExt<'dom>, @@ -243,7 +254,7 @@ where fn generate_pseudo_element_content<'dom, Node>( _pseudo_element_style: &ComputedValues, _element: Node, - _context: &SharedStyleContext, + _context: &LayoutContext, ) -> Vec<PseudoElementContentItem> where Node: NodeExt<'dom>, @@ -292,7 +303,7 @@ pub(crate) trait NodeExt<'dom>: 'dom + Copy + LayoutNode + Send + Sync { fn first_child(self) -> Option<Self>; fn next_sibling(self) -> Option<Self>; fn parent_node(self) -> Option<Self>; - fn style(self, context: &SharedStyleContext) -> ServoArc<ComputedValues>; + fn style(self, context: &LayoutContext) -> ServoArc<ComputedValues>; fn layout_data_mut(&self) -> AtomicRefMut<LayoutDataForElement>; fn element_box_slot(&self) -> BoxSlot<'dom>; @@ -349,8 +360,8 @@ where TNode::parent_node(&self) } - fn style(self, context: &SharedStyleContext) -> ServoArc<ComputedValues> { - self.to_threadsafe().style(context) + fn style(self, context: &LayoutContext) -> ServoArc<ComputedValues> { + self.to_threadsafe().style(context.shared_context()) } fn layout_data_mut(&self) -> AtomicRefMut<LayoutDataForElement> { diff --git a/components/layout_2020/flow/construct.rs b/components/layout_2020/flow/construct.rs index e7599bc843a..c02dcd08fd7 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; @@ -9,26 +10,31 @@ 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::sizing::{BoxContentSizes, ContentSizes, ContentSizesRequest}; +use crate::style_ext::{ComputedValuesExt, DisplayGeneratingBox, DisplayInside, DisplayOutside}; use rayon::iter::{IntoParallelIterator, ParallelIterator}; use rayon_croissant::ParallelIteratorExt; use servo_arc::Arc; -use std::convert::TryInto; -use style::context::SharedStyleContext; +use std::convert::{TryFrom, TryInto}; use style::properties::ComputedValues; use style::selector_parser::PseudoElement; impl BlockFormattingContext { pub fn construct<'dom>( - context: &SharedStyleContext<'_>, + context: &LayoutContext, style: &Arc<ComputedValues>, contents: NonReplacedContents<impl NodeExt<'dom>>, - ) -> Self { - let (contents, contains_floats) = BlockContainer::construct(context, style, contents); - Self { + content_sizes: ContentSizesRequest, + ) -> (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 + // https://dbaron.org/css/intrinsic/#intrinsic + let bfc = Self { contents, contains_floats: contains_floats == ContainsFloats::Yes, - } + }; + (bfc, inline_content_sizes) } } @@ -71,7 +77,7 @@ enum IntermediateBlockContainer<Node> { /// 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<ComputedValues>, @@ -123,19 +129,20 @@ 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<ComputedValues>, contents: NonReplacedContents<impl NodeExt<'dom>>, - ) -> (BlockContainer, ContainsFloats) { + content_sizes: ContentSizesRequest, + ) -> (BlockContainer, ContainsFloats, BoxContentSizes) { 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); @@ -148,32 +155,65 @@ impl BlockContainer { .is_empty() { if builder.block_level_boxes.is_empty() { + let content_sizes = content_sizes.compute(|| { + builder + .ongoing_inline_formatting_context + .inline_content_sizes(context) + }); 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(); } - let mut contains_floats = builder.contains_floats; - 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); - *contains_floats |= box_contains_floats; - box_slot.set(LayoutBox::BlockLevel(block_level_box.clone())); - block_level_box - }, - |left, right| *left |= right, - ) - .collect(), + type Intermediate<Node> = IntermediateBlockLevelBox<Node>; + 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 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, + 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())); + block_level_box + }, + |left, right| { + left.contains_floats |= right.contains_floats; + if content_sizes.requests_inline() { + left.outer_content_sizes_of_children + .max_assign(&right.outer_content_sizes_of_children) + } + }, ); - (container, contains_floats) + let container = BlockContainer::BlockLevelBoxes(iter.collect()); + + let Target { + contains_floats, + outer_content_sizes_of_children, + } = target; + let content_sizes = content_sizes.compute(|| outer_content_sizes_of_children); + (container, contains_floats, content_sizes) } } @@ -324,38 +364,38 @@ where display_inside: DisplayInside, contents: Contents<Node>, ) -> Arc<InlineLevelBox> { - 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 { + Arc::new(InlineLevelBox::Atomic( IndependentFormattingContext::construct( self.context, style.clone(), display_inside, - <Contents<Node>>::Replaced(replaced), + contents, + ContentSizesRequest::inline_if(style.inline_size_is_auto()), ), - )), - 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_ @@ -451,14 +491,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_)) @@ -482,14 +515,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_)) } @@ -509,9 +540,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::<Node::ConcreteElement>( - &context.guards, + &context.shared_context().guards, &PseudoElement::ServoText, &block_container_style, ) @@ -546,13 +578,24 @@ impl<'dom, Node> IntermediateBlockLevelBox<Node> where Node: NodeExt<'dom>, { - fn finish<'style>( + fn finish( self, - context: &SharedStyleContext<'style>, + context: &LayoutContext, + max_assign_in_flow_outer_content_sizes_to: Option<&mut ContentSizes>, ) -> (Arc<BlockLevelBox>, ContainsFloats) { match self { IntermediateBlockLevelBox::SameFormattingContextBlock { style, contents } => { - let (contents, contains_floats) = contents.finish(context, &style); + let (contents, contains_floats, box_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(&box_content_sizes.outer_inline(&style)) + } let block_level_box = Arc::new(BlockLevelBox::SameFormattingContextBlock { contents, style }); (block_level_box, contains_floats) @@ -562,12 +605,20 @@ where display_inside, contents, } => { + 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, + content_sizes, ); + if let Some(to) = max_assign_in_flow_outer_content_sizes_to { + to.max_assign(&contents.content_sizes.outer_inline(&contents.style)) + } ( Arc::new(BlockLevelBox::Independent(contents)), ContainsFloats::No, @@ -579,14 +630,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) }, @@ -595,14 +639,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) }, } @@ -613,22 +652,25 @@ impl<'dom, Node> IntermediateBlockContainer<Node> where Node: NodeExt<'dom>, { - fn finish<'style>( + fn finish( self, - context: &SharedStyleContext<'style>, + context: &LayoutContext, style: &Arc<ComputedValues>, - ) -> (BlockContainer, ContainsFloats) { + content_sizes: ContentSizesRequest, + ) -> (BlockContainer, ContainsFloats, BoxContentSizes) { match self { IntermediateBlockContainer::Deferred { contents } => { - BlockContainer::construct(context, style, contents) + BlockContainer::construct(context, style, contents, content_sizes) }, IntermediateBlockContainer::InlineFormattingContext(ifc) => { + 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, + content_sizes, ) }, } @@ -648,9 +690,3 @@ impl std::ops::BitOrAssign for ContainsFloats { } } } - -impl Default for ContainsFloats { - fn default() -> Self { - ContainsFloats::No - } -} diff --git a/components/layout_2020/flow/float.rs b/components/layout_2020/flow/float.rs index f9be366bc97..2acc2095004 100644 --- a/components/layout_2020/flow/float.rs +++ b/components/layout_2020/flow/float.rs @@ -2,7 +2,13 @@ * 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::sizing::ContentSizesRequest; +use crate::style_ext::{ComputedValuesExt, DisplayInside}; +use servo_arc::Arc; +use style::properties::ComputedValues; #[derive(Debug)] pub(crate) struct FloatBox { @@ -19,3 +25,23 @@ impl FloatContext { FloatContext {} } } + +impl FloatBox { + pub fn construct<'dom>( + context: &LayoutContext, + style: Arc<ComputedValues>, + display_inside: DisplayInside, + contents: Contents<impl NodeExt<'dom>>, + ) -> Self { + let content_sizes = ContentSizesRequest::inline_if(style.inline_size_is_auto()); + Self { + contents: IndependentFormattingContext::construct( + context, + style, + display_inside, + contents, + content_sizes, + ), + } + } +} diff --git a/components/layout_2020/flow/inline.rs b/components/layout_2020/flow/inline.rs index 33d1d8a0ed9..4c6e364a8fa 100644 --- a/components/layout_2020/flow/inline.rs +++ b/components/layout_2020/flow/inline.rs @@ -10,12 +10,16 @@ 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::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; +use style::values::computed::{Length, LengthPercentage, Percentage}; use style::Zero; +use webrender_api::FontInstanceKey; #[derive(Debug, Default)] pub(crate) struct InlineFormattingContext { @@ -63,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<AbsolutelyPositionedFragment<'box_tree>>, + containing_block: &'a ContainingBlock, line_boxes: LinesBoxes, inline_position: Length, partial_inline_boxes_stack: Vec<PartialInlineBoxFragment<'box_tree>>, @@ -77,6 +82,114 @@ struct LinesBoxes { } impl InlineFormattingContext { + // This works on an already-constructed `InlineFormattingContext`, + // Which would have to change if/when + // `BlockContainer::construct` parallelize their construction. + pub(super) fn inline_content_sizes(&self, layout_context: &LayoutContext) -> ContentSizes { + struct Computation { + paragraph: ContentSizes, + current_line: ContentSizes, + current_line_percentages: Percentage, + } + impl Computation { + fn traverse( + &mut self, + layout_context: &LayoutContext, + inline_level_boxes: &[Arc<InlineLevelBox>], + ) { + 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 { + ($condition: ident, $side: ident) => { + if inline_box.$condition { + self.add_lengthpercentage(padding.$side); + self.add_length(border.$side); + if let Some(lp) = margin.$side.non_auto() { + self.add_lengthpercentage(lp) + } + } + }; + } + + add!(first_fragment, inline_start); + self.traverse(layout_context, &inline_box.children); + add!(last_fragment, inline_end); + }, + InlineLevelBox::TextRun(text_run) => { + let BreakAndShapeResult { + 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 (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; + }, + InlineLevelBox::OutOfFlowFloatBox(_) | + InlineLevelBox::OutOfFlowAbsolutelyPositionedBox(_) => {}, + } + } + } + + 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 + .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<T: Zero>(x: &mut T) -> T { + std::mem::replace(x, T::zero()) + } + let mut computation = Computation { + paragraph: ContentSizes::zero(), + current_line: ContentSizes::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, @@ -85,6 +198,7 @@ impl InlineFormattingContext { absolutely_positioned_fragments: &mut Vec<AbsolutelyPositionedFragment<'a>>, ) -> FlowLayout { let mut ifc = InlineFormattingContextState { + absolutely_positioned_fragments, containing_block, partial_inline_boxes_stack: Vec::new(), line_boxes: LinesBoxes { @@ -107,10 +221,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) { @@ -131,12 +242,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 @@ -282,12 +392,114 @@ 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); + let pbm = &(&padding + &border) + &margin; + ifc.inline_position += pbm.inline_start; + let mut start_corner = Vec2 { + block: pbm.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 + // 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: 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 - pbm.inline_sum(); + atomic.content_sizes.shrink_to_fit(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 += pbm.inline_end; + ifc.current_nesting_level + .fragments_so_far + .push(Fragment::Box(fragment)); +} + +struct BreakAndShapeResult { + font_ascent: Au, + font_line_gap: Au, + font_key: FontInstanceKey, + runs: Vec<GlyphRun>, + break_at_start: bool, +} + impl TextRun { - fn layout(&self, layout_context: &LayoutContext, ifc: &mut InlineFormattingContextState) { + 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; - 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 +528,41 @@ 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(); - - let (runs, _break_at_start) = gfx::text::text_run::TextRun::break_and_shape( - &mut font, - &self.text, - &shaping_options, - &mut None, - ); + 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(); - ( - font.metrics.ascent, - font.metrics.line_gap, - font.font_key, - runs, - ) - }); + let (runs, break_at_start) = gfx::text::text_run::TextRun::break_and_shape( + &mut font, + &self.text, + &shaping_options, + &mut None, + ); + + 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 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 { diff --git a/components/layout_2020/flow/mod.rs b/components/layout_2020/flow/mod.rs index d7b9854668a..cbbefdabb1b 100644 --- a/components/layout_2020/flow/mod.rs +++ b/components/layout_2020/flow/mod.rs @@ -656,17 +656,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, @@ -677,14 +671,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/flow/root.rs b/components/layout_2020/flow/root.rs index 7c062f12f47..6396b4dd658 100644 --- a/components/layout_2020/flow/root.rs +++ b/components/layout_2020/flow/root.rs @@ -14,12 +14,12 @@ 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}; 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 +28,7 @@ pub struct BoxTreeRoot(BlockFormattingContext); pub struct FragmentTreeRoot(Vec<Fragment>); 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 +41,7 @@ impl BoxTreeRoot { } fn construct_for_root_element<'dom>( - context: &SharedStyleContext<'_>, + context: &LayoutContext, root_element: impl NodeExt<'dom>, ) -> (ContainsFloats, Vec<Arc<BlockLevelBox>>) { let style = root_element.style(context); @@ -60,32 +60,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, + ContentSizesRequest::None, + ), + ))], ) } } diff --git a/components/layout_2020/formatting_contexts.rs b/components/layout_2020/formatting_contexts.rs index 0517f599852..d7477182437 100644 --- a/components/layout_2020/formatting_contexts.rs +++ b/components/layout_2020/formatting_contexts.rs @@ -8,11 +8,11 @@ use crate::flow::BlockFormattingContext; use crate::fragments::Fragment; use crate::positioned::AbsolutelyPositionedFragment; use crate::replaced::ReplacedContent; +use crate::sizing::{BoxContentSizes, ContentSizesRequest}; 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; @@ -20,6 +20,10 @@ use style::values::computed::Length; #[derive(Debug)] pub(crate) struct IndependentFormattingContext { pub style: Arc<ComputedValues>, + + /// If it was requested during construction + pub content_sizes: BoxContentSizes, + contents: IndependentFormattingContextContents, } @@ -46,22 +50,39 @@ enum NonReplacedIFCKind<'a> { } impl IndependentFormattingContext { - pub fn construct<'dom, 'style>( - context: &SharedStyleContext<'style>, + pub fn construct<'dom>( + context: &LayoutContext, style: Arc<ComputedValues>, display_inside: DisplayInside, contents: Contents<impl NodeExt<'dom>>, + content_sizes: ContentSizesRequest, ) -> Self { use self::IndependentFormattingContextContents as Contents; - let contents = match contents.try_into() { + let (contents, 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, box_content_sizes) = BlockFormattingContext::construct( + context, + &style, + non_replaced, + content_sizes, + ); + (Contents::Flow(bfc), box_content_sizes) + }, + }, + Err(replaced) => { + // The `content_sizes` field is not used by layout code: + ( + Contents::Replaced(replaced), + BoxContentSizes::NoneWereRequested, + ) }, - Err(replaced) => Contents::Replaced(replaced), }; - Self { style, contents } + Self { + style, + contents, + content_sizes, + } } pub fn as_replaced(&self) -> Result<&ReplacedContent, NonReplacedIFC> { @@ -73,24 +94,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<AbsolutelyPositionedFragment<'a>>, - ) -> 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/lib.rs b/components/layout_2020/lib.rs index 80254421e66..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; @@ -18,6 +19,7 @@ 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/positioned.rs b/components/layout_2020/positioned.rs index 3577a86642b..06d88709165 100644 --- a/components/layout_2020/positioned.rs +++ b/components/layout_2020/positioned.rs @@ -3,12 +3,16 @@ * 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::sizing::ContentSizesRequest; +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 +46,32 @@ pub(crate) enum AbsoluteBoxOffsets<NonStatic> { } impl AbsolutelyPositionedBox { + pub fn construct<'dom>( + context: &LayoutContext, + style: Arc<ComputedValues>, + display_inside: DisplayInside, + contents: Contents<impl NodeExt<'dom>>, + ) -> Self { + // "Shrink-to-fit" in https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-width + 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(), + ); + Self { + contents: IndependentFormattingContext::construct( + context, + style, + display_inside, + contents, + content_sizes, + ), + } + } + pub(crate) fn layout<'a>( &'a self, initial_start_corner: Vec2<Length>, @@ -256,28 +286,60 @@ 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 { + self.absolutely_positioned_box + .contents + .content_sizes + .shrink_to_fit(available_size) + } }); - let containing_block_for_children = ContainingBlock { - inline_size, - block_size, - mode: style.writing_mode(), + let mut absolutely_positioned_fragments = Vec::new(); + let mut independent_layout = match self.absolutely_positioned_box.contents.as_replaced() { + 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, + &containing_block_for_children, + dummy_tree_rank, + &mut absolutely_positioned_fragments, + ) + }, }; - // 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; - 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 inline_start = match inline_anchor { Anchor::Start(start) => start + pb.inline_start + margin.inline_start, @@ -307,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<ComputedValues>, - 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<Length>, + ) -> Vec<Fragment> { + 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::<Vec<_>>(); - (fragments, block_size) - }, - }; - IndependentLayout { - fragments, - content_block_size, + }) + .into_iter() + .collect(), } } } diff --git a/components/layout_2020/sizing.rs b/components/layout_2020/sizing.rs new file mode 100644 index 00000000000..75c13b1dcb5 --- /dev/null +++ b/components/layout_2020/sizing.rs @@ -0,0 +1,152 @@ +/* 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; + +/// 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<T>(self, f: impl FnOnce() -> T) -> Option<T> { + match self { + Self::Inline => Some(f()), + 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)] +pub(crate) struct ContentSizes { + pub min_content: Length, + pub max_content: Length, +} + +/// https://drafts.csswg.org/css-sizing/#intrinsic-sizes +impl ContentSizes { + pub fn zero() -> Self { + Self { + min_content: Length::zero(), + max_content: Length::zero(), + } + } + + 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 + // (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); + } + } +} + +/// Optional min/max-content for storage in the box tree +#[derive(Debug)] +pub(crate) enum BoxContentSizes { + NoneWereRequested, // … during box construction + Inline(ContentSizes), +} + +impl BoxContentSizes { + fn expect_inline(&self) -> &ContentSizes { + match self { + Self::NoneWereRequested => panic!("Accessing content size that was not requested"), + Self::Inline(s) => s, + } + } + + /// 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 + } + + pub(crate) fn outer_inline_and_percentages( + &self, + style: &ComputedValues, + ) -> (ContentSizes, Percentage) { + // FIXME: account for 'min-width', 'max-width', 'box-sizing' + + 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, + }, + }; + + 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) + } +} diff --git a/components/layout_2020/style_ext.rs b/components/layout_2020/style_ext.rs index 8c145f23c6f..5748eac1b02 100644 --- a/components/layout_2020/style_ext.rs +++ b/components/layout_2020/style_ext.rs @@ -45,6 +45,9 @@ 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 inline_box_offsets_are_both_non_auto(&self) -> bool; fn box_offsets(&self) -> flow_relative::Sides<LengthPercentageOrAuto>; fn box_size(&self) -> flow_relative::Vec2<LengthPercentageOrAuto>; fn min_box_size(&self) -> flow_relative::Vec2<LengthPercentageOrAuto>; @@ -62,6 +65,39 @@ 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) + } + + 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) + } else { + (position.top, position.bottom) + }; + matches!( + offsets, + ( + LengthPercentageOrAuto::LengthPercentage(_), + LengthPercentageOrAuto::LengthPercentage(_), + ) + ) + } + #[inline] fn box_offsets(&self) -> flow_relative::Sides<LengthPercentageOrAuto> { let position = self.get_position(); 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..45202d51ad9 100644 --- a/components/layout_thread_2020/lib.rs +++ b/components/layout_thread_2020/lib.rs @@ -1081,9 +1081,9 @@ impl LayoutThread { let box_tree = if token.should_traverse() { driver::traverse_dom(&traversal, token, Some(rayon_pool)); - let shared = DomTraversal::<ServoLayoutElement>::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 diff --git a/components/style/values/computed/length.rs b/components/style/values/computed/length.rs index 53cf7cbd696..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 { @@ -186,6 +192,16 @@ impl LengthPercentage { } } + /// Returns the length component if this could be represented as a + /// non-calc length. + pub fn as_length(&self) -> Option<Length> { + 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<Percentage> { 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<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where 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<T>(&self, f: impl FnOnce(LengthPercentage) -> T) -> LengthPercentageOrAuto<T> { match self { LengthPercentageOrAuto::LengthPercentage(l) => { LengthPercentageOrAuto::LengthPercentage(f(l.clone())) 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 |