blob: f8f00e4729d0aef3c9ed57070f2d4dfdafa81d94 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.h"
#include "third_party/blink/renderer/core/layout/layout_analyzer.h"
#include "third_party/blink/renderer/core/layout/min_max_size.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_fragment_traversal.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_data.h"
#include "third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h"
#include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h"
#include "third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h"
#include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h"
#include "third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h"
#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
#include "third_party/blink/renderer/core/paint/paint_layer.h"
namespace blink {
LayoutNGBlockFlow::LayoutNGBlockFlow(Element* element)
: LayoutNGMixin<LayoutBlockFlow>(element) {}
LayoutNGBlockFlow::~LayoutNGBlockFlow() = default;
bool LayoutNGBlockFlow::IsOfType(LayoutObjectType type) const {
return type == kLayoutObjectNGBlockFlow ||
LayoutNGMixin<LayoutBlockFlow>::IsOfType(type);
}
void LayoutNGBlockFlow::ComputeIntrinsicLogicalWidths(
LayoutUnit& min_logical_width,
LayoutUnit& max_logical_width) const {
NGBlockNode node(const_cast<LayoutNGBlockFlow*>(this));
if (!node.CanUseNewLayout()) {
LayoutBlockFlow::ComputeIntrinsicLogicalWidths(min_logical_width,
max_logical_width);
return;
}
MinMaxSizeInput input;
// This function returns content-box plus scrollbar.
input.size_type = NGMinMaxSizeType::kContentBoxSize;
MinMaxSize sizes = node.ComputeMinMaxSize(StyleRef().GetWritingMode(), input);
sizes += LayoutUnit(ScrollbarLogicalWidth());
min_logical_width = sizes.min_size;
max_logical_width = sizes.max_size;
}
void LayoutNGBlockFlow::UpdateBlockLayout(bool relayout_children) {
LayoutAnalyzer::BlockScope analyzer(*this);
// This block is an entry-point from the legacy engine to LayoutNG. This means
// that we need to be at a formatting context boundary, since NG and legacy
// don't cooperate on e.g. margin collapsing.
DCHECK(CreatesNewFormattingContext());
if (IsOutOfFlowPositioned()) {
UpdateOutOfFlowBlockLayout();
return;
}
NGConstraintSpace constraint_space =
NGConstraintSpace::CreateFromLayoutObject(*this);
scoped_refptr<NGLayoutResult> result =
NGBlockNode(this).Layout(constraint_space);
for (NGOutOfFlowPositionedDescendant descendant :
result->OutOfFlowPositionedDescendants())
descendant.node.UseOldOutOfFlowPositioning();
const NGPhysicalBoxFragment* fragment =
ToNGPhysicalBoxFragment(result->PhysicalFragment());
// This object has already been positioned in legacy layout by our containing
// block. Copy the position and place the fragment.
//
// TODO(kojii): This object is not positioned yet when the containing legacy
// layout is not normal flow; e.g., table or flexbox. They lay out children to
// determine the overall layout, then move children. In flexbox case,
// LayoutLineItems() lays out children, which calls this function. Then later,
// ApplyLineItemPosition() changes Location() of the children. See also
// NGPhysicalFragment::IsPlacedByLayoutNG(). crbug.com/788590
//
// TODO(crbug.com/781241): LogicalLeft() is not calculated by the
// containing block until after our layout.
const LayoutBlock* containing_block = ContainingBlock();
NGPhysicalOffset physical_offset;
if (containing_block) {
NGPhysicalSize containing_block_size(containing_block->Size().Width(),
containing_block->Size().Height());
NGLogicalOffset logical_offset(LogicalLeft(), LogicalTop());
physical_offset = logical_offset.ConvertToPhysical(
constraint_space.GetWritingMode(), constraint_space.Direction(),
containing_block_size, fragment->Size());
}
result->SetOffset(physical_offset);
}
void LayoutNGBlockFlow::UpdateOutOfFlowBlockLayout() {
LayoutBoxModelObject* css_container = ToLayoutBoxModelObject(Container());
LayoutBox* container =
css_container->IsBox() ? ToLayoutBox(css_container) : ContainingBlock();
const ComputedStyle* container_style = container->Style();
const ComputedStyle* parent_style = Parent()->Style();
NGConstraintSpace constraint_space =
NGConstraintSpace::CreateFromLayoutObject(*this);
NGBlockNode container_node(container);
NGBoxFragmentBuilder container_builder(
container_node, scoped_refptr<const ComputedStyle>(container_style),
container_style->GetWritingMode(), container_style->Direction());
container_builder.SetIsNewFormattingContext(
container_node.CreatesNewFormattingContext());
// Compute ContainingBlock logical size.
// OverrideContainingBlockContentLogicalWidth/Height are used by e.g. grid
// layout. Override sizes are padding box size, not border box, so we must add
// borders and scrollbars to compensate.
NGBoxStrut borders_and_scrollbars =
ComputeBorders(constraint_space, *container_style) +
NGBlockNode(container).GetScrollbarSizes();
// Calculate the border-box size of the object that's the containing block of
// this out-of-flow positioned descendant. Note that this is not to be used as
// the containing block size to resolve sizes and positions for the
// descendant, since we're dealing with the border box here (not the padding
// box, which is where the containing block is established). These sizes are
// just used to do a fake/partial NG layout pass of the containing block (that
// object is really managed by legacy layout).
LayoutUnit container_border_box_logical_width;
LayoutUnit container_border_box_logical_height;
if (HasOverrideContainingBlockContentLogicalWidth()) {
container_border_box_logical_width =
OverrideContainingBlockContentLogicalWidth() +
borders_and_scrollbars.InlineSum();
} else {
container_border_box_logical_width = container->LogicalWidth();
}
if (HasOverrideContainingBlockContentLogicalHeight()) {
container_border_box_logical_height =
OverrideContainingBlockContentLogicalHeight() +
borders_and_scrollbars.BlockSum();
} else {
container_border_box_logical_height = container->LogicalHeight();
}
container_builder.SetInlineSize(container_border_box_logical_width);
container_builder.SetBlockSize(container_border_box_logical_height);
container_builder.SetBorders(
ComputeBorders(constraint_space, *container_style));
container_builder.SetPadding(
ComputePadding(constraint_space, *container_style));
// Calculate the actual size of the containing block for this out-of-flow
// descendant. This is what's used to size and position us.
LayoutUnit containing_block_logical_width =
ContainingBlockLogicalWidthForPositioned(css_container);
LayoutUnit containing_block_logical_height =
ContainingBlockLogicalHeightForPositioned(css_container);
// Determine static position.
// static_inline and static_block are inline/block direction offsets
// from physical origin. This is an unexpected blend of logical and
// physical in a single variable.
LayoutUnit static_inline;
LayoutUnit static_block;
Length logical_left;
Length logical_right;
Length logical_top;
Length logical_bottom;
ComputeInlineStaticDistance(logical_left, logical_right, this, css_container,
containing_block_logical_width);
ComputeBlockStaticDistance(logical_top, logical_bottom, this, css_container);
if (parent_style->IsLeftToRightDirection()) {
if (!logical_left.IsAuto()) {
static_inline =
ValueForLength(logical_left, containing_block_logical_width);
}
} else {
if (!logical_right.IsAuto()) {
static_inline =
ValueForLength(logical_right, containing_block_logical_width);
}
}
if (!logical_top.IsAuto())
static_block = ValueForLength(logical_top, containing_block_logical_height);
// Legacy static position is relative to padding box. Convert to border
// box. Also flip offsets as necessary to make them relative to to the
// left/top edges.
// First convert the static inline offset to a line-left offset, i.e. physical
// left or top.
LayoutUnit inline_left_or_top =
parent_style->IsLeftToRightDirection()
? static_inline
: containing_block_logical_width - static_inline;
// Now make it relative to the left or top border edge of the containing
// block.
inline_left_or_top +=
borders_and_scrollbars.LineLeft(container_style->Direction());
// Make the static block offset relative to the start border edge of the
// containing block.
static_block += borders_and_scrollbars.block_start;
// Then convert it to a physical top or left offset. Since we're already
// border-box relative, flip it around the size of the border box, rather than
// the size of the containing block (padding box).
LayoutUnit block_top_or_left =
container_style->IsFlippedBlocksWritingMode()
? container_border_box_logical_height - static_block
: static_block;
// Convert to physical direction. This can be done with a simple coordinate
// flip because the distances are relative to physical top/left origin.
NGPhysicalOffset static_location =
container_style->IsHorizontalWritingMode()
? NGPhysicalOffset(inline_left_or_top, block_top_or_left)
: NGPhysicalOffset(block_top_or_left, inline_left_or_top);
NGStaticPosition static_position =
NGStaticPosition::Create(container_style->GetWritingMode(),
parent_style->Direction(), static_location);
// Set correct container for inline containing blocks.
container_builder.AddOutOfFlowLegacyCandidate(
NGBlockNode(this), static_position,
css_container->IsBox() ? nullptr : css_container);
// We really only want to lay out ourselves here, so we pass |this| to
// Run(). Otherwise, NGOutOfFlowLayoutPart may also lay out other objects
// it discovers that are part of the same containing block, but those
// should get laid out by the actual containing block.
NGOutOfFlowLayoutPart(
&container_builder, css_container->CanContainAbsolutePositionObjects(),
css_container->CanContainFixedPositionObjects(), borders_and_scrollbars,
constraint_space, *container_style)
.Run(/* only_layout */ this);
scoped_refptr<NGLayoutResult> result = container_builder.ToBoxFragment();
// These are the unpositioned OOF descendants of the current OOF block.
for (NGOutOfFlowPositionedDescendant descendant :
result->OutOfFlowPositionedDescendants())
descendant.node.UseOldOutOfFlowPositioning();
const NGPhysicalBoxFragment* fragment =
ToNGPhysicalBoxFragment(result->PhysicalFragment());
DCHECK_GT(fragment->Children().size(), 0u);
// Copy sizes of all child fragments to Legacy.
// There could be multiple fragments, when this node has descendants whose
// container is this node's container.
// Example: fixed descendant of fixed element.
for (auto& child : fragment->Children()) {
const NGPhysicalFragment* child_fragment = child.get();
DCHECK(child_fragment->GetLayoutObject()->IsBox());
LayoutBox* child_legacy_box =
ToLayoutBox(child_fragment->GetLayoutObject());
NGPhysicalOffset child_offset = child.Offset();
if (container_style->IsFlippedBlocksWritingMode()) {
child_legacy_box->SetX(container_border_box_logical_height -
child_offset.left - child_fragment->Size().width);
} else {
child_legacy_box->SetX(child_offset.left);
}
child_legacy_box->SetY(child_offset.top);
}
DCHECK_EQ(fragment->Children()[0]->GetLayoutObject(), this);
SetIsLegacyInitiatedOutOfFlowLayout(true);
}
} // namespace blink