blob: 623f813f586c8d8ed0038a66454d87778eaec0f2 [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 NGLineBoxFragmentBuilder_h
#define NGLineBoxFragmentBuilder_h
#include "third_party/blink/renderer/core/layout/ng/geometry/ng_logical_offset.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_break_token.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_line_height_metrics.h"
#include "third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h"
#include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h"
#include "third_party/blink/renderer/core/layout/ng/ng_positioned_float.h"
#include "third_party/blink/renderer/platform/wtf/allocator.h"
namespace blink {
class ComputedStyle;
class NGInlineBreakToken;
class NGPhysicalFragment;
struct NGPositionedFloat;
class CORE_EXPORT NGLineBoxFragmentBuilder final
: public NGContainerFragmentBuilder {
STACK_ALLOCATED();
public:
NGLineBoxFragmentBuilder(NGInlineNode node,
scoped_refptr<const ComputedStyle> style,
WritingMode writing_mode,
TextDirection)
: NGContainerFragmentBuilder(style, writing_mode, TextDirection::kLtr),
node_(node),
base_direction_(TextDirection::kLtr) {}
void Reset();
LayoutUnit LineHeight() const {
return metrics_.LineHeight().ClampNegativeToZero();
}
const NGLineHeightMetrics& Metrics() const { return metrics_; }
void SetMetrics(const NGLineHeightMetrics& metrics) { metrics_ = metrics; }
void SetBaseDirection(TextDirection direction) {
base_direction_ = direction;
}
void SwapPositionedFloats(Vector<NGPositionedFloat>* positioned_floats) {
positioned_floats_.swap(*positioned_floats);
}
// Set the break token for the fragment to build.
// A finished break token will be attached if not set.
void SetBreakToken(scoped_refptr<NGInlineBreakToken> break_token) {
break_token_ = std::move(break_token);
}
// A data struct to keep NGLayoutResult or fragment until the box tree
// structures and child offsets are finalized.
struct Child {
DISALLOW_NEW();
scoped_refptr<NGLayoutResult> layout_result;
scoped_refptr<const NGPhysicalFragment> fragment;
LayoutObject* out_of_flow_positioned_box = nullptr;
LayoutObject* out_of_flow_containing_box = nullptr;
// The offset of the border box, initially in this child coordinate system.
// |ComputeInlinePositions()| converts it to the offset within the line box.
NGLogicalOffset offset;
// The inline size of the margin box.
LayoutUnit inline_size;
LayoutUnit margin_line_left;
// The index of |box_data_list_|, used in |PrepareForReorder()| and
// |UpdateAfterReorder()| to track children of boxes across BiDi reorder.
unsigned box_data_index = 0;
UBiDiLevel bidi_level = 0xff;
// Empty constructor needed for |resize()|.
Child() = default;
// Create a placeholder. A placeholder does not have a fragment nor a bidi
// level.
Child(NGLogicalOffset offset) : offset(offset) {}
// Crete a bidi control. A bidi control does not have a fragment, but has
// bidi level and affects bidi reordering.
Child(UBiDiLevel bidi_level) : bidi_level(bidi_level) {}
// Create an in-flow |NGLayoutResult|.
Child(scoped_refptr<NGLayoutResult> layout_result,
NGLogicalOffset offset,
LayoutUnit inline_size,
UBiDiLevel bidi_level)
: layout_result(std::move(layout_result)),
offset(offset),
inline_size(inline_size),
bidi_level(bidi_level) {}
// Create an in-flow |NGPhysicalFragment|.
Child(scoped_refptr<const NGPhysicalFragment> fragment,
NGLogicalOffset offset,
LayoutUnit inline_size,
UBiDiLevel bidi_level)
: fragment(std::move(fragment)),
offset(offset),
inline_size(inline_size),
bidi_level(bidi_level) {}
Child(scoped_refptr<const NGPhysicalFragment> fragment,
LayoutUnit block_offset,
LayoutUnit inline_size,
UBiDiLevel bidi_level)
: fragment(std::move(fragment)),
offset({LayoutUnit(), block_offset}),
inline_size(inline_size),
bidi_level(bidi_level) {}
// Create an out-of-flow positioned object.
Child(LayoutObject* out_of_flow_positioned_box,
LayoutObject* out_of_flow_containing_box,
UBiDiLevel bidi_level)
: out_of_flow_positioned_box(out_of_flow_positioned_box),
out_of_flow_containing_box(out_of_flow_containing_box),
bidi_level(bidi_level) {}
bool HasInFlowFragment() const { return layout_result || fragment; }
bool HasOutOfFlowFragment() const { return out_of_flow_positioned_box; }
bool HasFragment() const {
return HasInFlowFragment() || HasOutOfFlowFragment();
}
bool HasBidiLevel() const { return bidi_level != 0xff; }
bool IsPlaceholder() const { return !HasFragment() && !HasBidiLevel(); }
const NGPhysicalFragment* PhysicalFragment() const {
return layout_result ? layout_result->PhysicalFragment() : fragment.get();
}
};
// A vector of Child.
// Unlike the fragment builder, chlidren are mutable.
// Callers can add to the fragment builder in a batch once finalized.
class ChildList {
STACK_ALLOCATED();
public:
ChildList() = default;
void operator=(ChildList&& other) {
children_ = std::move(other.children_);
}
Child& operator[](wtf_size_t i) { return children_[i]; }
const Child& operator[](wtf_size_t i) const { return children_[i]; }
wtf_size_t size() const { return children_.size(); }
bool IsEmpty() const { return children_.IsEmpty(); }
void ReserveInitialCapacity(unsigned capacity) {
children_.ReserveInitialCapacity(capacity);
}
void clear() { children_.resize(0); }
void resize(wtf_size_t size) { children_.resize(size); }
using iterator = Vector<Child, 16>::iterator;
iterator begin() { return children_.begin(); }
iterator end() { return children_.end(); }
using const_iterator = Vector<Child, 16>::const_iterator;
const_iterator begin() const { return children_.begin(); }
const_iterator end() const { return children_.end(); }
using reverse_iterator = Vector<Child, 16>::reverse_iterator;
reverse_iterator rbegin() { return children_.rbegin(); }
reverse_iterator rend() { return children_.rend(); }
using const_reverse_iterator = Vector<Child, 16>::const_reverse_iterator;
const_reverse_iterator rbegin() const { return children_.rbegin(); }
const_reverse_iterator rend() const { return children_.rend(); }
Child* FirstInFlowChild();
Child* LastInFlowChild();
// Add a child. Accepts all constructor arguments for |Child|.
template <class... Args>
void AddChild(Args&&... args) {
children_.emplace_back(std::forward<Args>(args)...);
}
void InsertChild(unsigned index,
scoped_refptr<NGLayoutResult> layout_result,
const NGLogicalOffset& offset,
LayoutUnit inline_size,
UBiDiLevel bidi_level) {
children_.insert(index, Child{std::move(layout_result), offset,
inline_size, bidi_level});
}
void MoveInInlineDirection(LayoutUnit, unsigned start, unsigned end);
void MoveInBlockDirection(LayoutUnit);
void MoveInBlockDirection(LayoutUnit, unsigned start, unsigned end);
private:
Vector<Child, 16> children_;
};
// Add all items in ChildList. Skips null Child if any.
void AddChildren(ChildList&);
// Creates the fragment. Can only be called once.
scoped_refptr<NGLayoutResult> ToLineBoxFragment();
private:
NGInlineNode node_;
NGLineHeightMetrics metrics_;
Vector<NGPositionedFloat> positioned_floats_;
TextDirection base_direction_;
friend class NGLayoutResult;
friend class NGPhysicalLineBoxFragment;
DISALLOW_COPY_AND_ASSIGN(NGLineBoxFragmentBuilder);
};
} // namespace blink
WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS(
blink::NGLineBoxFragmentBuilder::Child);
#endif // NGLineBoxFragmentBuilder