| // 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. |
| |
| #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_LAYOUT_INPUT_NODE_H_ |
| #define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_LAYOUT_INPUT_NODE_H_ |
| |
| #include "base/optional.h" |
| #include "third_party/blink/renderer/core/core_export.h" |
| #include "third_party/blink/renderer/core/display_lock/display_lock_context.h" |
| #include "third_party/blink/renderer/core/layout/geometry/axis.h" |
| #include "third_party/blink/renderer/core/layout/geometry/logical_size.h" |
| #include "third_party/blink/renderer/core/layout/layout_box.h" |
| #include "third_party/blink/renderer/core/layout/ng/layout_box_utils.h" |
| #include "third_party/blink/renderer/core/layout/ng/list/layout_ng_outside_list_marker.h" |
| #include "third_party/blink/renderer/platform/geometry/layout_unit.h" |
| #include "third_party/blink/renderer/platform/text/writing_mode.h" |
| |
| namespace blink { |
| |
| class ComputedStyle; |
| class DisplayLockContext; |
| class Document; |
| class LayoutObject; |
| class LayoutBox; |
| class NGConstraintSpace; |
| struct MinMaxSizes; |
| struct PhysicalSize; |
| |
| // The input to the min/max inline size calculation algorithm for child nodes. |
| // Child nodes within the same formatting context need to know which floats are |
| // beside them. |
| struct MinMaxSizesInput { |
| // The min-max size calculation (un-intuitively) requires a percentage |
| // resolution size! |
| // This occurs when a replaced element has an intrinsic size. E.g. |
| // <div style="float: left; height: 100px"> |
| // <img sr="intrinsic-ratio-1x1.png" style="height: 50%;" /> |
| // </div> |
| // In the above example float ends up with a width of 50px. |
| // |
| // As we don't perform any tree walking, we need to pass the percentage |
| // resolution block-size for min/max down the min/max size calculation. |
| MinMaxSizesInput(LayoutUnit percentage_resolution_block_size, |
| MinMaxSizesType type) |
| : percentage_resolution_block_size(percentage_resolution_block_size), |
| type(type) {} |
| LayoutUnit float_left_inline_size; |
| LayoutUnit float_right_inline_size; |
| LayoutUnit percentage_resolution_block_size; |
| |
| MinMaxSizesType type; |
| }; |
| |
| // The output of the min/max inline size calculation algorithm. Contains the |
| // min/max sizes, and if this calculation will change if the |
| // |MinMaxSizesInput::percentage_resolution_block_size| value changes. |
| struct MinMaxSizesResult { |
| MinMaxSizesResult() = default; |
| MinMaxSizesResult(MinMaxSizes sizes, bool depends_on_percentage_block_size) |
| : sizes(sizes), |
| depends_on_percentage_block_size(depends_on_percentage_block_size) {} |
| |
| MinMaxSizes sizes; |
| bool depends_on_percentage_block_size = false; |
| }; |
| |
| // Represents the input to a layout algorithm for a given node. The layout |
| // engine should use the style, node type to determine which type of layout |
| // algorithm to use to produce fragments for this node. |
| class CORE_EXPORT NGLayoutInputNode { |
| DISALLOW_NEW(); |
| |
| public: |
| enum NGLayoutInputNodeType { |
| kBlock, |
| kInline |
| // When adding new values, ensure type_ below has enough bits. |
| }; |
| |
| static NGLayoutInputNode Create(LayoutBox* box, NGLayoutInputNodeType type) { |
| // This function should create an instance of the subclass. This works |
| // because subclasses are not virtual and do not add fields. |
| return NGLayoutInputNode(box, type); |
| } |
| |
| NGLayoutInputNode(std::nullptr_t) : box_(nullptr), type_(kBlock) {} |
| |
| NGLayoutInputNodeType Type() const { |
| return static_cast<NGLayoutInputNodeType>(type_); |
| } |
| bool IsInline() const { return type_ == kInline; } |
| bool IsBlock() const { return type_ == kBlock; } |
| |
| bool IsBlockFlow() const { return IsBlock() && box_->IsLayoutBlockFlow(); } |
| bool IsLayoutNGCustom() const { |
| return IsBlock() && box_->IsLayoutNGCustom(); |
| } |
| bool IsColumnSpanAll() const { return IsBlock() && box_->IsColumnSpanAll(); } |
| bool IsFloating() const { return IsBlock() && box_->IsFloating(); } |
| bool IsOutOfFlowPositioned() const { |
| return IsBlock() && box_->IsOutOfFlowPositioned(); |
| } |
| bool IsFloatingOrOutOfFlowPositioned() const { |
| return IsFloating() || IsOutOfFlowPositioned(); |
| } |
| bool IsReplaced() const { return box_->IsLayoutReplaced(); } |
| bool IsAbsoluteContainer() const { |
| return box_->CanContainAbsolutePositionObjects(); |
| } |
| bool IsFixedContainer() const { |
| return box_->CanContainFixedPositionObjects(); |
| } |
| bool IsBody() const { return IsBlock() && box_->IsBody(); } |
| bool IsDocumentElement() const { return box_->IsDocumentElement(); } |
| bool IsFlexItem() const { return IsBlock() && box_->IsFlexItemIncludingNG(); } |
| bool IsFlexibleBox() const { |
| return IsBlock() && box_->IsFlexibleBoxIncludingNG(); |
| } |
| bool IsGrid() const { return IsBlock() && box_->IsLayoutGridIncludingNG(); } |
| bool ShouldBeConsideredAsReplaced() const { |
| return box_->ShouldBeConsideredAsReplaced(); |
| } |
| bool IsListItem() const { return IsBlock() && box_->IsLayoutNGListItem(); } |
| bool IsListMarker() const { |
| return IsBlock() && box_->IsLayoutNGOutsideListMarker(); |
| } |
| bool ListMarkerOccupiesWholeLine() const { |
| DCHECK(IsListMarker()); |
| return To<LayoutNGOutsideListMarker>(box_)->NeedsOccupyWholeLine(); |
| } |
| bool IsButton() const { return IsBlock() && box_->IsLayoutNGButton(); } |
| bool IsFieldsetContainer() const { |
| return IsBlock() && box_->IsLayoutNGFieldset(); |
| } |
| bool IsRubyRun() const { return IsBlock() && box_->IsRubyRun(); } |
| bool IsRubyText() const { return box_->IsRubyText(); } |
| |
| // Return true if this is the legend child of a fieldset that gets special |
| // treatment (i.e. placed over the block-start border). |
| bool IsRenderedLegend() const { |
| return IsBlock() && box_->IsRenderedLegend(); |
| } |
| // Return true if this node is for <input type=range>. |
| bool IsSlider() const; |
| // Return true if this node is for a slider thumb in <input type=range>. |
| bool IsSliderThumb() const; |
| bool IsTable() const { return IsBlock() && box_->IsTable(); } |
| bool IsNGTable() const { return IsTable() && box_->IsLayoutNGMixin(); } |
| |
| bool IsTableCaption() const { return IsBlock() && box_->IsTableCaption(); } |
| |
| // Section with empty rows is considered empty. |
| bool IsEmptyTableSection() const; |
| |
| bool IsTableCol() const { |
| return Style().Display() == EDisplay::kTableColumn; |
| } |
| |
| bool IsTableColgroup() const { |
| return Style().Display() == EDisplay::kTableColumnGroup; |
| } |
| |
| wtf_size_t TableColumnSpan() const; |
| |
| wtf_size_t TableCellColspan() const; |
| |
| wtf_size_t TableCellRowspan() const; |
| |
| bool IsTextArea() const { return box_->IsTextAreaIncludingNG(); } |
| bool IsTextControl() const { return box_->IsTextControlIncludingNG(); } |
| bool IsTextControlPlaceholder() const; |
| bool IsTextField() const { return box_->IsTextFieldIncludingNG(); } |
| |
| bool IsMathRoot() const { return box_->IsMathMLRoot(); } |
| bool IsMathML() const { return box_->IsMathML(); } |
| |
| bool IsAnonymousBlock() const { return box_->IsAnonymousBlock(); } |
| |
| // If the node is a quirky container for margin collapsing, see: |
| // https://html.spec.whatwg.org/C/#margin-collapsing-quirks |
| // NOTE: The spec appears to only somewhat match reality. |
| bool IsQuirkyContainer() const { |
| return box_->GetDocument().InQuirksMode() && |
| (box_->IsBody() || box_->IsTableCell()); |
| } |
| |
| // Return true if this node is monolithic for block fragmentation. |
| bool IsMonolithic() const { |
| // Lines are always monolithic. We cannot block-fragment inside them. |
| if (IsInline()) |
| return true; |
| return box_->GetNGPaginationBreakability() == LayoutBox::kForbidBreaks; |
| } |
| |
| bool IsScrollContainer() const { |
| return IsBlock() && box_->IsScrollContainer(); |
| } |
| |
| bool CreatesNewFormattingContext() const { |
| return IsBlock() && box_->CreatesNewFormattingContext(); |
| } |
| |
| // Returns true if this node should pass its percentage resolution block-size |
| // to its children. Typically only quirks-mode, auto block-size, block nodes. |
| bool UseParentPercentageResolutionBlockSizeForChildren() const { |
| auto* layout_block = DynamicTo<LayoutBlock>(box_); |
| if (IsBlock() && layout_block) { |
| return LayoutBoxUtils::SkipContainingBlockForPercentHeightCalculation( |
| layout_block); |
| } |
| |
| return false; |
| } |
| |
| // Returns the border-box min/max content sizes for the node. |
| MinMaxSizesResult ComputeMinMaxSizes( |
| WritingMode, |
| const MinMaxSizesInput&, |
| const NGConstraintSpace* = nullptr) const; |
| |
| // Returns intrinsic sizing information for replaced elements. |
| // ComputeReplacedSize can use it to compute actual replaced size. |
| // Corresponds to Legacy's LayoutReplaced::IntrinsicSizingInfo. |
| // Use NGBlockNode::GetAspectRatio to get the aspect ratio. |
| void IntrinsicSize(base::Optional<LayoutUnit>* computed_inline_size, |
| base::Optional<LayoutUnit>* computed_block_size) const; |
| |
| // Returns the next sibling. |
| NGLayoutInputNode NextSibling() const; |
| |
| Document& GetDocument() const { return box_->GetDocument(); } |
| |
| Node* GetDOMNode() const { return box_->GetNode(); } |
| |
| PhysicalSize InitialContainingBlockSize() const; |
| |
| // Returns the LayoutObject which is associated with this node. |
| LayoutBox* GetLayoutBox() const { return box_; } |
| |
| const ComputedStyle& Style() const { return box_->StyleRef(); } |
| |
| bool ShouldApplySizeContainment() const { |
| return box_->ShouldApplySizeContainment(); |
| } |
| // Return true if we should apply at least inline-size containment |
| // (i.e. "contain" is "size" or "inline-size"). |
| bool ShouldApplyInlineSizeContainment() const { |
| return box_->ShouldApplyInlineSizeContainment(); |
| } |
| // Return true if we should apply at least block-size containment |
| // (i.e. "contain" is "size" or "block-size"). |
| bool ShouldApplyBlockSizeContainment() const { |
| return box_->ShouldApplyBlockSizeContainment(); |
| } |
| |
| bool IsContainerForContainerQueries() const { |
| return box_->IsContainerForContainerQueries(); |
| } |
| |
| LogicalAxes ContainedAxes() const { |
| LogicalAxes axes(kLogicalAxisNone); |
| if (ShouldApplyInlineSizeContainment()) |
| axes |= LogicalAxes(kLogicalAxisInline); |
| if (ShouldApplyBlockSizeContainment()) |
| axes |= LogicalAxes(kLogicalAxisBlock); |
| return axes; |
| } |
| |
| // CSS defines certain cases to synthesize inline block baselines from box. |
| // See comments in UseLogicalBottomMarginEdgeForInlineBlockBaseline(). |
| bool UseBlockEndMarginEdgeForInlineBlockBaseline() const { |
| if (auto* layout_box = DynamicTo<LayoutBlock>(GetLayoutBox())) |
| return layout_box->UseLogicalBottomMarginEdgeForInlineBlockBaseline(); |
| return false; |
| } |
| |
| // CSS intrinsic sizing getters. |
| // https://drafts.csswg.org/css-sizing-4/#intrinsic-size-override |
| // Note that this returns kIndefiniteSize if the override was not specified. |
| LayoutUnit OverrideIntrinsicContentInlineSize() const { |
| if (box_->HasOverrideIntrinsicContentLogicalWidth()) |
| return box_->OverrideIntrinsicContentLogicalWidth(); |
| return kIndefiniteSize; |
| } |
| // Note that this returns kIndefiniteSize if the override was not specified. |
| LayoutUnit OverrideIntrinsicContentBlockSize() const { |
| if (box_->HasOverrideIntrinsicContentLogicalHeight()) |
| return box_->OverrideIntrinsicContentLogicalHeight(); |
| return kIndefiniteSize; |
| } |
| |
| LayoutUnit DefaultIntrinsicContentInlineSize() const { |
| return box_->DefaultIntrinsicContentInlineSize(); |
| } |
| LayoutUnit DefaultIntrinsicContentBlockSize() const { |
| return box_->DefaultIntrinsicContentBlockSize(); |
| } |
| |
| // Display locking functionality. |
| const DisplayLockContext& GetDisplayLockContext() const { |
| DCHECK(box_->GetDisplayLockContext()); |
| return *box_->GetDisplayLockContext(); |
| } |
| bool ChildLayoutBlockedByDisplayLock() const { |
| return box_->ChildLayoutBlockedByDisplayLock(); |
| } |
| |
| CustomLayoutChild* GetCustomLayoutChild() const { |
| // TODO(ikilpatrick): Support NGInlineNode. |
| DCHECK(IsBlock()); |
| return box_->GetCustomLayoutChild(); |
| } |
| |
| // Return whether we can directly traverse fragments generated from this node |
| // (for painting, hit-testing and other layout read operations). If false is |
| // returned, we need to traverse the layout object tree instead. |
| bool CanTraversePhysicalFragments() const { |
| return box_->CanTraversePhysicalFragments(); |
| } |
| |
| String ToString() const; |
| |
| explicit operator bool() const { return box_ != nullptr; } |
| |
| bool operator==(const NGLayoutInputNode& other) const { |
| return box_ == other.box_; |
| } |
| |
| bool operator!=(const NGLayoutInputNode& other) const { |
| return !(*this == other); |
| } |
| |
| #if DCHECK_IS_ON() |
| void ShowNodeTree() const; |
| #endif |
| |
| protected: |
| NGLayoutInputNode(LayoutBox* box, NGLayoutInputNodeType type) |
| : box_(box), type_(type) {} |
| |
| void GetOverrideIntrinsicSize( |
| base::Optional<LayoutUnit>* computed_inline_size, |
| base::Optional<LayoutUnit>* computed_block_size) const; |
| |
| LayoutBox* box_; |
| |
| unsigned type_ : 1; // NGLayoutInputNodeType |
| }; |
| |
| } // namespace blink |
| |
| #endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_LAYOUT_INPUT_NODE_H_ |