| // 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 |