Commit 3a6221f7 authored by Aleks Totic's avatar Aleks Totic Committed by Commit Bot

[LayoutNG] Position descendants in inline containing block.

Part 4 of 4 of inline containing block for OOF descendants
https://chromium-review.googlesource.com/c/chromium/src/+/777707

Geometry of computing the containing block coordinates is complex.

In the old code, all containing block geometry was stored as members of
NGOutOfFlowLayoutPart.

With inline containing blocks, each inline-cb can have a different geometry.
Code was refactored so that cb geometry is configurable per descendant.

Bug: 740993
Change-Id: I3dd392472081ac6b09dd6a2540d142ba9966f3a6
Reviewed-on: https://chromium-review.googlesource.com/818333
Commit-Queue: Aleks Totic <atotic@chromium.org>
Reviewed-by: default avatarKoji Ishii <kojii@chromium.org>
Cr-Commit-Position: refs/heads/master@{#523635}
parent 5165b8fb
...@@ -463,9 +463,12 @@ void NGBlockNode::CopyChildFragmentPosition( ...@@ -463,9 +463,12 @@ void NGBlockNode::CopyChildFragmentPosition(
// We should only be positioning children which are relative to ourselves. // We should only be positioning children which are relative to ourselves.
// The flow thread, however, is invisible to LayoutNG, so we need to make // The flow thread, however, is invisible to LayoutNG, so we need to make
// an exception there. // an exception there.
DCHECK(box_ == layout_box->ContainingBlock() || DCHECK(
box_ == layout_box->ContainingBlock() ||
(layout_box->ContainingBlock()->IsLayoutFlowThread() && (layout_box->ContainingBlock()->IsLayoutFlowThread() &&
box_ == layout_box->ContainingBlock()->ContainingBlock())); box_ == layout_box->ContainingBlock()->ContainingBlock()) ||
(layout_box->ContainingBlock()->IsInline() && // anonymous wrapper case
box_->Parent() == layout_box->ContainingBlock()));
// LegacyLayout flips vertical-rl horizontal coordinates before paint. // LegacyLayout flips vertical-rl horizontal coordinates before paint.
// NGLayout flips X location for LegacyLayout compatibility. // NGLayout flips X location for LegacyLayout compatibility.
......
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
#include "core/layout/ng/ng_out_of_flow_layout_part.h" #include "core/layout/ng/ng_out_of_flow_layout_part.h"
#include "core/layout/LayoutObject.h"
#include "core/layout/ng/inline/ng_physical_line_box_fragment.h"
#include "core/layout/ng/ng_absolute_utils.h" #include "core/layout/ng/ng_absolute_utils.h"
#include "core/layout/ng/ng_block_node.h" #include "core/layout/ng/ng_block_node.h"
#include "core/layout/ng/ng_box_fragment.h" #include "core/layout/ng/ng_box_fragment.h"
...@@ -51,11 +53,12 @@ void NGOutOfFlowLayoutPart::Run(bool update_legacy) { ...@@ -51,11 +53,12 @@ void NGOutOfFlowLayoutPart::Run(bool update_legacy) {
&descendant_candidates); &descendant_candidates);
while (descendant_candidates.size() > 0) { while (descendant_candidates.size() > 0) {
ComputeInlineContainingBlocks(descendant_candidates);
for (auto& candidate : descendant_candidates) { for (auto& candidate : descendant_candidates) {
if (IsContainingBlockForDescendant(candidate.node.Style())) { if (IsContainingBlockForDescendant(candidate)) {
NGLogicalOffset offset; NGLogicalOffset offset;
scoped_refptr<NGLayoutResult> result = LayoutDescendant( scoped_refptr<NGLayoutResult> result =
candidate.node, candidate.static_position, &offset); LayoutDescendant(candidate, &offset);
container_builder_->AddChild(std::move(result), offset); container_builder_->AddChild(std::move(result), offset);
if (update_legacy) if (update_legacy)
candidate.node.UseOldOutOfFlowPositioning(); candidate.node.UseOldOutOfFlowPositioning();
...@@ -71,50 +74,246 @@ void NGOutOfFlowLayoutPart::Run(bool update_legacy) { ...@@ -71,50 +74,246 @@ void NGOutOfFlowLayoutPart::Run(bool update_legacy) {
} }
} }
NGOutOfFlowLayoutPart::ContainingBlockInfo
NGOutOfFlowLayoutPart::GetContainingBlockInfo(
const NGOutOfFlowPositionedDescendant& descendant) const {
// TODO remove containing_blocks_map_.Contains
if (descendant.inline_container &&
containing_blocks_map_.Contains(descendant.inline_container)) {
DCHECK(containing_blocks_map_.Contains(descendant.inline_container));
return containing_blocks_map_.at(descendant.inline_container);
}
return ContainingBlockInfo{&container_style_, container_size_,
content_offset_, content_physical_offset_};
}
void NGOutOfFlowLayoutPart::ComputeInlineContainingBlocks(
Vector<NGOutOfFlowPositionedDescendant> descendants) {
HashMap<const LayoutObject*, NGFragmentBuilder::FragmentPair>
inline_container_fragments;
for (auto& descendant : descendants) {
if (descendant.inline_container &&
!inline_container_fragments.Contains(descendant.inline_container)) {
NGFragmentBuilder::FragmentPair fragment_pair = {};
inline_container_fragments.insert(descendant.inline_container,
fragment_pair);
}
}
// Fetch start/end fragment info.
NGLogicalSize container_builder_size;
container_builder_->ComputeInlineContainerFragments(
&inline_container_fragments, &container_builder_size);
NGPhysicalSize container_builder_physical_size =
container_builder_size.ConvertToPhysical(
container_style_.GetWritingMode());
// Translate start/end fragments into ContainingBlockInfo.
for (auto& block_info : inline_container_fragments) {
NGLogicalOffset inline_content_offset;
NGPhysicalOffset inline_content_physical_offset;
NGLogicalSize inline_cb_size;
const ComputedStyle* inline_cb_style;
inline_cb_style = &block_info.value.start_fragment->Style();
scoped_refptr<NGConstraintSpace> dummy_constraint_space =
NGConstraintSpaceBuilder(inline_cb_style->GetWritingMode(), icb_size_)
.ToConstraintSpace(inline_cb_style->GetWritingMode());
// 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.
//
// 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)
//
// 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.
//
// Then magic happens.^H^H^H^H^H^H^H
//
// 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;
WritingMode container_writing_mode = container_style_.GetWritingMode();
TextDirection container_direction = container_style_.Direction();
// Step 1
NGLogicalOffset start_fragment_logical_offset =
block_info.value.start_fragment_offset.ConvertToLogical(
container_writing_mode, container_direction,
start_linebox_fragment->Size(), start_fragment->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());
NGPhysicalOffset start_linebox_physical_offset =
block_info.value.start_linebox_offset.ConvertToPhysical(
container_writing_mode, container_direction,
container_builder_physical_size, start_linebox_fragment->Size());
start_fragment_physical_offset += start_linebox_physical_offset;
// 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_offset.ConvertToLogical(
container_writing_mode, container_direction,
end_linebox_fragment->Size(), end_fragment->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 +
end_fragment->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());
NGPhysicalOffset end_linebox_physical_offset =
block_info.value.end_linebox_offset.ConvertToPhysical(
container_writing_mode, container_direction,
container_builder_physical_size, end_linebox_fragment->Size());
end_fragment_physical_offset += end_linebox_physical_offset;
// 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();
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);
NGLogicalOffset default_container_offset;
if (RuntimeEnabledFeatures::LayoutNGPaintFragmentsEnabled()) {
// NGPaint offset is wrt parent fragment.
default_container_offset =
start_fragment_logical_offset - content_offset_;
default_container_offset += NGLogicalOffset(
inline_cb_borders.inline_start, inline_cb_borders.block_start);
} else {
// Legacy offset is wrt inline container.
default_container_offset =
NGLogicalOffset(inline_cb_borders.inline_start,
inline_cb_borders.block_start) -
content_offset_;
}
containing_blocks_map_.insert(
block_info.key, ContainingBlockInfo{inline_cb_style, inline_cb_size,
inline_content_offset,
inline_content_physical_offset,
default_container_offset});
}
}
scoped_refptr<NGLayoutResult> NGOutOfFlowLayoutPart::LayoutDescendant( scoped_refptr<NGLayoutResult> NGOutOfFlowLayoutPart::LayoutDescendant(
NGBlockNode descendant, const NGOutOfFlowPositionedDescendant& descendant,
NGStaticPosition static_position,
NGLogicalOffset* offset) { NGLogicalOffset* offset) {
DCHECK(descendant); ContainingBlockInfo container_info = GetContainingBlockInfo(descendant);
WritingMode container_writing_mode(container_style_.GetWritingMode()); WritingMode container_writing_mode(container_info.style->GetWritingMode());
WritingMode descendant_writing_mode(descendant.Style().GetWritingMode()); WritingMode descendant_writing_mode(descendant.node.Style().GetWritingMode());
// Adjust the static_position origin. The static_position coordinate origin is // Adjust the static_position origin. The static_position coordinate origin is
// relative to the container's border box, ng_absolute_utils expects it to be // relative to the container's border box, ng_absolute_utils expects it to be
// relative to the container's padding box. // relative to the container's padding box.
static_position.offset -= content_physical_offset_; NGStaticPosition static_position(descendant.static_position);
static_position.offset -= container_info.content_physical_offset;
// The block estimate is in the descendant's writing mode. // The block estimate is in the descendant's writing mode.
scoped_refptr<NGConstraintSpace> descendant_constraint_space = scoped_refptr<NGConstraintSpace> descendant_constraint_space =
NGConstraintSpaceBuilder(container_writing_mode, icb_size_) NGConstraintSpaceBuilder(container_writing_mode, icb_size_)
.SetTextDirection(container_style_.Direction()) .SetTextDirection(container_info.style->Direction())
.SetAvailableSize(container_size_) .SetAvailableSize(container_info.content_size)
.SetPercentageResolutionSize(container_size_) .SetPercentageResolutionSize(container_info.content_size)
.ToConstraintSpace(descendant_writing_mode); .ToConstraintSpace(descendant_writing_mode);
Optional<MinMaxSize> min_max_size; Optional<MinMaxSize> min_max_size;
Optional<LayoutUnit> block_estimate; Optional<LayoutUnit> block_estimate;
scoped_refptr<NGLayoutResult> layout_result = nullptr; scoped_refptr<NGLayoutResult> layout_result = nullptr;
if (AbsoluteNeedsChildInlineSize(descendant.Style()) || NGBlockNode node = descendant.node;
NeedMinMaxSize(descendant.Style())) { if (AbsoluteNeedsChildInlineSize(descendant.node.Style()) ||
min_max_size = descendant.ComputeMinMaxSize(); NeedMinMaxSize(descendant.node.Style())) {
min_max_size = node.ComputeMinMaxSize();
} }
Optional<NGLogicalSize> replaced_size; Optional<NGLogicalSize> replaced_size;
if (descendant.IsReplaced()) if (descendant.node.IsReplaced())
replaced_size = ComputeReplacedSize( replaced_size = ComputeReplacedSize(
descendant, *descendant_constraint_space, min_max_size); descendant.node, *descendant_constraint_space, min_max_size);
NGAbsolutePhysicalPosition node_position = NGAbsolutePhysicalPosition node_position =
ComputePartialAbsoluteWithChildInlineSize( ComputePartialAbsoluteWithChildInlineSize(
*descendant_constraint_space, descendant.Style(), static_position, *descendant_constraint_space, descendant.node.Style(),
min_max_size, replaced_size, container_writing_mode, static_position, min_max_size, replaced_size, container_writing_mode,
container_style_.Direction()); container_info.style->Direction());
if (AbsoluteNeedsChildBlockSize(descendant.Style())) { if (AbsoluteNeedsChildBlockSize(descendant.node.Style())) {
layout_result = GenerateFragment(descendant, block_estimate, node_position); layout_result = GenerateFragment(descendant.node, container_info,
block_estimate, node_position);
DCHECK(layout_result->PhysicalFragment().get()); DCHECK(layout_result->PhysicalFragment().get());
NGFragment fragment(descendant_writing_mode, NGFragment fragment(descendant_writing_mode,
...@@ -124,30 +323,38 @@ scoped_refptr<NGLayoutResult> NGOutOfFlowLayoutPart::LayoutDescendant( ...@@ -124,30 +323,38 @@ scoped_refptr<NGLayoutResult> NGOutOfFlowLayoutPart::LayoutDescendant(
} }
ComputeFullAbsoluteWithChildBlockSize( ComputeFullAbsoluteWithChildBlockSize(
*descendant_constraint_space, descendant.Style(), static_position, *descendant_constraint_space, descendant.node.Style(), static_position,
block_estimate, replaced_size, container_writing_mode, block_estimate, replaced_size, container_writing_mode,
container_style_.Direction(), &node_position); container_info.style->Direction(), &node_position);
// Skip this step if we produced a fragment when estimating the block size. // Skip this step if we produced a fragment when estimating the block size.
if (!layout_result) { if (!layout_result) {
block_estimate = block_estimate =
node_position.size.ConvertToLogical(descendant_writing_mode).block_size; node_position.size.ConvertToLogical(descendant_writing_mode).block_size;
layout_result = GenerateFragment(descendant, block_estimate, node_position); layout_result = GenerateFragment(descendant.node, container_info,
block_estimate, node_position);
} }
// Compute logical offset, NGAbsolutePhysicalPosition is calculated relative // Compute logical offset, NGAbsolutePhysicalPosition is calculated relative
// to the padding box so add back the container's borders. // to the padding box so add back the container's borders.
NGBoxStrut inset = node_position.inset.ConvertToLogical( NGBoxStrut inset = node_position.inset.ConvertToLogical(
container_writing_mode, container_style_.Direction()); container_writing_mode, container_info.style->Direction());
offset->inline_offset = inset.inline_start + content_offset_.inline_offset; offset->inline_offset = inset.inline_start + content_offset_.inline_offset;
offset->block_offset = inset.block_start + content_offset_.block_offset; offset->block_offset = inset.block_start + content_offset_.block_offset;
offset->inline_offset +=
container_info.default_container_offset.inline_offset;
offset->block_offset += container_info.default_container_offset.block_offset;
return layout_result; return layout_result;
} }
bool NGOutOfFlowLayoutPart::IsContainingBlockForDescendant( bool NGOutOfFlowLayoutPart::IsContainingBlockForDescendant(
const ComputedStyle& descendant_style) { const NGOutOfFlowPositionedDescendant& descendant) {
EPosition position = descendant_style.GetPosition(); EPosition position = descendant.node.Style().GetPosition();
if (descendant.inline_container) {
DCHECK(position == EPosition::kAbsolute);
return true;
}
return (contains_absolute_ && position == EPosition::kAbsolute) || return (contains_absolute_ && position == EPosition::kAbsolute) ||
(contains_fixed_ && position == EPosition::kFixed); (contains_fixed_ && position == EPosition::kFixed);
} }
...@@ -159,13 +366,15 @@ bool NGOutOfFlowLayoutPart::IsContainingBlockForDescendant( ...@@ -159,13 +366,15 @@ bool NGOutOfFlowLayoutPart::IsContainingBlockForDescendant(
// position calculation. // position calculation.
scoped_refptr<NGLayoutResult> NGOutOfFlowLayoutPart::GenerateFragment( scoped_refptr<NGLayoutResult> NGOutOfFlowLayoutPart::GenerateFragment(
NGBlockNode descendant, NGBlockNode descendant,
const ContainingBlockInfo& container_info,
const Optional<LayoutUnit>& block_estimate, const Optional<LayoutUnit>& block_estimate,
const NGAbsolutePhysicalPosition node_position) { const NGAbsolutePhysicalPosition node_position) {
// As the block_estimate is always in the descendant's writing mode, we build // As the block_estimate is always in the descendant's writing mode, we build
// the constraint space in the descendant's writing mode. // the constraint space in the descendant's writing mode.
WritingMode writing_mode(descendant.Style().GetWritingMode()); WritingMode writing_mode(descendant.Style().GetWritingMode());
NGLogicalSize container_size( NGLogicalSize container_size(
container_size_.ConvertToPhysical(container_style_.GetWritingMode()) container_info.content_size
.ConvertToPhysical(container_style_.GetWritingMode())
.ConvertToLogical(writing_mode)); .ConvertToLogical(writing_mode));
LayoutUnit inline_size = LayoutUnit inline_size =
......
...@@ -37,15 +37,45 @@ class CORE_EXPORT NGOutOfFlowLayoutPart { ...@@ -37,15 +37,45 @@ class CORE_EXPORT NGOutOfFlowLayoutPart {
void Run(bool update_legacy = true); void Run(bool update_legacy = true);
private: private:
// Information needed to position descendant within a containing block.
// Geometry expressed here is complicated:
// There are two types of containing blocks:
// 1) Default containing block (DCB)
// Containing block passed in NGOutOfFlowLayoutPart constructor.
// It is the block element inside which this algorighm runs.
// All OOF descendants not in inline containing block are placed in DCB.
// 2) Inline containing block
// OOF descendants might be positioned wrt inline containing block.
// Inline containing block is positioned wrt default containing block.
struct ContainingBlockInfo {
STACK_ALLOCATED();
// Containing block style.
const ComputedStyle* style;
// Logical in containing block coordinates.
NGLogicalSize content_size;
// Content offset wrt border box.
NGLogicalOffset content_offset;
// Physical content offset wrt border box.
NGPhysicalOffset content_physical_offset;
// Logical offset wrt default containing block.
NGLogicalOffset default_container_offset;
};
ContainingBlockInfo GetContainingBlockInfo(
const NGOutOfFlowPositionedDescendant&) const;
void ComputeInlineContainingBlocks(Vector<NGOutOfFlowPositionedDescendant>);
scoped_refptr<NGLayoutResult> LayoutDescendant( scoped_refptr<NGLayoutResult> LayoutDescendant(
NGBlockNode descendant, const NGOutOfFlowPositionedDescendant&,
NGStaticPosition static_position,
NGLogicalOffset* offset); NGLogicalOffset* offset);
bool IsContainingBlockForDescendant(const ComputedStyle& descendant_style); bool IsContainingBlockForDescendant(
const NGOutOfFlowPositionedDescendant& descendant);
scoped_refptr<NGLayoutResult> GenerateFragment( scoped_refptr<NGLayoutResult> GenerateFragment(
NGBlockNode node, NGBlockNode node,
const ContainingBlockInfo&,
const Optional<LayoutUnit>& block_estimate, const Optional<LayoutUnit>& block_estimate,
const NGAbsolutePhysicalPosition node_position); const NGAbsolutePhysicalPosition node_position);
...@@ -54,10 +84,12 @@ class CORE_EXPORT NGOutOfFlowLayoutPart { ...@@ -54,10 +84,12 @@ class CORE_EXPORT NGOutOfFlowLayoutPart {
bool contains_absolute_; bool contains_absolute_;
bool contains_fixed_; bool contains_fixed_;
ContainingBlockInfo default_containing_block_;
NGLogicalOffset content_offset_; NGLogicalOffset content_offset_;
NGPhysicalOffset content_physical_offset_; NGPhysicalOffset content_physical_offset_;
NGLogicalSize container_size_; NGLogicalSize container_size_;
NGPhysicalSize icb_size_; NGPhysicalSize icb_size_;
HashMap<const LayoutObject*, ContainingBlockInfo> containing_blocks_map_;
}; };
} // namespace blink } // namespace blink
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment