blob: bc31a16375c3bfefb47d18d1e7cf82160480e320 [file] [log] [blame]
// Copyright 2017 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_mixin.h"
#include <memory>
#include <utility>
#include "third_party/blink/renderer/core/layout/layout_view.h"
#include "third_party/blink/renderer/core/layout/ng/layout_box_utils.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_layout_result.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/paint/paint_layer_scrollable_area.h"
namespace blink {
template <typename Base>
LayoutNGMixin<Base>::LayoutNGMixin(Element* element) : Base(element) {
static_assert(
std::is_base_of<LayoutBlock, Base>::value,
"Base class of LayoutNGMixin must be LayoutBlock or derived class.");
DCHECK(!element || !element->ShouldForceLegacyLayout());
}
template <typename Base>
LayoutNGMixin<Base>::~LayoutNGMixin() = default;
template <typename Base>
bool LayoutNGMixin<Base>::IsOfType(LayoutObject::LayoutObjectType type) const {
return type == LayoutObject::kLayoutObjectNGMixin || Base::IsOfType(type);
}
template <typename Base>
void LayoutNGMixin<Base>::ComputeIntrinsicLogicalWidths(
LayoutUnit& min_logical_width,
LayoutUnit& max_logical_width) const {
NGBlockNode node(const_cast<LayoutNGMixin<Base>*>(this));
if (!node.CanUseNewLayout()) {
Base::ComputeIntrinsicLogicalWidths(min_logical_width, max_logical_width);
return;
}
LayoutUnit available_logical_height =
LayoutBoxUtils::AvailableLogicalHeight(*this, Base::ContainingBlock());
MinMaxSizeInput input(available_logical_height);
// This function returns content-box plus scrollbar.
input.size_type = NGMinMaxSizeType::kContentBoxSize;
MinMaxSize sizes =
node.ComputeMinMaxSize(node.Style().GetWritingMode(), input);
if (Base::IsTableCell()) {
// If a table cell, or the column that it belongs to, has a specified fixed
// positive inline-size, and the measured intrinsic max size is less than
// that, use specified size as max size.
LayoutNGTableCellInterface* cell =
ToInterface<LayoutNGTableCellInterface>(node.GetLayoutBox());
Length table_cell_width = cell->StyleOrColLogicalWidth();
if (table_cell_width.IsFixed() && table_cell_width.Value() > 0) {
sizes.max_size = std::max(sizes.min_size,
Base::AdjustContentBoxLogicalWidthForBoxSizing(
LayoutUnit(table_cell_width.Value())));
}
}
sizes += LayoutUnit(Base::ScrollbarLogicalWidth());
min_logical_width = sizes.min_size;
max_logical_width = sizes.max_size;
}
template <typename Base>
void LayoutNGMixin<Base>::UpdateOutOfFlowBlockLayout() {
LayoutBoxModelObject* css_container =
ToLayoutBoxModelObject(Base::Container());
LayoutBox* container = css_container->IsBox() ? ToLayoutBox(css_container)
: Base::ContainingBlock();
const ComputedStyle* container_style = container->Style();
NGConstraintSpace constraint_space =
NGConstraintSpace::CreateFromLayoutObject(*this,
false /* is_layout_root */);
// As this is part of the Legacy->NG bridge, the container_builder is used
// for indicating the resolved size of the OOF-positioned containing-block
// and not used for caching purposes.
// When we produce a layout result from it, we access its child fragments
// which must contain *at least* this node. We use the child fragments for
// copying back position information.
NGBlockNode container_node(container);
NGBoxFragmentBuilder container_builder(
container_node, scoped_refptr<const ComputedStyle>(container_style),
/* space */ nullptr, container_style->GetWritingMode(),
container_style->Direction());
container_builder.SetIsNewFormattingContext(
container_node.CreatesNewFormattingContext());
NGFragmentGeometry fragment_geometry;
fragment_geometry.border = ComputeBorders(constraint_space, container_node);
fragment_geometry.scrollbar =
ComputeScrollbars(constraint_space, container_node);
fragment_geometry.padding =
ComputePadding(constraint_space, *container_style);
NGBoxStrut border_scrollbar =
fragment_geometry.border + fragment_geometry.scrollbar;
// 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 (Base::HasOverrideContainingBlockContentLogicalWidth()) {
container_border_box_logical_width =
Base::OverrideContainingBlockContentLogicalWidth() +
border_scrollbar.InlineSum();
} else {
container_border_box_logical_width = container->LogicalWidth();
}
if (Base::HasOverrideContainingBlockContentLogicalHeight()) {
container_border_box_logical_height =
Base::OverrideContainingBlockContentLogicalHeight() +
border_scrollbar.BlockSum();
} else {
container_border_box_logical_height = container->LogicalHeight();
}
fragment_geometry.border_box_size = {container_border_box_logical_width,
container_border_box_logical_height};
container_builder.SetInitialFragmentGeometry(fragment_geometry);
NGLogicalStaticPosition static_position =
LayoutBoxUtils::ComputeStaticPositionFromLegacy(*this, border_scrollbar);
// Set correct container for inline containing blocks.
container_builder.AddOutOfFlowLegacyCandidate(
NGBlockNode(this), static_position, ToLayoutInlineOrNull(css_container));
base::Optional<LogicalSize> initial_containing_block_fixed_size;
if (container->IsLayoutView() && !Base::GetDocument().Printing()) {
if (LocalFrameView* frame_view = ToLayoutView(container)->GetFrameView()) {
IntSize size =
frame_view->LayoutViewport()->ExcludeScrollbars(frame_view->Size());
PhysicalSize physical_size(size);
initial_containing_block_fixed_size =
physical_size.ConvertToLogical(container->Style()->GetWritingMode());
}
}
// 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(css_container->CanContainAbsolutePositionObjects(),
css_container->CanContainFixedPositionObjects(),
*container_style, constraint_space, border_scrollbar,
&container_builder, initial_containing_block_fixed_size)
.Run(/* only_layout */ this);
scoped_refptr<const NGLayoutResult> result =
container_builder.ToBoxFragment();
// These are the unpositioned OOF descendants of the current OOF block.
for (const auto& descendant :
result->PhysicalFragment().OutOfFlowPositionedDescendants())
descendant.node.UseLegacyOutOfFlowPositioning();
const auto& fragment = 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->GetMutableLayoutObject());
PhysicalOffset 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);
Base::SetIsLegacyInitiatedOutOfFlowLayout(true);
}
template class CORE_TEMPLATE_EXPORT LayoutNGMixin<LayoutBlock>;
template class CORE_TEMPLATE_EXPORT LayoutNGMixin<LayoutBlockFlow>;
template class CORE_TEMPLATE_EXPORT LayoutNGMixin<LayoutProgress>;
template class CORE_TEMPLATE_EXPORT LayoutNGMixin<LayoutTableCaption>;
template class CORE_TEMPLATE_EXPORT LayoutNGMixin<LayoutTableCell>;
} // namespace blink