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