summaryrefslogtreecommitdiff
path: root/chromium/third_party/blink/renderer/core/layout/ng/ng_block_break_token.h
blob: 6b7e39055a5031d7109225dcd6158a889c85bb29 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
// 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.

#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_BLOCK_BREAK_TOKEN_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_BLOCK_BREAK_TOKEN_H_

#include "base/dcheck_is_on.h"
#include "base/memory/scoped_refptr.h"
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/layout/ng/ng_break_token.h"
#include "third_party/blink/renderer/platform/geometry/layout_unit.h"
#include "third_party/blink/renderer/platform/wtf/casting.h"
#include "third_party/blink/renderer/platform/wtf/vector.h"

namespace blink {

class NGBoxFragmentBuilder;
class NGInlineBreakToken;

// Represents a break token for a block node.
class CORE_EXPORT NGBlockBreakToken final : public NGBreakToken {
 public:
  // Creates a break token for a node which did fragment, and can potentially
  // produce more fragments.
  //
  // The node is NGBlockNode, or any other NGLayoutInputNode that produces
  // anonymous box.
  static scoped_refptr<NGBlockBreakToken> Create(const NGBoxFragmentBuilder&);

  // Creates a break token for a node that needs to produce its first fragment
  // in the next fragmentainer. In this case we create a break token for a node
  // that hasn't yet produced any fragments.
  static scoped_refptr<NGBlockBreakToken> CreateBreakBefore(
      NGLayoutInputNode node,
      bool is_forced_break) {
    auto* token = new NGBlockBreakToken(PassKey(), node);
    token->is_break_before_ = true;
    token->is_forced_break_ = is_forced_break;
    token->has_unpositioned_list_marker_ = node.IsListItem();
    return base::AdoptRef(token);
  }

  ~NGBlockBreakToken() override {
    for (const NGBreakToken* token : ChildBreakTokens())
      token->Release();
  }

  // Represents the amount of block-size consumed by previous fragments.
  //
  // E.g. if the node specifies a block-size of 200px, and the previous
  // fragments generated for this box consumed 150px in total (which is what
  // this method would return then), there's 50px left to consume. The next
  // fragment will become 50px tall, assuming no additional fragmentation (if
  // the fragmentainer is shorter than 50px, for instance).
  LayoutUnit ConsumedBlockSize() const { return consumed_block_size_; }

  // The consumed block size when writing back to legacy layout. The only time
  // this may be different than ConsumedBlockSize() is in the case of a
  // fragmentainer. We clamp the fragmentainer block size from 0 to 1 for legacy
  // write-back only in the case where there is content that overflows the
  // zero-height fragmentainer. This can result in a different consumed block
  // size when used for legacy. This difference is represented by
  // |consumed_block_size_legacy_adjustment_|.
  LayoutUnit ConsumedBlockSizeForLegacy() const {
    return consumed_block_size_ + consumed_block_size_legacy_adjustment_;
  }

  // A unique identifier for a fragment that generates a break token. This is
  // unique within the generating layout input node. The break token of the
  // first fragment gets 0, then second 1, and so on. Note that we don't "count"
  // break tokens that aren't associated with a fragment (this happens when we
  // want a fragmentainer break before laying out the node). What the sequence
  // number is for such a break token is undefined.
  unsigned SequenceNumber() const {
    DCHECK(!IsBreakBefore());
    return sequence_number_;
  }

  // Return true if this is a break token that was produced without any
  // "preceding" fragment. This happens when we determine that the first
  // fragment for a node needs to be created in a later fragmentainer than the
  // one it was it was first encountered, due to block space shortage.
  bool IsBreakBefore() const { return is_break_before_; }

  bool IsForcedBreak() const { return is_forced_break_; }

  bool IsCausedByColumnSpanner() const { return is_caused_by_column_spanner_; }

  // Return true if all children have been "seen". When we have reached this
  // point, and resume layout in a fragmentainer, we should only process child
  // break tokens, if any, and not attempt to start laying out nodes that don't
  // have one (since all children are either finished, or have a break token).
  bool HasSeenAllChildren() const { return has_seen_all_children_; }

