diff options
Diffstat (limited to 'components/layout_2020/flow/mod.rs')
-rw-r--r-- | components/layout_2020/flow/mod.rs | 77 |
1 files changed, 66 insertions, 11 deletions
diff --git a/components/layout_2020/flow/mod.rs b/components/layout_2020/flow/mod.rs index c04f85ae351..49fc65ad17e 100644 --- a/components/layout_2020/flow/mod.rs +++ b/components/layout_2020/flow/mod.rs @@ -27,7 +27,7 @@ use crate::formatting_contexts::{ Baselines, IndependentFormattingContext, IndependentLayout, NonReplacedFormattingContext, }; use crate::fragment_tree::{ - BaseFragmentInfo, BoxFragment, CollapsedBlockMargins, CollapsedMargin, Fragment, + BaseFragmentInfo, BoxFragment, CollapsedBlockMargins, CollapsedMargin, Fragment, FragmentFlags, }; use crate::geom::{AuOrAuto, LogicalRect, LogicalSides, LogicalVec2}; use crate::positioned::{AbsolutelyPositionedBox, PositioningContext, PositioningContextLength}; @@ -212,7 +212,9 @@ struct CollapsibleWithParentStartMargin(bool); #[derive(Debug, Serialize)] pub(crate) struct OutsideMarker { #[serde(skip_serializing)] - pub style: Arc<ComputedValues>, + pub marker_style: Arc<ComputedValues>, + #[serde(skip_serializing)] + pub list_item_style: Arc<ComputedValues>, pub block_container: BlockContainer, } @@ -228,15 +230,16 @@ impl OutsideMarker { let content_sizes = self .block_container .inline_content_sizes(layout_context, containing_block.style.writing_mode); - let containing_block = ContainingBlock { + let containing_block_for_children = ContainingBlock { inline_size: content_sizes.max_content, block_size: AuOrAuto::auto(), - style: &self.style, + style: &self.marker_style, }; + let flow_layout = self.block_container.layout( layout_context, positioning_context, - &containing_block, + &containing_block_for_children, sequential_layout_state, collapsible_with_parent_start_margin.unwrap_or(CollapsibleWithParentStartMargin(false)), ); @@ -257,20 +260,33 @@ impl OutsideMarker { }, ); + // Position the marker beyond the inline start of the border box list item. This needs to + // take into account the border and padding of the item. + // + // TODO: This is the wrong containing block, as it should be the containing block of the + // parent of this list item. What this means in practice is that the writing mode could be + // wrong and padding defined as a percentage will be resolved incorrectly. + let pbm_of_list_item = self.list_item_style.padding_border_margin(containing_block); let content_rect = LogicalRect { start_corner: LogicalVec2 { - inline: -max_inline_size, + inline: -max_inline_size - + (pbm_of_list_item.border.inline_start + + pbm_of_list_item.padding.inline_start) + .into(), block: Zero::zero(), }, size: LogicalVec2 { inline: max_inline_size, - block: Zero::zero(), + block: flow_layout.content_block_size, }, }; + let mut base_fragment_info = BaseFragmentInfo::anonymous(); + base_fragment_info.flags |= FragmentFlags::IS_OUTSIDE_LIST_ITEM_MARKER; + Fragment::Box(BoxFragment::new( - BaseFragmentInfo::anonymous(), - self.style.clone(), + base_fragment_info, + self.marker_style.clone(), flow_layout.fragments, content_rect, LogicalSides::zero(), @@ -1604,6 +1620,12 @@ struct PlacementState { current_block_direction_position: Au, inflow_baselines: Baselines, is_inline_block_context: bool, + + /// If this [`PlacementState`] is laying out a list item with an outside marker. Record the + /// block size of that marker, because the content block size of the list item needs to be at + /// least as tall as the marker size -- even though the marker doesn't advance the block + /// position of the placement. + marker_block_size: Option<Au>, } impl PlacementState { @@ -1622,6 +1644,7 @@ impl PlacementState { current_block_direction_position: Au::zero(), inflow_baselines: Baselines::default(), is_inline_block_context, + marker_block_size: None, } } @@ -1665,10 +1688,29 @@ impl PlacementState { ) { match fragment { Fragment::Box(fragment) => { + // If this child is a marker positioned outside of a list item, then record its + // size, but also ensure that it doesn't advance the block position of the placment. + // This ensures item content is placed next to the marker. + // + // This is a pretty big hack because it doesn't properly handle all interactions + // between the marker and the item. For instance the marker should be positioned at + // the baseline of list item content and the first line of the item content should + // be at least as tall as the marker -- not the entire list item itself. + let is_outside_marker = fragment + .base + .flags + .contains(FragmentFlags::IS_OUTSIDE_LIST_ITEM_MARKER); + if is_outside_marker { + assert!(self.marker_block_size.is_none()); + self.marker_block_size = Some(fragment.content_rect.size.block.into()); + return; + } + let fragment_block_margins = &fragment.block_margins_collapsed_with_children; let mut fragment_block_size = fragment.padding.block_sum() + fragment.border.block_sum() + fragment.content_rect.size.block.into(); + // We use `last_in_flow_margin_collapses_with_parent_end_margin` to implement // this quote from https://drafts.csswg.org/css2/#collapsing-margins // > If the top and bottom margins of an element with clearance are adjoining, @@ -1747,10 +1789,23 @@ impl PlacementState { self.current_block_direction_position += self.current_margin.solve(); self.current_margin = CollapsedMargin::zero(); } + let (total_block_size, collapsed_through) = match self.marker_block_size { + Some(marker_block_size) => ( + self.current_block_direction_position.max(marker_block_size), + // If this is a list item (even empty) with an outside marker, then it + // should not collapse through. + false, + ), + None => ( + self.current_block_direction_position, + self.next_in_flow_margin_collapses_with_parent_start_margin, + ), + }; + ( - self.current_block_direction_position.into(), + total_block_size.into(), CollapsedBlockMargins { - collapsed_through: self.next_in_flow_margin_collapses_with_parent_start_margin, + collapsed_through, start: self.start_margin, end: self.current_margin, }, |