diff options
Diffstat (limited to 'chromium/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc')
-rw-r--r-- | chromium/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc | 404 |
1 files changed, 227 insertions, 177 deletions
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc index 6a8ee5eaedd..be334862956 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc @@ -25,7 +25,7 @@ NGOutOfFlowLayoutPart::NGOutOfFlowLayoutPart( NGBoxFragmentBuilder* container_builder, bool contains_absolute, bool contains_fixed, - const NGBoxStrut& borders_and_scrollers, + const NGBoxStrut& border_scrollbar, const NGConstraintSpace& container_space, const ComputedStyle& container_style, base::Optional<NGLogicalSize> initial_containing_block_fixed_size) @@ -34,34 +34,77 @@ NGOutOfFlowLayoutPart::NGOutOfFlowLayoutPart( contains_fixed_(contains_fixed) { if (!container_builder->HasOutOfFlowDescendantCandidates()) return; - NGPhysicalBoxStrut physical_borders = borders_and_scrollers.ConvertToPhysical( - container_style.GetWritingMode(), container_style.Direction()); + + NGPhysicalBoxStrut physical_border_scrollbar = + border_scrollbar.ConvertToPhysical(container_style.GetWritingMode(), + container_style.Direction()); default_containing_block_.style = &container_style; default_containing_block_.content_size_for_absolute = container_builder_->Size(); default_containing_block_.content_size_for_absolute.inline_size = std::max(default_containing_block_.content_size_for_absolute.inline_size - - borders_and_scrollers.InlineSum(), + border_scrollbar.InlineSum(), LayoutUnit()); default_containing_block_.content_size_for_absolute.block_size = std::max(default_containing_block_.content_size_for_absolute.block_size - - borders_and_scrollers.BlockSum(), + border_scrollbar.BlockSum(), LayoutUnit()); default_containing_block_.content_size_for_fixed = initial_containing_block_fixed_size ? initial_containing_block_fixed_size.value() : default_containing_block_.content_size_for_absolute; - default_containing_block_.content_offset = NGLogicalOffset{ - borders_and_scrollers.inline_start, borders_and_scrollers.block_start}; - default_containing_block_.content_physical_offset = - NGPhysicalOffset(physical_borders.left, physical_borders.top); + + default_containing_block_.container_offset = NGLogicalOffset( + border_scrollbar.inline_start, border_scrollbar.block_start); + default_containing_block_.physical_container_offset = NGPhysicalOffset( + physical_border_scrollbar.left, physical_border_scrollbar.top); } void NGOutOfFlowLayoutPart::Run(LayoutBox* only_layout) { Vector<NGOutOfFlowPositionedDescendant> descendant_candidates; + const LayoutObject* current_container = container_builder_->GetLayoutObject(); + container_builder_->GetAndClearOutOfFlowDescendantCandidates( - &descendant_candidates, container_builder_->GetLayoutObject()); + &descendant_candidates, current_container); + + // Special case: containing block is a split inline. + // Detailed explanation of what this means is inside + // NGOutOfFlowLayoutPart::LayoutDescendant. + // This code can be removed once we stop inline splitting. + // only_layout is only true when positioning blocks for Legacy. + // Cross-anonymous inline containers cannot be done in Legacy. + if (descendant_candidates.size() > 0 && current_container && !only_layout && + current_container->IsAnonymousBlock() && + current_container->IsRelPositioned()) { + // Comments and code copied from + // LayoutBox::ContainingBlockLogicalWidthForPositioned. + // Ensure we compute our width based on the width of our rel-pos inline + // container rather than any anonymous block created to manage a block-flow + // ancestor of ours in the rel-pos inline's inline flow. + LayoutBoxModelObject* absolute_containing_block = + ToLayoutBox(current_container)->Continuation(); + // There may be nested parallel inline continuations. We have now found the + // innermost inline (which may not be relatively positioned). Locate the + // inline that serves as the containing block of this box. + while (!absolute_containing_block->CanContainOutOfFlowPositionedElement( + EPosition::kAbsolute)) { + absolute_containing_block = + ToLayoutBoxModelObject(absolute_containing_block->Container()); + } + DCHECK(absolute_containing_block->IsLayoutInline()); + // Make absolute_containing_block continuation root. + absolute_containing_block = ToLayoutBoxModelObject( + absolute_containing_block->GetNode()->GetLayoutObject()); + for (auto& candidate : descendant_candidates) { + if (absolute_containing_block->CanContainOutOfFlowPositionedElement( + candidate.node.Style().GetPosition())) { + candidate.inline_container = absolute_containing_block; + container_builder_->AddOutOfFlowDescendant(candidate); + } + } + return; + } while (descendant_candidates.size() > 0) { ComputeInlineContainingBlocks(descendant_candidates); @@ -70,7 +113,7 @@ void NGOutOfFlowLayoutPart::Run(LayoutBox* only_layout) { (!only_layout || candidate.node.GetLayoutBox() == only_layout)) { NGLogicalOffset offset; scoped_refptr<NGLayoutResult> result = - LayoutDescendant(candidate, &offset); + LayoutDescendant(candidate, &offset, only_layout); container_builder_->AddChild(*result, offset); if (candidate.node.GetLayoutBox() != only_layout) candidate.node.UseOldOutOfFlowPositioning(); @@ -82,7 +125,7 @@ void NGOutOfFlowLayoutPart::Run(LayoutBox* only_layout) { // This happens when an absolute container has a fixed child. descendant_candidates.Shrink(0); container_builder_->GetAndClearOutOfFlowDescendantCandidates( - &descendant_candidates, container_builder_->GetLayoutObject()); + &descendant_candidates, current_container); } } @@ -100,223 +143,185 @@ NGOutOfFlowLayoutPart::GetContainingBlockInfo( void NGOutOfFlowLayoutPart::ComputeInlineContainingBlocks( Vector<NGOutOfFlowPositionedDescendant> descendants) { - HashMap<const LayoutObject*, NGBoxFragmentBuilder::FragmentPair> - inline_container_fragments; + NGBoxFragmentBuilder::InlineContainingBlockMap inline_container_fragments; for (auto& descendant : descendants) { if (descendant.inline_container && !inline_container_fragments.Contains(descendant.inline_container)) { - NGBoxFragmentBuilder::FragmentPair fragment_pair = {}; + NGBoxFragmentBuilder::InlineContainingBlockGeometry inline_geometry = {}; inline_container_fragments.insert(descendant.inline_container, - fragment_pair); + inline_geometry); } } // Fetch start/end fragment info. - NGLogicalSize container_builder_size; container_builder_->ComputeInlineContainerFragments( - &inline_container_fragments, &container_builder_size); + &inline_container_fragments); + NGLogicalSize container_builder_size = container_builder_->Size(); NGPhysicalSize container_builder_physical_size = ToNGPhysicalSize(container_builder_size, default_containing_block_.style->GetWritingMode()); // Translate start/end fragments into ContainingBlockInfo. for (auto& block_info : inline_container_fragments) { // Variables needed to describe ContainingBlockInfo - const ComputedStyle* inline_cb_style; + const ComputedStyle* inline_cb_style = block_info.key->Style(); NGLogicalSize inline_cb_size; - NGLogicalOffset inline_content_offset; - NGPhysicalOffset inline_content_physical_offset; - NGLogicalOffset default_container_offset; - if (!block_info.value.start_fragment) { + NGLogicalOffset container_offset; + NGPhysicalOffset physical_container_offset; + + if (!block_info.value.has_value()) { // This happens when Legacy block is the default container. // In this case, container builder does not have any fragments because // ng layout algorithm did not run. DCHECK(block_info.key->IsLayoutInline()); - inline_cb_style = block_info.key->Style(); NOTIMPLEMENTED() << "Inline containing block might need geometry information"; // TODO(atotic) ContainingBlockInfo geometry // must be computed from Legacy algorithm } else { - inline_cb_style = &block_info.value.start_fragment->Style(); + DCHECK(inline_cb_style); + + // TODO Creating dummy constraint space just to get borders feels wrong. NGConstraintSpace dummy_constraint_space = NGConstraintSpaceBuilder(inline_cb_style->GetWritingMode(), inline_cb_style->GetWritingMode(), /* is_new_fc */ false) .ToConstraintSpace(); - - // TODO Creating dummy constraint space just to get borders feels wrong. NGBoxStrut inline_cb_borders = ComputeBorders(dummy_constraint_space, *inline_cb_style); - NGPhysicalBoxStrut physical_borders = inline_cb_borders.ConvertToPhysical( - inline_cb_style->GetWritingMode(), inline_cb_style->Direction()); - NGBoxStrut inline_cb_padding = - ComputePadding(dummy_constraint_space, *inline_cb_style); - // Warning: lots of non-obvious coordinate manipulation ahead. + // The calculation below determines the size of the inline containing + // block rect. + // + // To perform this calculation we: + // 1. Determine the start_offset "^", this is at the logical-start (wrt. + // default containing block), of the start fragment rect. + // 2. Determine the end_offset "$", this is at the logical-end (wrt. + // default containing block), of the end fragment rect. + // 3. Determine the logical rectangle defined by these two offsets. // - // High level goal is: - // - Find logical topleft of start fragment, and logical bottomright - // of end fragment. - // - Use these to compute inline-cb geometry: - // - inline-cb size (content_size) - // - inline-cb offset from containing block (default_container_offset) + // Case 1a: Same direction, overlapping fragments. + // +--------------- + // ---> |^*****--------> + // +*----*--------- + // * * + // ------*----*+ + // ----> *****$| ---> + // ------------+ // - // We start with: - // start_fragment, which has physical offset from start_linebox_fragment, - // end_fragment, also with physical offset. - // start_linebox_fragment, which has logical offset from containing box. - // end_linebox_fragment, which also has logical offset from containing - // box. + // Case 1b: Different direction, overlapping fragments. + // +--------------- + // ---> ^******* <-----| + // *------*-------- + // * * + // -----*------* + // |<-- *******$ ---> + // ------------+ // - // Then magic happens.^H^H^H^H^H^H^H + // Case 2a: Same direction, non-overlapping fragments. + // +-------- + // ---------> |^ -----> + // +*------- + // * + // --------+ * + // ------->| $ ---> + // --------+ // - // Then we do the following: - // 1. Find start fragment physical topleft wrt containing box. - // - convert start_fragment offset to logical. - // - convert start fragment inline/block start to physical. - // - convert linebox topleft to physical. - // - add start fragment to linebox topleft - // 2. Find end fragment bottom right wrt containing box - // - convert end fragment offset to logical. - // - convert end fragment inline/block end to physical - // - convert linebox topleft to physical - // - add end fragment bottomLeft to linebox topleft - // 3. Convert both topleft/bottomright to logical, so that we can - // 4. Enforce logical topLeft < bottomRight - // 5. Compute size, physical offset - const NGPhysicalFragment* start_fragment = - block_info.value.start_fragment; - const NGPhysicalLineBoxFragment* start_linebox_fragment = - block_info.value.start_linebox_fragment; + // Case 2b: Same direction, non-overlapping fragments. + // +-------- + // ---------> ^ <-----| + // *-------- + // * + // --------+ * + // | <------ $ ---> + // --------+ + // + // Note in cases [1a, 2a] we need to account for the inline borders of + // the rectangles, where-as in [1b, 2b] we do not. This is handled by the + // is_same_direction check(s). + // + // Note in cases [2a, 2b] we don't allow a "negative" containing block + // size, we clamp negative sizes to zero. WritingMode container_writing_mode = default_containing_block_.style->GetWritingMode(); TextDirection container_direction = default_containing_block_.style->Direction(); - // Step 1 - NGLogicalOffset start_fragment_logical_offset = - block_info.value.start_fragment_union_rect.offset.ConvertToLogical( - container_writing_mode, container_direction, - start_linebox_fragment->Size(), - block_info.value.start_fragment_union_rect.size); - // Text fragments do not include inline-cb borders and padding. - if (start_fragment->IsText()) { - start_fragment_logical_offset -= inline_cb_borders.StartOffset(); - start_fragment_logical_offset -= inline_cb_padding.StartOffset(); - } - NGPhysicalOffset start_fragment_physical_offset = - start_fragment_logical_offset.ConvertToPhysical( - container_writing_mode, container_direction, - start_linebox_fragment->Size(), NGPhysicalSize()); - // Step 2 - const NGPhysicalLineBoxFragment* end_linebox_fragment = - block_info.value.end_linebox_fragment; - const NGPhysicalFragment* end_fragment = block_info.value.end_fragment; - NGLogicalOffset end_fragment_logical_offset = - block_info.value.end_fragment_union_rect.offset.ConvertToLogical( - container_writing_mode, container_direction, - end_linebox_fragment->Size(), - block_info.value.end_fragment_union_rect.size); - // Text fragments do not include inline-cb borders and padding. - if (end_fragment->IsText()) { - end_fragment_logical_offset += NGLogicalOffset( - inline_cb_borders.inline_end, inline_cb_borders.block_end); - end_fragment_logical_offset += NGLogicalOffset( - inline_cb_padding.inline_end, inline_cb_padding.block_end); - } - NGLogicalOffset end_fragment_bottom_right = - end_fragment_logical_offset + - block_info.value.end_fragment_union_rect.size.ConvertToLogical( - container_writing_mode); - NGPhysicalOffset end_fragment_physical_offset = - end_fragment_bottom_right.ConvertToPhysical( - container_writing_mode, container_direction, - end_linebox_fragment->Size(), NGPhysicalSize()); - // Step 3 - NGLogicalOffset start_fragment_logical_offset_wrt_box = - start_fragment_physical_offset.ConvertToLogical( - inline_cb_style->GetWritingMode(), inline_cb_style->Direction(), - container_builder_physical_size, NGPhysicalSize()); - NGLogicalOffset end_fragment_logical_offset_wrt_box = - end_fragment_physical_offset.ConvertToLogical( - inline_cb_style->GetWritingMode(), inline_cb_style->Direction(), - container_builder_physical_size, NGPhysicalSize()); - - // Step 4 - end_fragment_logical_offset_wrt_box.inline_offset = - std::max(end_fragment_logical_offset_wrt_box.inline_offset, - start_fragment_logical_offset_wrt_box.inline_offset + - inline_cb_borders.InlineSum()); - end_fragment_logical_offset_wrt_box.block_offset = - std::max(end_fragment_logical_offset_wrt_box.block_offset, - start_fragment_logical_offset_wrt_box.block_offset + - inline_cb_borders.BlockSum()); - - // Step 5 - inline_cb_size.inline_size = - end_fragment_logical_offset_wrt_box.inline_offset - - start_fragment_logical_offset_wrt_box.inline_offset - - inline_cb_borders.InlineSum(); - inline_cb_size.block_size = - end_fragment_logical_offset_wrt_box.block_offset - - start_fragment_logical_offset_wrt_box.block_offset - - inline_cb_borders.BlockSum(); + bool is_same_direction = + container_direction == inline_cb_style->Direction(); + + // Step 1 - determine the start_offset. + const NGPhysicalOffsetRect& start_rect = + block_info.value.value().start_fragment_union_rect; + NGLogicalOffset start_offset = start_rect.offset.ConvertToLogical( + container_writing_mode, container_direction, + container_builder_physical_size, start_rect.size); + + // Make sure we add the inline borders, we don't need to do this in the + // inline direction if the blocks are in opposite directions. + start_offset.block_offset += inline_cb_borders.block_start; + if (is_same_direction) + start_offset.inline_offset += inline_cb_borders.inline_start; + + // Step 2 - determine the end_offset. + const NGPhysicalOffsetRect& end_rect = + block_info.value.value().end_fragment_union_rect; + NGLogicalOffset end_offset = end_rect.offset.ConvertToLogical( + container_writing_mode, container_direction, + container_builder_physical_size, end_rect.size); + + // Add in the size of the fragment to get the logical end of the fragment. + end_offset += end_rect.size.ConvertToLogical(container_writing_mode); + + // Make sure we substract the inline borders, we don't need to do this in + // the inline direction if the blocks are in opposite directions. + end_offset.block_offset -= inline_cb_borders.block_end; + if (is_same_direction) + end_offset.inline_offset -= inline_cb_borders.inline_end; + + // Make sure we don't end up with a rectangle with "negative" size. + end_offset.inline_offset = + std::max(end_offset.inline_offset, start_offset.inline_offset); + + // Step 3 - determine the logical rectange. + + // Determine the logical size of the containing block. + inline_cb_size = {end_offset.inline_offset - start_offset.inline_offset, + end_offset.block_offset - start_offset.block_offset}; DCHECK_GE(inline_cb_size.inline_size, LayoutUnit()); DCHECK_GE(inline_cb_size.block_size, LayoutUnit()); - inline_content_offset = NGLogicalOffset{inline_cb_borders.inline_start, - inline_cb_borders.block_start}; - inline_content_physical_offset = - NGPhysicalOffset(physical_borders.left, physical_borders.top); - - // NGPaint offset is wrt parent fragment. - default_container_offset = start_fragment_logical_offset_wrt_box - - default_containing_block_.content_offset; - default_container_offset += inline_cb_borders.StartOffset(); + // Determine the container offsets. + container_offset = start_offset; + physical_container_offset = container_offset.ConvertToPhysical( + container_writing_mode, container_direction, + container_builder_physical_size, + ToNGPhysicalSize(inline_cb_size, container_writing_mode)); } containing_blocks_map_.insert( block_info.key, ContainingBlockInfo{inline_cb_style, inline_cb_size, inline_cb_size, - inline_content_offset, - inline_content_physical_offset, - default_container_offset}); + container_offset, physical_container_offset}); } } scoped_refptr<NGLayoutResult> NGOutOfFlowLayoutPart::LayoutDescendant( const NGOutOfFlowPositionedDescendant& descendant, - NGLogicalOffset* offset) { + NGLogicalOffset* offset, + LayoutBox* only_layout) { ContainingBlockInfo container_info = GetContainingBlockInfo(descendant); WritingMode container_writing_mode(container_info.style->GetWritingMode()); WritingMode descendant_writing_mode(descendant.node.Style().GetWritingMode()); - // Adjust the static_position origin. - // The static_position coordinate origin is relative to default_container's - // border box. - // ng_absolute_utils expects static position to be relative to - // the container's padding box. - // Adjust static position by offset of container from default container, - // and default_container border width. + // Adjust the static_position (which is currently relative to the default + // container's border-box). ng_absolute_utils expects the static position to + // be relative to the container's padding-box. NGStaticPosition static_position(descendant.static_position); - NGPhysicalSize default_containing_block_physical_size = - ToNGPhysicalSize(default_containing_block_.ContentSize( - descendant.node.Style().GetPosition()), - default_containing_block_.style->GetWritingMode()); - NGPhysicalOffset default_container_physical_offset = - container_info.default_container_offset.ConvertToPhysical( - default_containing_block_.style->GetWritingMode(), - default_containing_block_.style->Direction(), - default_containing_block_physical_size, - default_containing_block_physical_size); - - static_position.offset = static_position.offset - - default_containing_block_.content_physical_offset - - default_container_physical_offset; + static_position.offset -= container_info.physical_container_offset; NGLogicalSize container_content_size = container_info.ContentSize(descendant.node.Style().GetPosition()); - // The block estimate is in the descendant's writing mode. + NGConstraintSpace descendant_constraint_space = NGConstraintSpaceBuilder(container_writing_mode, descendant_writing_mode, /* is_new_fc */ true) @@ -324,8 +329,10 @@ scoped_refptr<NGLayoutResult> NGOutOfFlowLayoutPart::LayoutDescendant( .SetAvailableSize(container_content_size) .SetPercentageResolutionSize(container_content_size) .ToConstraintSpace(); - base::Optional<MinMaxSize> min_max_size; + + // The block_estimate is in the descendant's writing mode. base::Optional<LayoutUnit> block_estimate; + base::Optional<MinMaxSize> min_max_size; scoped_refptr<NGLayoutResult> layout_result = nullptr; @@ -389,19 +396,16 @@ scoped_refptr<NGLayoutResult> NGOutOfFlowLayoutPart::LayoutDescendant( ToLayoutBlock(node.GetLayoutBox()) ->SetIsLegacyInitiatedOutOfFlowLayout(false); } - // Compute logical offset, NGAbsolutePhysicalPosition is calculated relative - // to the padding box so add back the container's borders. + NGBoxStrut inset = node_position.inset.ConvertToLogical( - container_writing_mode, container_info.style->Direction()); + container_writing_mode, default_containing_block_.style->Direction()); + + // inset is relative to the container's padding-box. Convert this to being + // relative to the default container's border-box. offset->inline_offset = - inset.inline_start + - default_containing_block_.content_offset.inline_offset; + inset.inline_start + container_info.container_offset.inline_offset; offset->block_offset = - inset.block_start + default_containing_block_.content_offset.block_offset; - - offset->inline_offset += - container_info.default_container_offset.inline_offset; - offset->block_offset += container_info.default_container_offset.block_offset; + inset.block_start + container_info.container_offset.block_offset; base::Optional<LayoutUnit> y = ComputeAbsoluteDialogYPosition( *descendant.node.GetLayoutBox(), @@ -413,6 +417,47 @@ scoped_refptr<NGLayoutResult> NGOutOfFlowLayoutPart::LayoutDescendant( offset->inline_offset = y.value(); } + // Special case: oof css container is a split inline. + // When css container spans multiple anonymous blocks, its dimensions + // can only be computed by a block that is an ancestor of all fragments + // generated by css container. That block is parent of anonymous containing + // block. + // That is why instead of OOF being placed by its anononymous container, + // they get placed by anonymous container's parent. + // This is different from all other OOF blocks, and requires special + // handling in several places in the OOF code. + // There is an exception to special case: if anonymous block is Legacy, + // we cannot do the fancy multiple anonymous block traversal, and we handle + // it like regular blocks. + // + // Detailed example: + // + // If Layout tree looks like this: + // LayoutNGBlockFlow#container + // LayoutNGBlockFlow (anonymous#1) + // LayoutInline#1 (relative) + // LayoutNGBlockFlow (anonymous#2 relative) + // LayoutNGBlockFlow#oof (positioned) + // LayoutNGBlockFlow (anonymous#3) + // LayoutInline#3 (continuation) + // + // The containing block geometry is defined by split inlines, + // LayoutInline#1, LayoutInline#3. + // Css container anonymous#2 does not have information needed + // to compute containing block geometry. + // Therefore, #oof cannot be placed by anonymous#2. NG handles this case + // by placing #oof in parent of anonymous (#container). + // + // But, PaintPropertyTreeBuilder expects #oof.Location() to be wrt + // css container, #anonymous2. This is why the code below adjusts + // the legacy offset from being wrt #container to being wrt #anonymous2. + + const LayoutObject* container = descendant.node.GetLayoutBox()->Container(); + if (!only_layout && container->IsAnonymousBlock()) { + NGLogicalOffset container_offset = + container_builder_->GetChildOffset(container); + *offset -= container_offset; + } return layout_result; } @@ -423,6 +468,11 @@ bool NGOutOfFlowLayoutPart::IsContainingBlockForDescendant( // Descendants whose containing block is inline are always positioned // inside closest parent block flow. if (descendant.inline_container) { + DCHECK( + descendant.node.Style().GetPosition() == EPosition::kAbsolute && + descendant.inline_container->CanContainAbsolutePositionObjects() || + (descendant.node.Style().GetPosition() == EPosition::kFixed && + descendant.inline_container->CanContainFixedPositionObjects())); return true; } return (contains_absolute_ && position == EPosition::kAbsolute) || |