  // Return true if layout was past the block-end border edge of the node when
  // it fragmented. This typically means that something is overflowing the node,
  // and that establishes a parallel flow [1]. Subsequent content may be put
  // into the same fragmentainer as a fragment whose break token is in this
  // state, as long as it fits.
  //
  // [1] https://www.w3.org/TR/css-break-3/#parallel-flows
  //
  // <div style="columns:2; column-fill:auto; height:100px;">
  //   <div id="a" style="height:100px;">
  //     <div id="inner" style="height:200px;"></div>
  //   </div>
  //   <div id="b" style="margin-top:-30px; height:30px;"></div>
  // </div>
  //
  // #a and #b will be in the first column, while #inner will be in both the
  // first and second one. The important detail here is that we're at the end of
  // #a exactly at the bottom of the first column - even if #a broke inside
  // because of #child. This means that we have no space left as such, but we're
  // not ready to proceed to the next column. Anything that can fit at the
  // bottom of a column (either because it actually has 0 height, or e.g. a
  // negative top margin) will be put into that column, not the next.
  bool IsAtBlockEnd() const { return is_at_block_end_; }

  // True if earlier fragments could not position the list marker.
  bool HasUnpositionedListMarker() const {
    return has_unpositioned_list_marker_;
  }

  // The break tokens for children of the layout node.
  //
  // Each child we have visited previously in the block-flow layout algorithm
  // has an associated break token. This may be either finished (we should skip
  // this child) or unfinished (we should try and produce the next fragment for
  // this child).
  //
  // A child which we haven't visited yet doesn't have a break token here.
  const base::span<const NGBreakToken* const> ChildBreakTokens() const {
    return base::make_span(child_break_tokens_, num_children_);
  }

  // Find the child NGInlineBreakToken for the specified node.
  const NGInlineBreakToken* InlineBreakTokenFor(const NGLayoutInputNode&) const;
  const NGInlineBreakToken* InlineBreakTokenFor(const LayoutBox&) const;

#if DCHECK_IS_ON()
  String ToString() const override;
#endif

  using PassKey = base::PassKey<NGBlockBreakToken>;

  // Must only be called from Create(), because it assumes that enough space
  // has been allocated in the flexible array to store the children.
  NGBlockBreakToken(PassKey, const NGBoxFragmentBuilder&);

  explicit NGBlockBreakToken(PassKey, NGLayoutInputNode node);

  // This exposes a mutable part of the break token for |NGOutOfFlowLayoutPart|.
  class MutableForOutOfFlow final {
    STACK_ALLOCATED();

   protected:
    friend class NGOutOfFlowLayoutPart;
    // Replace the child break token at the provided |index|.
    void ReplaceChildBreakToken(const NGBreakToken* child_break_token,
                                wtf_size_t index) {
      DCHECK_LT(index, break_token_->num_children_);
      break_token_->child_break_tokens_[index]->Release();
      break_token_->child_break_tokens_[index] = child_break_token;
      break_token_->child_break_tokens_[index]->AddRef();
    }

   private:
    friend class NGBlockBreakToken;
    explicit MutableForOutOfFlow(const NGBlockBreakToken* break_token)
        : break_token_(const_cast<NGBlockBreakToken*>(break_token)) {}

    NGBlockBreakToken* break_token_;
  };

  MutableForOutOfFlow GetMutableForOutOfFlow() const {
    return MutableForOutOfFlow(this);
  }

 private:
  LayoutUnit consumed_block_size_;
  LayoutUnit consumed_block_size_legacy_adjustment_;
  unsigned sequence_number_ = 0;

  wtf_size_t num_children_;
  // This must be the last member, because it is a flexible array.
  const NGBreakToken* child_break_tokens_[];
};

template <>
struct DowncastTraits<NGBlockBreakToken> {
  static bool AllowFrom(const NGBreakToken& token) {
    return token.IsBlockType();
  }
};

}  // namespace blink

#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_BLOCK_BREAK_TOKEN_H_