1 // Copyright 2017 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_BLOCK_BREAK_TOKEN_H_ 6 #define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_BLOCK_BREAK_TOKEN_H_ 7 8 #include "base/memory/scoped_refptr.h" 9 #include "third_party/blink/renderer/core/core_export.h" 10 #include "third_party/blink/renderer/core/layout/ng/ng_break_token.h" 11 #include "third_party/blink/renderer/platform/geometry/layout_unit.h" 12 #include "third_party/blink/renderer/platform/wtf/casting.h" 13 #include "third_party/blink/renderer/platform/wtf/vector.h" 14 15 namespace blink { 16 17 class NGBoxFragmentBuilder; 18 class NGInlineBreakToken; 19 20 // Represents a break token for a block node. 21 class CORE_EXPORT NGBlockBreakToken final : public NGBreakToken { 22 public: 23 // Creates a break token for a node which did fragment, and can potentially 24 // produce more fragments. 25 // 26 // The node is NGBlockNode, or any other NGLayoutInputNode that produces 27 // anonymous box. 28 static scoped_refptr<NGBlockBreakToken> Create(const NGBoxFragmentBuilder&); 29 30 // Creates a break token for a node that needs to produce its first fragment 31 // in the next fragmentainer. In this case we create a break token for a node 32 // that hasn't yet produced any fragments. CreateBreakBefore(NGLayoutInputNode node,bool is_forced_break)33 static scoped_refptr<NGBlockBreakToken> CreateBreakBefore( 34 NGLayoutInputNode node, 35 bool is_forced_break) { 36 auto* token = new NGBlockBreakToken(PassKey(), node); 37 token->is_break_before_ = true; 38 token->is_forced_break_ = is_forced_break; 39 return base::AdoptRef(token); 40 } 41 ~NGBlockBreakToken()42 ~NGBlockBreakToken() override { 43 for (const NGBreakToken* token : ChildBreakTokens()) 44 token->Release(); 45 } 46 47 // Represents the amount of block-size consumed by previous fragments. 48 // 49 // E.g. if the node specifies a block-size of 200px, and the previous 50 // fragments generated for this box consumed 150px in total (which is what 51 // this method would return then), there's 50px left to consume. The next 52 // fragment will become 50px tall, assuming no additional fragmentation (if 53 // the fragmentainer is shorter than 50px, for instance). ConsumedBlockSize()54 LayoutUnit ConsumedBlockSize() const { return consumed_block_size_; } 55 56 // A unique identifier for a fragment that generates a break token. This is 57 // unique within the generating layout input node. The break token of the 58 // first fragment gets 0, then second 1, and so on. Note that we don't "count" 59 // break tokens that aren't associated with a fragment (this happens when we 60 // want a fragmentainer break before laying out the node). What the sequence 61 // number is for such a break token is undefined. SequenceNumber()62 unsigned SequenceNumber() const { 63 DCHECK(!IsBreakBefore()); 64 return sequence_number_; 65 } 66 67 // Return true if this is a break token that was produced without any 68 // "preceding" fragment. This happens when we determine that the first 69 // fragment for a node needs to be created in a later fragmentainer than the 70 // one it was it was first encountered, due to block space shortage. IsBreakBefore()71 bool IsBreakBefore() const { return is_break_before_; } 72 IsForcedBreak()73 bool IsForcedBreak() const { return is_forced_break_; } 74 IsCausedByColumnSpanner()75 bool IsCausedByColumnSpanner() const { return is_caused_by_column_spanner_; } 76 77 // Return true if all children have been "seen". When we have reached this 78 // point, and resume layout in a fragmentainer, we should only process child 79 // break tokens, if any, and not attempt to start laying out nodes that don't 80 // have one (since all children are either finished, or have a break token). HasSeenAllChildren()81 bool HasSeenAllChildren() const { return has_seen_all_children_; } 82 83 // Return true if layout was past the block-end border edge of the node when 84 // it fragmented. This typically means that something is overflowing the node, 85 // and that establishes a parallel flow [1]. Subsequent content may be put 86 // into the same fragmentainer as a fragment whose break token is in this 87 // state, as long as it fits. 88 // 89 // [1] https://www.w3.org/TR/css-break-3/#parallel-flows 90 // 91 // <div style="columns:2; column-fill:auto; height:100px;"> 92 // <div id="a" style="height:100px;"> 93 // <div id="inner" style="height:200px;"></div> 94 // </div> 95 // <div id="b" style="margin-top:-30px; height:30px;"></div> 96 // </div> 97 // 98 // #a and #b will be in the first column, while #inner will be in both the 99 // first and second one. The important detail here is that we're at the end of 100 // #a exactly at the bottom of the first column - even if #a broke inside 101 // because of #child. This means that we have no space left as such, but we're 102 // not ready to proceed to the next column. Anything that can fit at the 103 // bottom of a column (either because it actually has 0 height, or e.g. a 104 // negative top margin) will be put into that column, not the next. IsAtBlockEnd()105 bool IsAtBlockEnd() const { return is_at_block_end_; } 106 107 // The break tokens for children of the layout node. 108 // 109 // Each child we have visited previously in the block-flow layout algorithm 110 // has an associated break token. This may be either finished (we should skip 111 // this child) or unfinished (we should try and produce the next fragment for 112 // this child). 113 // 114 // A child which we haven't visited yet doesn't have a break token here. ChildBreakTokens()115 const base::span<const NGBreakToken* const> ChildBreakTokens() const { 116 return base::make_span(child_break_tokens_, num_children_); 117 } 118 119 // Find the child NGInlineBreakToken for the specified node. 120 const NGInlineBreakToken* InlineBreakTokenFor(const NGLayoutInputNode&) const; 121 const NGInlineBreakToken* InlineBreakTokenFor(const LayoutBox&) const; 122 123 #if DCHECK_IS_ON() 124 String ToString() const override; 125 #endif 126 127 using PassKey = util::PassKey<NGBlockBreakToken>; 128 129 // Must only be called from Create(), because it assumes that enough space 130 // has been allocated in the flexible array to store the children. 131 NGBlockBreakToken(PassKey, const NGBoxFragmentBuilder&); 132 133 explicit NGBlockBreakToken(PassKey, NGLayoutInputNode node); 134 135 private: 136 LayoutUnit consumed_block_size_; 137 unsigned sequence_number_ = 0; 138 139 wtf_size_t num_children_; 140 // This must be the last member, because it is a flexible array. 141 const NGBreakToken* child_break_tokens_[]; 142 }; 143 144 template <> 145 struct DowncastTraits<NGBlockBreakToken> { 146 static bool AllowFrom(const NGBreakToken& token) { 147 return token.IsBlockType(); 148 } 149 }; 150 151 } // namespace blink 152 153 #endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_BLOCK_BREAK_TOKEN_H_ 154