blob: a5c0f9628d84a479471bc37f4ab75924dff6fbfe [file] [log] [blame]
// 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/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;
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_; }
// 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_; }
// 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_;
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_