| // 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 NGBlockLayoutAlgorithm_h |
| #define NGBlockLayoutAlgorithm_h |
| |
| #include "base/memory/scoped_refptr.h" |
| #include "third_party/blink/renderer/core/core_export.h" |
| #include "third_party/blink/renderer/core/layout/ng/geometry/ng_margin_strut.h" |
| #include "third_party/blink/renderer/core/layout/ng/ng_block_break_token.h" |
| #include "third_party/blink/renderer/core/layout/ng/ng_block_node.h" |
| #include "third_party/blink/renderer/core/layout/ng/ng_fragment_builder.h" |
| #include "third_party/blink/renderer/core/layout/ng/ng_layout_algorithm.h" |
| #include "third_party/blink/renderer/core/layout/ng/ng_unpositioned_float.h" |
| #include "third_party/blink/renderer/core/layout/ng/ng_unpositioned_float_vector.h" |
| |
| namespace blink { |
| |
| class NGConstraintSpace; |
| class NGFragment; |
| class NGLayoutResult; |
| class NGPhysicalLineBoxFragment; |
| |
| // This struct is used for communicating to a child the position of the previous |
| // inflow child. This will be used to calculate the position of the next child. |
| struct NGPreviousInflowPosition { |
| LayoutUnit logical_block_offset; |
| NGMarginStrut margin_strut; |
| bool empty_block_affected_by_clearance; |
| }; |
| |
| // This strut holds information for the current inflow child. The data is not |
| // useful outside of handling this single inflow child. |
| struct NGInflowChildData { |
| NGBfcOffset bfc_offset_estimate; |
| NGMarginStrut margin_strut; |
| NGBoxStrut margins; |
| bool force_clearance; |
| }; |
| |
| // A class for general block layout (e.g. a <div> with no special style). |
| // Lays out the children in sequence. |
| class CORE_EXPORT NGBlockLayoutAlgorithm |
| : public NGLayoutAlgorithm<NGBlockNode, |
| NGFragmentBuilder, |
| NGBlockBreakToken> { |
| public: |
| // Default constructor. |
| // @param node The input node to perform layout upon. |
| // @param space The constraint space which the algorithm should generate a |
| // fragment within. |
| // @param break_token The break token from which the layout should start. |
| NGBlockLayoutAlgorithm(NGBlockNode node, |
| const NGConstraintSpace& space, |
| NGBlockBreakToken* break_token = nullptr); |
| |
| ~NGBlockLayoutAlgorithm() override; |
| |
| void SetBoxType(NGPhysicalFragment::NGBoxType type); |
| |
| base::Optional<MinMaxSize> ComputeMinMaxSize( |
| const MinMaxSizeInput&) const override; |
| scoped_refptr<NGLayoutResult> Layout() override; |
| |
| private: |
| // Return the BFC block offset of this block. |
| LayoutUnit BfcBlockOffset() const { |
| // If we have resolved our BFC block offset, use that. |
| if (container_builder_.BfcBlockOffset()) |
| return *container_builder_.BfcBlockOffset(); |
| // Otherwise fall back to the BFC block offset assigned by the parent |
| // algorithm. |
| return ConstraintSpace().BfcOffset().block_offset; |
| } |
| |
| // Return the BFC block offset of the next block-start border edge (for some |
| // child) we'd get if we commit pending margins. |
| LayoutUnit NextBorderEdge( |
| const NGPreviousInflowPosition& previous_inflow_position) const { |
| return BfcBlockOffset() + previous_inflow_position.logical_block_offset + |
| previous_inflow_position.margin_strut.Sum(); |
| } |
| |
| NGBoxStrut CalculateMargins(NGLayoutInputNode child, |
| const NGBreakToken* child_break_token); |
| |
| // Creates a new constraint space for the current child. |
| scoped_refptr<NGConstraintSpace> CreateConstraintSpaceForChild( |
| const NGLayoutInputNode child, |
| const NGInflowChildData& child_data, |
| const NGLogicalSize child_available_size, |
| const base::Optional<LayoutUnit> floats_bfc_block_offset = base::nullopt); |
| |
| // @return Estimated BFC block offset for the "to be layout" child. |
| NGInflowChildData ComputeChildData(const NGPreviousInflowPosition&, |
| NGLayoutInputNode, |
| const NGBreakToken* child_break_token, |
| bool force_clearance); |
| |
| NGPreviousInflowPosition ComputeInflowPosition( |
| const NGPreviousInflowPosition&, |
| const NGLayoutInputNode child, |
| const NGInflowChildData&, |
| const base::Optional<LayoutUnit>& child_bfc_block_offset, |
| const NGLogicalOffset&, |
| const NGLayoutResult&, |
| const NGFragment&, |
| bool empty_block_affected_by_clearance); |
| |
| // Position an empty child using the parent BFC block offset. |
| // The fragment doesn't know its offset, but we can still calculate its BFC |
| // position because the parent fragment's BFC is known. |
| // Example: |
| // BFC Offset is known here because of the padding. |
| // <div style="padding: 1px"> |
| // <div id="empty-div" style="margin: 1px"></div> |
| LayoutUnit PositionEmptyChildWithParentBfc( |
| const NGLayoutInputNode& child, |
| const NGConstraintSpace& child_space, |
| const NGInflowChildData& child_data, |
| const NGLayoutResult&) const; |
| |
| void HandleOutOfFlowPositioned(const NGPreviousInflowPosition&, NGBlockNode); |
| void HandleFloat(const NGPreviousInflowPosition&, |
| NGBlockNode, |
| NGBlockBreakToken*); |
| |
| // This uses the NGLayoutOpporunityIterator to position the fragment. |
| // |
| // An element that establishes a new formatting context must not overlap the |
| // margin box of any floats within the current BFC. |
| // |
| // Example: |
| // <div id="container"> |
| // <div id="float"></div> |
| // <div id="new-fc" style="margin-top: 20px;"></div> |
| // </div> |
| // 1) If #new-fc is small enough to fit the available space right from #float |
| // then it will be placed there and we collapse its margin. |
| // 2) If #new-fc is too big then we need to clear its position and place it |
| // below #float ignoring its vertical margin. |
| // |
| // Returns false if we need to abort layout, because a previously unknown BFC |
| // block offset has now been resolved. |
| bool HandleNewFormattingContext( |
| NGLayoutInputNode child, |
| NGBreakToken* child_break_token, |
| NGPreviousInflowPosition*, |
| scoped_refptr<NGBreakToken>* previous_inline_break_token); |
| |
| // Performs the actual layout of a new formatting context. This may be called |
| // multiple times from HandleNewFormattingContext. |
| std::pair<scoped_refptr<NGLayoutResult>, NGLayoutOpportunity> |
| LayoutNewFormattingContext(NGLayoutInputNode child, |
| NGBreakToken* child_break_token, |
| const NGInflowChildData&, |
| NGBfcOffset origin_offset, |
| bool abort_if_cleared); |
| |
| // Handle an in-flow child. |
| // Returns false if we need to abort layout, because a previously unknown BFC |
| // block offset has now been resolved. (Same as HandleNewFormattingContext). |
| bool HandleInflow(NGLayoutInputNode child, |
| NGBreakToken* child_break_token, |
| NGPreviousInflowPosition*, |
| scoped_refptr<NGBreakToken>* previous_inline_break_token); |
| |
| // Return the amount of block space available in the current fragmentainer |
| // for the node being laid out by this algorithm. |
| LayoutUnit FragmentainerSpaceAvailable() const; |
| |
| // Return true if the node being laid out by this fragmentainer has used all |
| // the available space in the current fragmentainer. |
| // |block_offset| is the border-edge relative block offset we want to check |
| // whether fits within the fragmentainer or not. |
| bool IsFragmentainerOutOfSpace(LayoutUnit block_offset) const; |
| |
| // Insert a fragmentainer break before the child if necessary. |
| // Update previous in-flow position and return true if a break was inserted. |
| // Otherwise return false. |
| bool BreakBeforeChild(NGLayoutInputNode child, |
| const NGLayoutResult&, |
| NGPreviousInflowPosition*, |
| LayoutUnit block_offset, |
| bool is_pushed_by_floats); |
| |
| enum BreakType { NoBreak, SoftBreak, ForcedBreak }; |
| |
| // Given a child fragment and the corresponding node's style, determine the |
| // type of break we should insert in front of it, if any. |
| BreakType BreakTypeBeforeChild(NGLayoutInputNode child, |
| const NGLayoutResult&, |
| LayoutUnit block_offset, |
| bool is_pushed_by_floats) const; |
| |
| // Final adjustments before fragment creation. We need to prevent the |
| // fragment from crossing fragmentainer boundaries, and rather create a break |
| // token if we're out of space. |
| void FinalizeForFragmentation(); |
| |
| void PropagateBaselinesFromChildren(); |
| bool AddBaseline(const NGBaselineRequest&, |
| const NGPhysicalFragment*, |
| LayoutUnit child_offset); |
| |
| // Compute the baseline offset of a line box from the content box. |
| // Line boxes are in line-relative coordinates. This function returns the |
| // offset in flow-relative coordinates. |
| LayoutUnit ComputeLineBoxBaselineOffset( |
| const NGBaselineRequest&, |
| const NGPhysicalLineBoxFragment&, |
| LayoutUnit line_box_block_offset) const; |
| |
| // If still unresolved, resolve the fragment's BFC block offset. |
| // |
| // This includes applying clearance, so the bfc_block_offset passed won't be |
| // the final BFC block offset, if it wasn't large enough to get past all |
| // relevant floats. The updated BFC block offset can be read out with |
| // ContainerBfcBlockOffset(). |
| // |
| // In addition to resolving our BFC block offset, this will also position |
| // pending floats, and update our in-flow layout state. Returns false if |
| // resolving the BFC block offset resulted in needing to abort layout. It |
| // will always return true otherwise. If the BFC block offset was already |
| // resolved, this method does nothing (and returns true). |
| bool ResolveBfcBlockOffset(NGPreviousInflowPosition*, |
| LayoutUnit bfc_block_offset); |
| |
| // A very common way to resolve the BFC block offset is to simply commit the |
| // pending margin, so here's a convenience overload for that. |
| bool ResolveBfcBlockOffset( |
| NGPreviousInflowPosition* previous_inflow_position) { |
| return ResolveBfcBlockOffset(previous_inflow_position, |
| NextBorderEdge(*previous_inflow_position)); |
| } |
| |
| // Return true if the BFC block offset has changed and this means that we |
| // need to abort layout. |
| bool NeedsAbortOnBfcBlockOffsetChange() const; |
| |
| // Positions pending floats starting from {@origin_block_offset}. |
| void PositionPendingFloats(LayoutUnit origin_block_offset); |
| |
| // Adds a set of positioned floats as children to the current fragment. |
| void AddPositionedFloats(const Vector<NGPositionedFloat>& positioned_floats); |
| |
| // Positions a list marker for the specified block content. |
| void PositionOrPropagateListMarker(const NGLayoutResult&, NGLogicalOffset*); |
| |
| // Positions a list marker when the block does not have any line boxes. |
| void PositionListMarkerWithoutLineBoxes(); |
| |
| // Calculates logical offset for the current fragment using either {@code |
| // intrinsic_block_size_} when the fragment doesn't know it's offset or |
| // {@code known_fragment_offset} if the fragment knows it's offset |
| // @return Fragment's offset relative to the fragment's parent. |
| NGLogicalOffset CalculateLogicalOffset( |
| const NGFragment& fragment, |
| LayoutUnit child_bfc_line_offset, |
| const base::Optional<LayoutUnit>& child_bfc_block_offset); |
| |
| // Computes default content size for HTML and BODY elements in quirks mode. |
| // Returns NGSizeIndefinite in all other cases. |
| LayoutUnit CalculateDefaultBlockSize(); |
| |
| // Computes minimum size for HTML and BODY elements in quirks mode. |
| // Returns NGSizeIndefinite in all other cases. |
| LayoutUnit CalculateMinimumBlockSize(const NGMarginStrut& end_margin_strut); |
| |
| NGLogicalSize child_available_size_; |
| NGLogicalSize child_percentage_size_; |
| |
| NGBoxStrut border_padding_; |
| NGBoxStrut border_scrollbar_padding_; |
| LayoutUnit intrinsic_block_size_; |
| |
| // The line box index at which we ran out of space. This where we'll actually |
| // end up breaking, unless we determine that we should break earlier in order |
| // to satisfy the widows request. |
| int first_overflowing_line_ = 0; |
| |
| // Set if we should fit as many lines as there's room for, i.e. no early |
| // break. In that case we'll break before first_overflowing_line_. In this |
| // case there'll either be enough widows for the next fragment, or we have |
| // determined that we're unable to fulfill the widows request. |
| bool fit_all_lines_ = false; |
| |
| // Set if we're resuming layout of a node that has already produced fragments. |
| bool is_resuming_; |
| |
| // Set when we're to abort if the BFC block offset gets resolved or updated. |
| // Sometimes we walk past elements (i.e. floats) that depend on the BFC block |
| // offset being known (in order to position and lay themselves out properly). |
| // When this happens, and we finally manage to resolve (or update) the BFC |
| // block offset at some subsequent element, we need to check if this flag is |
| // set, and abort layout if it is. |
| bool abort_when_bfc_block_offset_updated_ = false; |
| |
| bool has_processed_first_child_ = false; |
| |
| std::unique_ptr<NGExclusionSpace> exclusion_space_; |
| NGUnpositionedFloatVector unpositioned_floats_; |
| }; |
| |
| } // namespace blink |
| |
| #endif // NGBlockLayoutAlgorithm_h |