diff options
Diffstat (limited to 'components/layout_2020/flow')
-rw-r--r-- | components/layout_2020/flow/construct.rs | 222 | ||||
-rw-r--r-- | components/layout_2020/flow/float.rs | 3 | ||||
-rw-r--r-- | components/layout_2020/flow/inline.rs | 197 | ||||
-rw-r--r-- | components/layout_2020/flow/mod.rs | 54 | ||||
-rw-r--r-- | components/layout_2020/flow/root.rs | 138 |
5 files changed, 376 insertions, 238 deletions
diff --git a/components/layout_2020/flow/construct.rs b/components/layout_2020/flow/construct.rs index cff3fa281a6..fe9465f775e 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::cell::ArcRefCell; use crate::context::LayoutContext; use crate::dom_traversal::{BoxSlot, Contents, NodeExt, NonReplacedContents, TraversalHandler}; use crate::element_data::LayoutBox; @@ -18,6 +19,7 @@ use servo_arc::Arc; use std::convert::{TryFrom, TryInto}; use style::properties::ComputedValues; use style::selector_parser::PseudoElement; +use style::values::specified::text::TextDecorationLine; impl BlockFormattingContext { pub fn construct<'dom>( @@ -26,9 +28,16 @@ impl BlockFormattingContext { style: &Arc<ComputedValues>, contents: NonReplacedContents, content_sizes: ContentSizesRequest, + propagated_text_decoration_line: TextDecorationLine, ) -> (Self, BoxContentSizes) { - let (contents, contains_floats, inline_content_sizes) = - BlockContainer::construct(context, node, style, contents, content_sizes); + let (contents, contains_floats, inline_content_sizes) = BlockContainer::construct( + context, + node, + style, + contents, + content_sizes, + propagated_text_decoration_line, + ); // FIXME: add contribution to `inline_content_sizes` of floats in this formatting context // https://dbaron.org/css/intrinsic/#intrinsic let bfc = Self { @@ -51,6 +60,7 @@ enum BlockLevelCreator { Independent { display_inside: DisplayInside, contents: Contents, + propagated_text_decoration_line: TextDecorationLine, }, OutOfFlowAbsolutelyPositionedBox { display_inside: DisplayInside, @@ -71,7 +81,7 @@ enum BlockLevelCreator { /// Deferring allows using rayon’s `into_par_iter`. enum IntermediateBlockContainer { InlineFormattingContext(InlineFormattingContext), - Deferred(NonReplacedContents), + Deferred(NonReplacedContents, TextDecorationLine), } /// A builder for a block container. @@ -139,13 +149,16 @@ impl BlockContainer { block_container_style: &Arc<ComputedValues>, contents: NonReplacedContents, content_sizes: ContentSizesRequest, + propagated_text_decoration_line: TextDecorationLine, ) -> (BlockContainer, ContainsFloats, BoxContentSizes) { + let text_decoration_line = + propagated_text_decoration_line | block_container_style.clone_text_decoration_line(); let mut builder = BlockContainerBuilder { context, root, block_container_style, block_level_boxes: Vec::new(), - ongoing_inline_formatting_context: InlineFormattingContext::default(), + ongoing_inline_formatting_context: InlineFormattingContext::new(text_decoration_line), ongoing_inline_boxes_stack: Vec::new(), anonymous_style: None, contains_floats: ContainsFloats::No, @@ -282,54 +295,48 @@ where // context with the parent style of that builder. let inlines = self.current_inline_level_boxes(); - fn last_text(inlines: &mut [Arc<InlineLevelBox>]) -> Option<&mut String> { - let last = inlines.last_mut()?; - if let InlineLevelBox::TextRun(_) = &**last { - // We never clone text run boxes, so the refcount is 1 and unwrap succeeds: - let last = Arc::get_mut(last).unwrap(); - if let InlineLevelBox::TextRun(TextRun { text, .. }) = last { - Some(text) - } else { - unreachable!() - } - } else { - None - } - } - let mut new_text_run_contents; let output; - if let Some(text) = last_text(inlines) { - // Append to the existing text run - new_text_run_contents = None; - output = text; - } else { - new_text_run_contents = Some(String::new()); - output = new_text_run_contents.as_mut().unwrap(); - } - if leading_whitespace { - output.push(' ') - } - loop { - if let Some(i) = input.bytes().position(|b| b.is_ascii_whitespace()) { - let (non_whitespace, rest) = input.split_at(i); - output.push_str(non_whitespace); - output.push(' '); - if let Some(i) = rest.bytes().position(|b| !b.is_ascii_whitespace()) { - input = &rest[i..]; + { + let mut last_box = inlines.last_mut().map(|last| last.borrow_mut()); + let last_text = last_box.as_mut().and_then(|last| match &mut **last { + InlineLevelBox::TextRun(last) => Some(&mut last.text), + _ => None, + }); + + if let Some(text) = last_text { + // Append to the existing text run + new_text_run_contents = None; + output = text; + } else { + new_text_run_contents = Some(String::new()); + output = new_text_run_contents.as_mut().unwrap(); + } + + if leading_whitespace { + output.push(' ') + } + loop { + if let Some(i) = input.bytes().position(|b| b.is_ascii_whitespace()) { + let (non_whitespace, rest) = input.split_at(i); + output.push_str(non_whitespace); + output.push(' '); + if let Some(i) = rest.bytes().position(|b| !b.is_ascii_whitespace()) { + input = &rest[i..]; + } else { + break; + } } else { + output.push_str(input); break; } - } else { - output.push_str(input); - break; } } if let Some(text) = new_text_run_contents { let parent_style = parent_style.clone(); - inlines.push(Arc::new(InlineLevelBox::TextRun(TextRun { + inlines.push(ArcRefCell::new(InlineLevelBox::TextRun(TextRun { tag: node.as_opaque(), parent_style, text, @@ -353,29 +360,53 @@ where if !text.starts_with(|c: char| c.is_ascii_whitespace()) { return (false, text); } - let mut inline_level_boxes = self.current_inline_level_boxes().iter().rev(); - let mut stack = Vec::new(); - let preserved = loop { - match inline_level_boxes.next().map(|b| &**b) { - Some(InlineLevelBox::TextRun(r)) => break !r.text.ends_with(' '), - Some(InlineLevelBox::Atomic { .. }) => break false, - Some(InlineLevelBox::OutOfFlowAbsolutelyPositionedBox(_)) | - Some(InlineLevelBox::OutOfFlowFloatBox(_)) => {}, - Some(InlineLevelBox::InlineBox(b)) => { - stack.push(inline_level_boxes); - inline_level_boxes = b.children.iter().rev() - }, - None => { - if let Some(iter) = stack.pop() { - inline_level_boxes = iter - } else { - break false; // Paragraph start - } - }, - } + + let preserved = match whitespace_is_preserved(self.current_inline_level_boxes()) { + WhitespacePreservedResult::Unknown => { + // Paragraph start. + false + }, + WhitespacePreservedResult::NotPreserved => false, + WhitespacePreservedResult::Preserved => true, }; + let text = text.trim_start_matches(|c: char| c.is_ascii_whitespace()); - (preserved, text) + return (preserved, text); + + fn whitespace_is_preserved( + inline_level_boxes: &[ArcRefCell<InlineLevelBox>], + ) -> WhitespacePreservedResult { + for inline_level_box in inline_level_boxes.iter().rev() { + match *inline_level_box.borrow() { + InlineLevelBox::TextRun(ref r) => { + if r.text.ends_with(' ') { + return WhitespacePreservedResult::NotPreserved; + } + return WhitespacePreservedResult::Preserved; + }, + InlineLevelBox::Atomic { .. } => { + return WhitespacePreservedResult::NotPreserved; + }, + InlineLevelBox::OutOfFlowAbsolutelyPositionedBox(_) | + InlineLevelBox::OutOfFlowFloatBox(_) => {}, + InlineLevelBox::InlineBox(ref b) => { + match whitespace_is_preserved(&b.children) { + WhitespacePreservedResult::Unknown => {}, + result => return result, + } + }, + } + } + + WhitespacePreservedResult::Unknown + } + + #[derive(Clone, Copy, PartialEq)] + enum WhitespacePreservedResult { + Preserved, + NotPreserved, + Unknown, + } } fn handle_inline_level_element( @@ -384,7 +415,7 @@ where style: &Arc<ComputedValues>, display_inside: DisplayInside, contents: Contents, - ) -> Arc<InlineLevelBox> { + ) -> ArcRefCell<InlineLevelBox> { 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 @@ -410,9 +441,9 @@ where .pop() .expect("no ongoing inline level box found"); inline_box.last_fragment = true; - Arc::new(InlineLevelBox::InlineBox(inline_box)) + ArcRefCell::new(InlineLevelBox::InlineBox(inline_box)) } else { - Arc::new(InlineLevelBox::Atomic( + ArcRefCell::new(InlineLevelBox::Atomic( IndependentFormattingContext::construct( self.context, node, @@ -420,6 +451,8 @@ where display_inside, contents, ContentSizesRequest::inline_if(!style.inline_size_is_length()), + // Text decorations are not propagated to atomic inline-level descendants. + TextDecorationLine::NONE, ), )) }; @@ -466,15 +499,18 @@ where for mut fragmented_parent_inline_box in fragmented_inline_boxes { fragmented_parent_inline_box .children - .push(Arc::new(fragmented_inline)); + .push(ArcRefCell::new(fragmented_inline)); fragmented_inline = InlineLevelBox::InlineBox(fragmented_parent_inline_box); } self.ongoing_inline_formatting_context .inline_level_boxes - .push(Arc::new(fragmented_inline)); + .push(ArcRefCell::new(fragmented_inline)); } + let propagated_text_decoration_line = + self.ongoing_inline_formatting_context.text_decoration_line; + // We found a block level element, so the ongoing inline formatting // context needs to be ended. self.end_ongoing_inline_formatting_context(); @@ -482,11 +518,12 @@ where let kind = match contents.try_into() { Ok(contents) => match display_inside { DisplayInside::Flow => BlockLevelCreator::SameFormattingContextBlock( - IntermediateBlockContainer::Deferred(contents), + IntermediateBlockContainer::Deferred(contents, propagated_text_decoration_line), ), _ => BlockLevelCreator::Independent { display_inside, contents: contents.into(), + propagated_text_decoration_line, }, }, Err(contents) => { @@ -494,6 +531,7 @@ where BlockLevelCreator::Independent { display_inside, contents, + propagated_text_decoration_line, } }, }; @@ -525,7 +563,7 @@ where kind, }); } else { - let box_ = Arc::new(InlineLevelBox::OutOfFlowAbsolutelyPositionedBox( + let box_ = ArcRefCell::new(InlineLevelBox::OutOfFlowAbsolutelyPositionedBox(Arc::new( AbsolutelyPositionedBox::construct( self.context, node, @@ -533,7 +571,7 @@ where display_inside, contents, ), - )); + ))); self.current_inline_level_boxes().push(box_.clone()); box_slot.set(LayoutBox::InlineLevel(box_)) } @@ -561,7 +599,7 @@ where kind, }); } else { - let box_ = Arc::new(InlineLevelBox::OutOfFlowFloatBox(FloatBox::construct( + let box_ = ArcRefCell::new(InlineLevelBox::OutOfFlowFloatBox(FloatBox::construct( self.context, node, style, @@ -610,7 +648,7 @@ where }); } - fn current_inline_level_boxes(&mut self) -> &mut Vec<Arc<InlineLevelBox>> { + fn current_inline_level_boxes(&mut self) -> &mut Vec<ArcRefCell<InlineLevelBox>> { match self.ongoing_inline_boxes_stack.last_mut() { Some(last) => &mut last.children, None => &mut self.ongoing_inline_formatting_context.inline_level_boxes, @@ -634,7 +672,7 @@ where self, context: &LayoutContext, max_assign_in_flow_outer_content_sizes_to: Option<&mut ContentSizes>, - ) -> (Arc<BlockLevelBox>, ContainsFloats) { + ) -> (ArcRefCell<BlockLevelBox>, ContainsFloats) { let node = self.node; let style = self.style; let (block_level_box, contains_floats) = match self.kind { @@ -651,7 +689,7 @@ where 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 { + let block_level_box = ArcRefCell::new(BlockLevelBox::SameFormattingContextBlock { tag: node.as_opaque(), contents, style, @@ -661,6 +699,7 @@ where BlockLevelCreator::Independent { display_inside, contents, + propagated_text_decoration_line, } => { let content_sizes = ContentSizesRequest::inline_if( max_assign_in_flow_outer_content_sizes_to.is_some() && @@ -673,12 +712,13 @@ where display_inside, contents, content_sizes, + propagated_text_decoration_line, ); 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)), + ArcRefCell::new(BlockLevelBox::Independent(contents)), ContainsFloats::No, ) }, @@ -686,22 +726,23 @@ where display_inside, contents, } => { - let block_level_box = Arc::new(BlockLevelBox::OutOfFlowAbsolutelyPositionedBox( - AbsolutelyPositionedBox::construct( - context, - node, - style, - display_inside, - contents, - ), - )); + let block_level_box = + ArcRefCell::new(BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(Arc::new( + AbsolutelyPositionedBox::construct( + context, + node, + style, + display_inside, + contents, + ), + ))); (block_level_box, ContainsFloats::No) }, BlockLevelCreator::OutOfFlowFloatBox { display_inside, contents, } => { - let block_level_box = Arc::new(BlockLevelBox::OutOfFlowFloatBox( + let block_level_box = ArcRefCell::new(BlockLevelBox::OutOfFlowFloatBox( FloatBox::construct(context, node, style, display_inside, contents), )); (block_level_box, ContainsFloats::Yes) @@ -722,8 +763,15 @@ impl IntermediateBlockContainer { content_sizes: ContentSizesRequest, ) -> (BlockContainer, ContainsFloats, BoxContentSizes) { match self { - IntermediateBlockContainer::Deferred(contents) => { - BlockContainer::construct(context, node, style, contents, content_sizes) + IntermediateBlockContainer::Deferred(contents, propagated_text_decoration_line) => { + BlockContainer::construct( + context, + node, + style, + contents, + content_sizes, + propagated_text_decoration_line, + ) }, IntermediateBlockContainer::InlineFormattingContext(ifc) => { let content_sizes = content_sizes.compute(|| ifc.inline_content_sizes(context)); diff --git a/components/layout_2020/flow/float.rs b/components/layout_2020/flow/float.rs index 5b34392ac8c..4a647f23071 100644 --- a/components/layout_2020/flow/float.rs +++ b/components/layout_2020/flow/float.rs @@ -9,6 +9,7 @@ use crate::sizing::ContentSizesRequest; use crate::style_ext::{ComputedValuesExt, DisplayInside}; use servo_arc::Arc; use style::properties::ComputedValues; +use style::values::specified::text::TextDecorationLine; #[derive(Debug, Serialize)] pub(crate) struct FloatBox { @@ -43,6 +44,8 @@ impl FloatBox { display_inside, contents, content_sizes, + // Text decorations are not propagated to any out-of-flow descendants + TextDecorationLine::NONE, ), } } diff --git a/components/layout_2020/flow/inline.rs b/components/layout_2020/flow/inline.rs index bd8f5bc784f..6588a6a3137 100644 --- a/components/layout_2020/flow/inline.rs +++ b/components/layout_2020/flow/inline.rs @@ -2,16 +2,20 @@ * 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::cell::ArcRefCell; use crate::context::LayoutContext; use crate::flow::float::FloatBox; use crate::flow::FlowLayout; use crate::formatting_contexts::IndependentFormattingContext; use crate::fragments::{ AbsoluteOrFixedPositionedFragment, AnonymousFragment, BoxFragment, CollapsedBlockMargins, - DebugId, Fragment, TextFragment, + DebugId, FontMetrics, Fragment, TextFragment, }; use crate::geom::flow_relative::{Rect, Sides, Vec2}; -use crate::positioned::{relative_adjustement, AbsolutelyPositionedBox, PositioningContext}; +use crate::positioned::{ + relative_adjustement, AbsolutelyPositionedBox, HoistedAbsolutelyPositionedBox, + PositioningContext, +}; use crate::sizing::ContentSizes; use crate::style_ext::{ComputedValuesExt, Display, DisplayGeneratingBox, DisplayOutside}; use crate::ContainingBlock; @@ -22,19 +26,21 @@ use style::dom::OpaqueNode; use style::properties::ComputedValues; use style::values::computed::{Length, LengthPercentage, Percentage}; use style::values::specified::text::TextAlignKeyword; +use style::values::specified::text::TextDecorationLine; use style::Zero; use webrender_api::FontInstanceKey; #[derive(Debug, Default, Serialize)] pub(crate) struct InlineFormattingContext { - pub(super) inline_level_boxes: Vec<Arc<InlineLevelBox>>, + pub(super) inline_level_boxes: Vec<ArcRefCell<InlineLevelBox>>, + pub(super) text_decoration_line: TextDecorationLine, } #[derive(Debug, Serialize)] pub(crate) enum InlineLevelBox { InlineBox(InlineBox), TextRun(TextRun), - OutOfFlowAbsolutelyPositionedBox(AbsolutelyPositionedBox), + OutOfFlowAbsolutelyPositionedBox(Arc<AbsolutelyPositionedBox>), OutOfFlowFloatBox(FloatBox), Atomic(IndependentFormattingContext), } @@ -46,7 +52,7 @@ pub(crate) struct InlineBox { pub style: Arc<ComputedValues>, pub first_fragment: bool, pub last_fragment: bool, - pub children: Vec<Arc<InlineLevelBox>>, + pub children: Vec<ArcRefCell<InlineLevelBox>>, } /// https://www.w3.org/TR/css-display-3/#css-text-run @@ -59,10 +65,16 @@ pub(crate) struct TextRun { } struct InlineNestingLevelState<'box_tree> { - remaining_boxes: std::slice::Iter<'box_tree, Arc<InlineLevelBox>>, + remaining_boxes: InlineBoxChildIter<'box_tree>, fragments_so_far: Vec<Fragment>, inline_start: Length, max_block_size_of_fragments_so_far: Length, + positioning_context: Option<PositioningContext>, + /// Indicates whether this nesting level have text decorations in effect. + /// From https://drafts.csswg.org/css-text-decor/#line-decoration + // "When specified on or propagated to a block container that establishes + // an IFC..." + text_decoration_line: TextDecorationLine, } struct PartialInlineBoxFragment<'box_tree> { @@ -77,7 +89,7 @@ struct PartialInlineBoxFragment<'box_tree> { } struct InlineFormattingContextState<'box_tree, 'a, 'b> { - positioning_context: &'a mut PositioningContext<'box_tree>, + positioning_context: &'a mut PositioningContext, containing_block: &'b ContainingBlock<'b>, lines: Lines, inline_position: Length, @@ -85,6 +97,31 @@ struct InlineFormattingContextState<'box_tree, 'a, 'b> { current_nesting_level: InlineNestingLevelState<'box_tree>, } +impl<'box_tree, 'a, 'b> InlineFormattingContextState<'box_tree, 'a, 'b> { + fn push_hoisted_box_to_positioning_context( + &mut self, + hoisted_box: HoistedAbsolutelyPositionedBox, + ) { + if let Some(context) = self.current_nesting_level.positioning_context.as_mut() { + context.push(hoisted_box); + return; + } + + for nesting_level in self.partial_inline_boxes_stack.iter_mut().rev() { + if let Some(context) = nesting_level + .parent_nesting_level + .positioning_context + .as_mut() + { + context.push(hoisted_box); + return; + } + } + + self.positioning_context.push(hoisted_box); + } +} + struct Lines { // One anonymous fragment per line fragments: Vec<Fragment>, @@ -92,6 +129,13 @@ struct Lines { } impl InlineFormattingContext { + pub(super) fn new(text_decoration_line: TextDecorationLine) -> InlineFormattingContext { + InlineFormattingContext { + inline_level_boxes: Default::default(), + text_decoration_line, + } + } + // This works on an already-constructed `InlineFormattingContext`, // Which would have to change if/when // `BlockContainer::construct` parallelize their construction. @@ -105,10 +149,10 @@ impl InlineFormattingContext { fn traverse( &mut self, layout_context: &LayoutContext, - inline_level_boxes: &[Arc<InlineLevelBox>], + inline_level_boxes: &[ArcRefCell<InlineLevelBox>], ) { for inline_level_box in inline_level_boxes { - match &**inline_level_box { + match &*inline_level_box.borrow() { InlineLevelBox::InlineBox(inline_box) => { let padding = inline_box.style.padding(); let border = inline_box.style.border_width(); @@ -204,10 +248,10 @@ impl InlineFormattingContext { computation.paragraph } - pub(super) fn layout<'a>( - &'a self, + pub(super) fn layout( + &self, layout_context: &LayoutContext, - positioning_context: &mut PositioningContext<'a>, + positioning_context: &mut PositioningContext, containing_block: &ContainingBlock, tree_rank: usize, ) -> FlowLayout { @@ -221,17 +265,20 @@ impl InlineFormattingContext { }, inline_position: Length::zero(), current_nesting_level: InlineNestingLevelState { - remaining_boxes: self.inline_level_boxes.iter(), + remaining_boxes: InlineBoxChildIter::from_formatting_context(self), fragments_so_far: Vec::with_capacity(self.inline_level_boxes.len()), inline_start: Length::zero(), max_block_size_of_fragments_so_far: Length::zero(), + positioning_context: None, + text_decoration_line: self.text_decoration_line, }, }; + loop { if let Some(child) = ifc.current_nesting_level.remaining_boxes.next() { - match &**child { + match &*child.borrow() { InlineLevelBox::InlineBox(inline) => { - let partial = inline.start_layout(&mut ifc); + let partial = inline.start_layout(child.clone(), &mut ifc); ifc.partial_inline_boxes_stack.push(partial) }, InlineLevelBox::TextRun(run) => run.layout(layout_context, &mut ifc), @@ -256,14 +303,17 @@ impl InlineFormattingContext { panic!("display:none does not generate an abspos box") }, }; - let hoisted_fragment = box_.to_hoisted(initial_start_corner, tree_rank); - let hoisted_fragment_id = hoisted_fragment.fragment_id; - ifc.positioning_context.push(hoisted_fragment); - ifc.lines - .fragments - .push(Fragment::AbsoluteOrFixedPositioned( - AbsoluteOrFixedPositionedFragment(hoisted_fragment_id), - )); + let hoisted_box = box_.clone().to_hoisted(initial_start_corner, tree_rank); + let hoisted_fragment = hoisted_box.fragment.clone(); + ifc.push_hoisted_box_to_positioning_context(hoisted_box); + ifc.current_nesting_level.fragments_so_far.push( + Fragment::AbsoluteOrFixedPositioned( + AbsoluteOrFixedPositionedFragment { + hoisted_fragment, + position: box_.contents.style.clone_position(), + }, + ), + ); }, InlineLevelBox::OutOfFlowFloatBox(_box_) => { // TODO @@ -273,6 +323,7 @@ impl InlineFormattingContext { // Reached the end of ifc.remaining_boxes if let Some(mut partial) = ifc.partial_inline_boxes_stack.pop() { partial.finish_layout( + layout_context, &mut ifc.current_nesting_level, &mut ifc.inline_position, false, @@ -353,6 +404,7 @@ impl Lines { block: line_block_size, }; self.next_line_block_position += size.block; + self.fragments .push(Fragment::Anonymous(AnonymousFragment::new( Rect { start_corner, size }, @@ -364,7 +416,8 @@ impl Lines { impl InlineBox { fn start_layout<'box_tree>( - &'box_tree self, + &self, + this_inline_level_box: ArcRefCell<InlineLevelBox>, ifc: &mut InlineFormattingContextState<'box_tree, '_, '_>, ) -> PartialInlineBoxFragment<'box_tree> { let style = self.style.clone(); @@ -389,6 +442,9 @@ impl InlineBox { if style.clone_position().is_relative() { start_corner += &relative_adjustement(&style, ifc.containing_block) } + let positioning_context = PositioningContext::new_for_style(&style); + let text_decoration_line = + ifc.current_nesting_level.text_decoration_line | style.clone_text_decoration_line(); PartialInlineBoxFragment { tag: self.tag, style, @@ -400,10 +456,14 @@ impl InlineBox { parent_nesting_level: std::mem::replace( &mut ifc.current_nesting_level, InlineNestingLevelState { - remaining_boxes: self.children.iter(), + remaining_boxes: InlineBoxChildIter::from_inline_level_box( + this_inline_level_box, + ), fragments_so_far: Vec::with_capacity(self.children.len()), inline_start: ifc.inline_position, max_block_size_of_fragments_so_far: Length::zero(), + positioning_context, + text_decoration_line: text_decoration_line, }, ), } @@ -413,6 +473,7 @@ impl InlineBox { impl<'box_tree> PartialInlineBoxFragment<'box_tree> { fn finish_layout( &mut self, + layout_context: &LayoutContext, nesting_level: &mut InlineNestingLevelState, inline_position: &mut Length, at_line_break: bool, @@ -434,7 +495,6 @@ impl<'box_tree> PartialInlineBoxFragment<'box_tree> { self.border.clone(), self.margin.clone(), CollapsedBlockMargins::zero(), - None, // hoisted_fragment_id ); let last_fragment = self.last_box_tree_fragment && !at_line_break; if last_fragment { @@ -454,16 +514,21 @@ impl<'box_tree> PartialInlineBoxFragment<'box_tree> { fragment.border.block_sum() + fragment.margin.block_sum(), ); + + if let Some(context) = nesting_level.positioning_context.as_mut() { + context.layout_collected_children(layout_context, &mut fragment); + } + self.parent_nesting_level .fragments_so_far .push(Fragment::Box(fragment)); } } -fn layout_atomic<'box_tree>( +fn layout_atomic( layout_context: &LayoutContext, - ifc: &mut InlineFormattingContextState<'box_tree, '_, '_>, - atomic: &'box_tree IndependentFormattingContext, + ifc: &mut InlineFormattingContextState, + atomic: &IndependentFormattingContext, ) { let cbis = ifc.containing_block.inline_size; let padding = atomic.style.padding().percentages_relative_to(cbis); @@ -497,7 +562,6 @@ fn layout_atomic<'box_tree>( border, margin, CollapsedBlockMargins::zero(), - None, // hoisted_fragment_id ) }, Err(non_replaced) => { @@ -573,7 +637,6 @@ fn layout_atomic<'box_tree>( border, margin, CollapsedBlockMargins::zero(), - None, // hoisted_fragment_id ) }, }; @@ -588,8 +651,7 @@ fn layout_atomic<'box_tree>( } struct BreakAndShapeResult { - font_ascent: Au, - font_line_gap: Au, + font_metrics: FontMetrics, font_key: FontInstanceKey, runs: Vec<GlyphRun>, break_at_start: bool, @@ -656,8 +718,7 @@ impl TextRun { ); BreakAndShapeResult { - font_ascent: font.metrics.ascent, - font_line_gap: font.metrics.line_gap, + font_metrics: (&font.metrics).into(), font_key: font.font_key, runs, break_at_start, @@ -669,8 +730,7 @@ impl TextRun { use style::values::generics::text::LineHeight; let BreakAndShapeResult { - font_ascent, - font_line_gap, + font_metrics, font_key, runs, break_at_start: _, @@ -707,7 +767,7 @@ impl TextRun { } } let line_height = match self.parent_style.get_inherited_text().line_height { - LineHeight::Normal => font_line_gap.into(), + LineHeight::Normal => font_metrics.line_gap, LineHeight::Number(n) => font_size * n.0, LineHeight::Length(l) => l.0, }; @@ -732,9 +792,10 @@ impl TextRun { debug_id: DebugId::new(), parent_style: self.parent_style.clone(), rect, - ascent: font_ascent.into(), + font_metrics, font_key, glyphs, + text_decoration_line: ifc.current_nesting_level.text_decoration_line, })); if runs.is_empty() { break; @@ -743,7 +804,12 @@ impl TextRun { ifc.current_nesting_level.inline_start = Length::zero(); let mut nesting_level = &mut ifc.current_nesting_level; for partial in ifc.partial_inline_boxes_stack.iter_mut().rev() { - partial.finish_layout(nesting_level, &mut ifc.inline_position, true); + partial.finish_layout( + layout_context, + nesting_level, + &mut ifc.inline_position, + true, + ); partial.start_corner.inline = Length::zero(); partial.padding.inline_start = Length::zero(); partial.border.inline_start = Length::zero(); @@ -758,3 +824,54 @@ impl TextRun { } } } + +enum InlineBoxChildIter<'box_tree> { + InlineFormattingContext(std::slice::Iter<'box_tree, ArcRefCell<InlineLevelBox>>), + InlineBox { + inline_level_box: ArcRefCell<InlineLevelBox>, + child_index: usize, + }, +} + +impl<'box_tree> InlineBoxChildIter<'box_tree> { + fn from_formatting_context( + inline_formatting_context: &'box_tree InlineFormattingContext, + ) -> InlineBoxChildIter<'box_tree> { + InlineBoxChildIter::InlineFormattingContext( + inline_formatting_context.inline_level_boxes.iter(), + ) + } + + fn from_inline_level_box( + inline_level_box: ArcRefCell<InlineLevelBox>, + ) -> InlineBoxChildIter<'box_tree> { + InlineBoxChildIter::InlineBox { + inline_level_box, + child_index: 0, + } + } +} + +impl<'box_tree> Iterator for InlineBoxChildIter<'box_tree> { + type Item = ArcRefCell<InlineLevelBox>; + fn next(&mut self) -> Option<ArcRefCell<InlineLevelBox>> { + match *self { + InlineBoxChildIter::InlineFormattingContext(ref mut iter) => iter.next().cloned(), + InlineBoxChildIter::InlineBox { + ref inline_level_box, + ref mut child_index, + } => match *inline_level_box.borrow() { + InlineLevelBox::InlineBox(ref inline_box) => { + if *child_index >= inline_box.children.len() { + return None; + } + + let kid = inline_box.children[*child_index].clone(); + *child_index += 1; + Some(kid) + }, + _ => unreachable!(), + }, + } + } +} diff --git a/components/layout_2020/flow/mod.rs b/components/layout_2020/flow/mod.rs index e15947f52b0..dcf2f9c4de3 100644 --- a/components/layout_2020/flow/mod.rs +++ b/components/layout_2020/flow/mod.rs @@ -4,6 +4,7 @@ //! Flow layout, also known as block-and-inline layout. +use crate::cell::ArcRefCell; use crate::context::LayoutContext; use crate::flow::float::{FloatBox, FloatContext}; use crate::flow::inline::InlineFormattingContext; @@ -38,7 +39,7 @@ pub(crate) struct BlockFormattingContext { #[derive(Debug, Serialize)] pub(crate) enum BlockContainer { - BlockLevelBoxes(Vec<Arc<BlockLevelBox>>), + BlockLevelBoxes(Vec<ArcRefCell<BlockLevelBox>>), InlineFormattingContext(InlineFormattingContext), } @@ -50,7 +51,7 @@ pub(crate) enum BlockLevelBox { style: Arc<ComputedValues>, contents: BlockContainer, }, - OutOfFlowAbsolutelyPositionedBox(AbsolutelyPositionedBox), + OutOfFlowAbsolutelyPositionedBox(Arc<AbsolutelyPositionedBox>), OutOfFlowFloatBox(FloatBox), Independent(IndependentFormattingContext), } @@ -65,10 +66,10 @@ struct FlowLayout { struct CollapsibleWithParentStartMargin(bool); impl BlockFormattingContext { - pub(super) fn layout<'a>( - &'a self, + pub(super) fn layout( + &self, layout_context: &LayoutContext, - positioning_context: &mut PositioningContext<'a>, + positioning_context: &mut PositioningContext, containing_block: &ContainingBlock, tree_rank: usize, ) -> IndependentLayout { @@ -101,10 +102,10 @@ impl BlockFormattingContext { } impl BlockContainer { - fn layout<'a>( - &'a self, + fn layout( + &self, layout_context: &LayoutContext, - positioning_context: &mut PositioningContext<'a>, + positioning_context: &mut PositioningContext, containing_block: &ContainingBlock, tree_rank: usize, float_context: Option<&mut FloatContext>, @@ -130,10 +131,10 @@ impl BlockContainer { } } -fn layout_block_level_children<'a>( +fn layout_block_level_children( layout_context: &LayoutContext, - positioning_context: &mut PositioningContext<'a>, - child_boxes: &'a [Arc<BlockLevelBox>], + positioning_context: &mut PositioningContext, + child_boxes: &[ArcRefCell<BlockLevelBox>], containing_block: &ContainingBlock, tree_rank: usize, mut float_context: Option<&mut FloatContext>, @@ -204,7 +205,7 @@ fn layout_block_level_children<'a>( .iter() .enumerate() .map(|(tree_rank, box_)| { - let mut fragment = box_.layout( + let mut fragment = box_.borrow().layout( layout_context, positioning_context, containing_block, @@ -224,7 +225,7 @@ fn layout_block_level_children<'a>( .mapfold_reduce_into( positioning_context, |positioning_context, (tree_rank, box_)| { - box_.layout( + box_.borrow().layout( layout_context, positioning_context, containing_block, @@ -256,10 +257,10 @@ fn layout_block_level_children<'a>( } impl BlockLevelBox { - fn layout<'a>( - &'a self, + fn layout( + &self, layout_context: &LayoutContext, - positioning_context: &mut PositioningContext<'a>, + positioning_context: &mut PositioningContext, containing_block: &ContainingBlock, tree_rank: usize, float_context: Option<&mut FloatContext>, @@ -314,12 +315,13 @@ impl BlockLevelBox { )) }, BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(box_) => { - let hoisted_fragment = box_.to_hoisted(Vec2::zero(), tree_rank); - let hoisted_fragment_id = hoisted_fragment.fragment_id.clone(); - positioning_context.push(hoisted_fragment); - Fragment::AbsoluteOrFixedPositioned(AbsoluteOrFixedPositionedFragment( - hoisted_fragment_id, - )) + let hoisted_box = box_.clone().to_hoisted(Vec2::zero(), tree_rank); + let hoisted_fragment = hoisted_box.fragment.clone(); + positioning_context.push(hoisted_box); + Fragment::AbsoluteOrFixedPositioned(AbsoluteOrFixedPositionedFragment { + hoisted_fragment, + position: box_.contents.style.clone_position(), + }) }, BlockLevelBox::OutOfFlowFloatBox(_box_) => { // FIXME: call layout_maybe_position_relative_fragment here @@ -338,13 +340,13 @@ enum NonReplacedContents<'a> { /// https://drafts.csswg.org/css2/visudet.html#blockwidth /// https://drafts.csswg.org/css2/visudet.html#normal-block -fn layout_in_flow_non_replaced_block_level<'a>( +fn layout_in_flow_non_replaced_block_level( layout_context: &LayoutContext, - positioning_context: &mut PositioningContext<'a>, + positioning_context: &mut PositioningContext, containing_block: &ContainingBlock, tag: OpaqueNode, style: &Arc<ComputedValues>, - block_level_kind: NonReplacedContents<'a>, + block_level_kind: NonReplacedContents, tree_rank: usize, float_context: Option<&mut FloatContext>, ) -> BoxFragment { @@ -500,7 +502,6 @@ fn layout_in_flow_non_replaced_block_level<'a>( border, margin, block_margins_collapsed_with_children, - None, // hoisted_fragment_id ) } @@ -552,7 +553,6 @@ fn layout_in_flow_replaced_block_level<'a>( border, margin, block_margins_collapsed_with_children, - None, // hoisted_fragment_id ) } diff --git a/components/layout_2020/flow/root.rs b/components/layout_2020/flow/root.rs index 0b93701b01b..adbcb26d4c9 100644 --- a/components/layout_2020/flow/root.rs +++ b/components/layout_2020/flow/root.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::cell::ArcRefCell; use crate::context::LayoutContext; use crate::display_list::stacking_context::{ ContainingBlock, ContainingBlockInfo, StackingContext, StackingContextBuildMode, @@ -37,7 +38,7 @@ pub struct BoxTreeRoot(BlockFormattingContext); #[derive(Serialize)] pub struct FragmentTreeRoot { /// The children of the root of the fragment tree. - children: Vec<Fragment>, + children: Vec<ArcRefCell<Fragment>>, /// The scrollable overflow of the root of the fragment tree. scrollable_overflow: PhysicalRect<Length>, @@ -49,7 +50,7 @@ pub struct FragmentTreeRoot { impl BoxTreeRoot { pub fn construct<'dom, Node>(context: &LayoutContext, root_element: Node) -> Self where - Node: 'dom + Copy + LayoutNode + Send + Sync, + Node: 'dom + Copy + LayoutNode<'dom> + Send + Sync, { let (contains_floats, boxes) = construct_for_root_element(&context, root_element); Self(BlockFormattingContext { @@ -62,7 +63,7 @@ impl BoxTreeRoot { fn construct_for_root_element<'dom>( context: &LayoutContext, root_element: impl NodeExt<'dom>, -) -> (ContainsFloats, Vec<Arc<BlockLevelBox>>) { +) -> (ContainsFloats, Vec<ArcRefCell<BlockLevelBox>>) { let style = root_element.style(context); let replaced = ReplacedContent::for_element(root_element); let box_style = style.get_box(); @@ -83,27 +84,30 @@ fn construct_for_root_element<'dom>( if box_style.position.is_absolutely_positioned() { ( ContainsFloats::No, - vec![Arc::new(BlockLevelBox::OutOfFlowAbsolutelyPositionedBox( - AbsolutelyPositionedBox::construct( - context, - root_element, - style, - display_inside, - contents, - ), - ))], + vec![ArcRefCell::new( + BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(Arc::new( + AbsolutelyPositionedBox::construct( + context, + root_element, + style, + display_inside, + contents, + ), + )), + )], ) } else if box_style.float.is_floating() { ( ContainsFloats::Yes, - vec![Arc::new(BlockLevelBox::OutOfFlowFloatBox( + vec![ArcRefCell::new(BlockLevelBox::OutOfFlowFloatBox( FloatBox::construct(context, root_element, style, display_inside, contents), ))], ) } else { + let propagated_text_decoration_line = style.clone_text_decoration_line(); ( ContainsFloats::No, - vec![Arc::new(BlockLevelBox::Independent( + vec![ArcRefCell::new(BlockLevelBox::Independent( IndependentFormattingContext::construct( context, root_element, @@ -111,6 +115,7 @@ fn construct_for_root_element<'dom>( display_inside, contents, ContentSizesRequest::None, + propagated_text_decoration_line, ), ))], ) @@ -142,44 +147,47 @@ impl BoxTreeRoot { let dummy_tree_rank = 0; let mut positioning_context = PositioningContext::new_for_containing_block_for_all_descendants(); - let mut independent_layout = self.0.layout( + let independent_layout = self.0.layout( layout_context, &mut positioning_context, &(&initial_containing_block).into(), dummy_tree_rank, ); + let mut children = independent_layout + .fragments + .into_iter() + .map(|fragment| ArcRefCell::new(fragment)) + .collect(); positioning_context.layout_initial_containing_block_children( layout_context, &initial_containing_block, - &mut independent_layout.fragments, + &mut children, ); - let scrollable_overflow = - independent_layout - .fragments - .iter() - .fold(PhysicalRect::zero(), |acc, child| { - let child_overflow = child.scrollable_overflow(); + let scrollable_overflow = children.iter().fold(PhysicalRect::zero(), |acc, child| { + let child_overflow = child + .borrow() + .scrollable_overflow(&physical_containing_block); - // https://drafts.csswg.org/css-overflow/#scrolling-direction - // We want to clip scrollable overflow on box-start and inline-start - // sides of the scroll container. - // - // FIXME(mrobinson, bug 25564): This should take into account writing - // mode. - let child_overflow = PhysicalRect::new( - euclid::Point2D::zero(), - euclid::Size2D::new( - child_overflow.size.width + child_overflow.origin.x, - child_overflow.size.height + child_overflow.origin.y, - ), - ); - acc.union(&child_overflow) - }); + // https://drafts.csswg.org/css-overflow/#scrolling-direction + // We want to clip scrollable overflow on box-start and inline-start + // sides of the scroll container. + // + // FIXME(mrobinson, bug 25564): This should take into account writing + // mode. + let child_overflow = PhysicalRect::new( + euclid::Point2D::zero(), + euclid::Size2D::new( + child_overflow.size.width + child_overflow.origin.x, + child_overflow.size.height + child_overflow.origin.y, + ), + ); + acc.union(&child_overflow) + }); FragmentTreeRoot { - children: independent_layout.fragments, + children, scrollable_overflow, initial_containing_block: physical_containing_block, } @@ -197,12 +205,12 @@ impl FragmentTreeRoot { containing_block_for_all_descendants: ContainingBlock::new( &self.initial_containing_block, stacking_context_builder.current_space_and_clip, - &self.children, ), }; for fragment in &self.children { - fragment.build_stacking_context_tree( + fragment.borrow().build_stacking_context_tree( + fragment, &mut stacking_context_builder, &containing_block_info, &mut stacking_context, @@ -218,7 +226,7 @@ impl FragmentTreeRoot { pub fn print(&self) { let mut print_tree = PrintTree::new("Fragment Tree".to_string()); for fragment in &self.children { - fragment.print(&mut print_tree); + fragment.borrow().print(&mut print_tree); } } @@ -233,49 +241,11 @@ impl FragmentTreeRoot { &self, mut process_func: impl FnMut(&Fragment, &PhysicalRect<Length>) -> Option<T>, ) -> Option<T> { - fn recur<T>( - fragments: &[Fragment], - containing_block: &PhysicalRect<Length>, - process_func: &mut impl FnMut(&Fragment, &PhysicalRect<Length>) -> Option<T>, - ) -> Option<T> { - for fragment in fragments { - if let Some(result) = process_func(fragment, containing_block) { - return Some(result); - } - - match fragment { - Fragment::Box(fragment) => { - let new_containing_block = fragment - .content_rect - .to_physical(fragment.style.writing_mode, containing_block) - .translate(containing_block.origin.to_vector()); - if let Some(result) = - recur(&fragment.children, &new_containing_block, process_func) - { - return Some(result); - } - }, - Fragment::Anonymous(fragment) => { - let new_containing_block = fragment - .rect - .to_physical(fragment.mode, containing_block) - .translate(containing_block.origin.to_vector()); - if let Some(result) = - recur(&fragment.children, &new_containing_block, process_func) - { - return Some(result); - } - }, - _ => {}, - } - } - None - } - recur( - &self.children, - &self.initial_containing_block, - &mut process_func, - ) + self.children.iter().find_map(|child| { + child + .borrow() + .find(&self.initial_containing_block, &mut process_func) + }) } pub fn get_content_box_for_node(&self, requested_node: OpaqueNode) -> Rect<Au> { |