blob: c7f1563350a7fd24f87600a42bd9b30fc2c9bbfd [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_LAYOUT_RESULT_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_LAYOUT_RESULT_H_
#include "base/memory/scoped_refptr.h"
#include "third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.h"
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space.h"
#include "third_party/blink/renderer/core/layout/ng/geometry/ng_bfc_offset.h"
#include "third_party/blink/renderer/core/layout/ng/geometry/ng_margin_strut.h"
#include "third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.h"
#include "third_party/blink/renderer/core/layout/ng/ng_block_node.h"
#include "third_party/blink/renderer/core/layout/ng/ng_break_appeal.h"
#include "third_party/blink/renderer/core/layout/ng/ng_early_break.h"
#include "third_party/blink/renderer/core/layout/ng/ng_floats_utils.h"
#include "third_party/blink/renderer/core/layout/ng/ng_fragment.h"
#include "third_party/blink/renderer/core/layout/ng/ng_link.h"
#include "third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.h"
#include "third_party/blink/renderer/core/style/computed_style_constants.h"
#include "third_party/blink/renderer/platform/wtf/vector.h"
namespace blink {
class NGBoxFragmentBuilder;
class NGContainerFragmentBuilder;
class NGExclusionSpace;
class NGLineBoxFragmentBuilder;
// The NGLayoutResult stores the resulting data from layout. This includes
// geometry information in form of a NGPhysicalFragment, which is kept around
// for painting, hit testing, etc., as well as additional data which is only
// necessary during layout and stored on this object.
// Layout code should access the NGPhysicalFragment through the wrappers in
// NGFragment et al.
class CORE_EXPORT NGLayoutResult : public RefCounted<NGLayoutResult> {
public:
enum EStatus {
kSuccess = 0,
kBfcBlockOffsetResolved = 1,
kNeedsEarlierBreak = 2,
kOutOfFragmentainerSpace = 3,
kNeedsRelayoutWithNoForcedTruncateAtLineClamp = 4,
// When adding new values, make sure the bit size of |Bitfields::status| is
// large enough to store.
};
// Creates a copy of |other| but uses the "post-layout" fragments to ensure
// fragment-tree consistency.
static scoped_refptr<const NGLayoutResult> CloneWithPostLayoutFragments(
const NGLayoutResult& other,
const base::Optional<PhysicalRect> updated_layout_overflow =
base::nullopt);
// Create a copy of NGLayoutResult with |BfcBlockOffset| replaced by the given
// parameter. Note, when |bfc_block_offset| is |nullopt|, |BfcBlockOffset| is
// still replaced with |nullopt|.
NGLayoutResult(const NGLayoutResult& other,
const NGConstraintSpace& new_space,
const NGMarginStrut& new_end_margin_strut,
LayoutUnit bfc_line_offset,
base::Optional<LayoutUnit> bfc_block_offset,
LayoutUnit block_offset_delta);
~NGLayoutResult();
const NGPhysicalContainerFragment& PhysicalFragment() const {
DCHECK(physical_fragment_);
DCHECK_EQ(kSuccess, Status());
return *physical_fragment_;
}
int LinesUntilClamp() const {
return HasRareData() ? rare_data_->lines_until_clamp : 0;
}
// How much an annotation box overflow from this box.
// This is for LayoutNGRubyRun and line boxes.
// 0 : No overflow
// -N : Overflowing by N px at block-start side
// This happens only for LayoutRubyRun.
// N : Overflowing by N px at block-end side
LayoutUnit AnnotationOverflow() const {
return HasRareData() ? rare_data_->annotation_overflow : LayoutUnit();
}
// The amount of available space for block-start side annotations of the
// next box.
// This never be negative.
LayoutUnit BlockEndAnnotationSpace() const {
return HasRareData() ? rare_data_->block_end_annotation_space
: LayoutUnit();
}
LogicalOffset OutOfFlowPositionedOffset() const {
DCHECK(bitfields_.has_oof_positioned_offset);
return HasRareData() ? rare_data_->oof_positioned_offset
: oof_positioned_offset_;
}
// Returns if we can use the first-tier OOF-positioned cache.
bool CanUseOutOfFlowPositionedFirstTierCache() const {
DCHECK(physical_fragment_->IsOutOfFlowPositioned());
return bitfields_.can_use_out_of_flow_positioned_first_tier_cache;
}
const NGUnpositionedListMarker UnpositionedListMarker() const {
return HasRareData() ? rare_data_->unpositioned_list_marker
: NGUnpositionedListMarker();
}
// Get the column spanner (if any) that interrupted column layout.
NGBlockNode ColumnSpanner() const {
return HasRareData() ? rare_data_->column_spanner : NGBlockNode(nullptr);
}
scoped_refptr<const NGEarlyBreak> GetEarlyBreak() const {
if (!HasRareData())
return nullptr;
return rare_data_->early_break;
}
// Return the appeal of the best breakpoint (if any) we found inside the node.
NGBreakAppeal EarlyBreakAppeal() const {
if (HasRareData())
return static_cast<NGBreakAppeal>(rare_data_->early_break_appeal);
return kBreakAppealLastResort;
}
const NGExclusionSpace& ExclusionSpace() const {
if (bitfields_.has_rare_data_exclusion_space) {
DCHECK(HasRareData());
return rare_data_->exclusion_space;
}
return space_.ExclusionSpace();
}
EStatus Status() const { return static_cast<EStatus>(bitfields_.status); }
LayoutUnit BfcLineOffset() const {
if (HasRareData())
return rare_data_->bfc_line_offset;
if (bitfields_.has_oof_positioned_offset) {
DCHECK(physical_fragment_->IsOutOfFlowPositioned());
return LayoutUnit();
}
return bfc_offset_.line_offset;
}
const base::Optional<LayoutUnit> BfcBlockOffset() const {
if (HasRareData())
return rare_data_->bfc_block_offset;
if (bitfields_.has_oof_positioned_offset) {
DCHECK(physical_fragment_->IsOutOfFlowPositioned());
return LayoutUnit();
}
if (bitfields_.is_bfc_block_offset_nullopt)
return base::nullopt;
return bfc_offset_.block_offset;
}
const NGMarginStrut EndMarginStrut() const {
return HasRareData() ? rare_data_->end_margin_strut : NGMarginStrut();
}
// Get the intrinsic block-size of the fragment (i.e. the block-size the
// fragment would get if no block-size constraints were applied). This is not
// supported (and should not be needed [1]) if the node got split into
// multiple fragments.
//
// [1] If a node gets block-fragmented, it means that it has possibly been
// constrained and/or stretched by something extrinsic (i.e. the
// fragmentainer), so the value returned here wouldn't be useful.
const LayoutUnit IntrinsicBlockSize() const {
#if DCHECK_IS_ON()
AssertSoleBoxFragment();
#endif
return intrinsic_block_size_;
}
LayoutUnit OverflowBlockSize() const {
return HasRareData() && rare_data_->overflow_block_size != kIndefiniteSize
? rare_data_->overflow_block_size
: intrinsic_block_size_;
}
LayoutUnit MinimalSpaceShortage() const {
if (!HasRareData() || rare_data_->minimal_space_shortage == kIndefiniteSize)
return LayoutUnit::Max();
return rare_data_->minimal_space_shortage;
}
LayoutUnit TallestUnbreakableBlockSize() const {
if (!HasRareData() ||
rare_data_->tallest_unbreakable_block_size == kIndefiniteSize)
return LayoutUnit();
return rare_data_->tallest_unbreakable_block_size;
}
// Return whether this result is single-use only (true), or if it is allowed
// to be involved in cache hits in future layout passes (false).
// For example, this happens when a block is fragmented, since we don't yet
// support caching of block-fragmented results.
bool IsSingleUse() const {
return HasRareData() && rare_data_->is_single_use;
}
SerializedScriptValue* CustomLayoutData() const {
return HasRareData() ? rare_data_->custom_layout_data.get() : nullptr;
}
wtf_size_t TableColumnCount() const {
return HasRareData() ? rare_data_->table_column_count_ : 0;
}
LayoutUnit MathItalicCorrection() const {
return HasRareData() && rare_data_->math_layout_data_
? rare_data_->math_layout_data_->italic_correction_
: LayoutUnit();
}
// The break-before value on the first child needs to be propagated to the
// container, in search of a valid class A break point.
EBreakBetween InitialBreakBefore() const {
return static_cast<EBreakBetween>(bitfields_.initial_break_before);
}
// The break-after value on the last child needs to be propagated to the
// container, in search of a valid class A break point.
EBreakBetween FinalBreakAfter() const {
return static_cast<EBreakBetween>(bitfields_.final_break_after);
}
// Return true if the fragment broke because a forced break before a child.
bool HasForcedBreak() const { return bitfields_.has_forced_break; }
// Returns true if the fragment should be considered empty for margin
// collapsing purposes (e.g. margins "collapse through").
bool IsSelfCollapsing() const { return bitfields_.is_self_collapsing; }
// Return true if this fragment got its block offset increased by the presence
// of floats.
bool IsPushedByFloats() const { return bitfields_.is_pushed_by_floats; }
// Returns the types of preceding adjoining objects.
// See |NGAdjoiningObjectTypes|.
//
// Adjoining floats should be treated differently when calculating clearance
// on a block with adjoining block-start margin (in such cases we will know
// up front that the block will need clearance, since, if it doesn't, the
// float will be pulled along with the block, and the block will fail to
// clear).
NGAdjoiningObjectTypes AdjoiningObjectTypes() const {
return bitfields_.adjoining_object_types;
}
// Returns true if the initial (pre-layout) block-size of this fragment was
// indefinite. (e.g. it has "height: auto").
bool IsInitialBlockSizeIndefinite() const {
return bitfields_.is_initial_block_size_indefinite;
}
// Returns true if there is a descendant that depends on percentage
// resolution block-size changes.
// Some layout modes (flex-items, table-cells) have more complex child
// percentage sizing behaviour (typically when their parent layout forces a
// block-size on them).
bool HasDescendantThatDependsOnPercentageBlockSize() const {
return bitfields_.has_descendant_that_depends_on_percentage_block_size;
}
// Returns true if this subtree modified the incoming margin-strut (i.e.
// appended a non-zero margin).
bool SubtreeModifiedMarginStrut() const {
return bitfields_.subtree_modified_margin_strut;
}
// Returns the space which generated this object for caching purposes.
const NGConstraintSpace& GetConstraintSpaceForCaching() const {
#if DCHECK_IS_ON()
DCHECK(has_valid_space_);
#endif
return space_;
}
// This exposes a mutable part of the layout result just for the
// |NGOutOfFlowLayoutPart|.
class MutableForOutOfFlow final {
STACK_ALLOCATED();
protected:
friend class NGOutOfFlowLayoutPart;
void SetOutOfFlowPositionedOffset(
const LogicalOffset& offset,
bool can_use_out_of_flow_positioned_first_tier_cache) {
// OOF-positioned nodes *must* always have an initial BFC-offset.
DCHECK(layout_result_->physical_fragment_->IsOutOfFlowPositioned());
DCHECK_EQ(layout_result_->BfcLineOffset(), LayoutUnit());
DCHECK_EQ(layout_result_->BfcBlockOffset().value_or(LayoutUnit()),
LayoutUnit());
layout_result_->bitfields_
.can_use_out_of_flow_positioned_first_tier_cache =
can_use_out_of_flow_positioned_first_tier_cache;
layout_result_->bitfields_.has_oof_positioned_offset = true;
if (layout_result_->HasRareData())
layout_result_->rare_data_->oof_positioned_offset = offset;
else
layout_result_->oof_positioned_offset_ = offset;
}
private:
friend class NGLayoutResult;
MutableForOutOfFlow(const NGLayoutResult* layout_result)
: layout_result_(const_cast<NGLayoutResult*>(layout_result)) {}
NGLayoutResult* layout_result_;
};
MutableForOutOfFlow GetMutableForOutOfFlow() const {
return MutableForOutOfFlow(this);
}
#if DCHECK_IS_ON()
void CheckSameForSimplifiedLayout(const NGLayoutResult&,
bool check_same_block_size = true) const;
#endif
using NGBoxFragmentBuilderPassKey = util::PassKey<NGBoxFragmentBuilder>;
// This constructor is for a non-success status.
NGLayoutResult(NGBoxFragmentBuilderPassKey, EStatus, NGBoxFragmentBuilder*);
// This constructor requires a non-null fragment and sets a success status.
NGLayoutResult(
NGBoxFragmentBuilderPassKey,
scoped_refptr<const NGPhysicalContainerFragment> physical_fragment,
NGBoxFragmentBuilder*);
using NGLineBoxFragmentBuilderPassKey =
util::PassKey<NGLineBoxFragmentBuilder>;
// This constructor requires a non-null fragment and sets a success status.
NGLayoutResult(
NGLineBoxFragmentBuilderPassKey,
scoped_refptr<const NGPhysicalContainerFragment> physical_fragment,
NGLineBoxFragmentBuilder*);
// See https://mathml-refresh.github.io/mathml-core/#box-model
struct MathData {
LayoutUnit italic_correction_;
};
private:
friend class MutableForOutOfFlow;
// Creates a copy of NGLayoutResult with a new (but "identical") fragment.
NGLayoutResult(
const NGLayoutResult& other,
scoped_refptr<const NGPhysicalContainerFragment> physical_fragment);
// We don't need the copy constructor, move constructor, copy
// assigmnment-operator, or move assignment-operator today.
// Delete these to clarify that they will not work because a |RefCounted|
// object can't be copied directly.
//
// If at some point we do need these constructors particular care will need
// to be taken with the |rare_data_| field which is manually memory managed.
NGLayoutResult(const NGLayoutResult&) = delete;
NGLayoutResult(NGLayoutResult&&) = delete;
NGLayoutResult& operator=(const NGLayoutResult& other) = delete;
NGLayoutResult& operator=(NGLayoutResult&& other) = delete;
NGLayoutResult() = delete;
// Delegate constructor that sets up what it can, based on the builder.
NGLayoutResult(
scoped_refptr<const NGPhysicalContainerFragment> physical_fragment,
NGContainerFragmentBuilder* builder);
static NGExclusionSpace MergeExclusionSpaces(
const NGLayoutResult& other,
const NGExclusionSpace& new_input_exclusion_space,
LayoutUnit bfc_line_offset,
LayoutUnit block_offset_delta);
struct RareData {
USING_FAST_MALLOC(RareData);
public:
RareData(LayoutUnit bfc_line_offset,
base::Optional<LayoutUnit> bfc_block_offset)
: bfc_line_offset(bfc_line_offset),
bfc_block_offset(bfc_block_offset) {}
LayoutUnit bfc_line_offset;
base::Optional<LayoutUnit> bfc_block_offset;
scoped_refptr<const NGEarlyBreak> early_break;
NGBreakAppeal early_break_appeal = kBreakAppealLastResort;
LogicalOffset oof_positioned_offset;
NGMarginStrut end_margin_strut;
NGUnpositionedListMarker unpositioned_list_marker;
NGBlockNode column_spanner = nullptr;
union {
// Only set in the initial column balancing layout pass, when we have no
// clue what the column block-size is going to be.
LayoutUnit tallest_unbreakable_block_size;
// Only set in subsequent column balancing passes, when we have set a
// tentative column block-size. At every column boundary we'll record
// space shortage, and store the smallest one here. If the columns
// couldn't fit all the content, and we're allowed to stretch columns
// further, we'll perform another pass with the column block-size
// increased by this amount.
LayoutUnit minimal_space_shortage = kIndefiniteSize;
};
NGExclusionSpace exclusion_space;
scoped_refptr<SerializedScriptValue> custom_layout_data;
LayoutUnit overflow_block_size = kIndefiniteSize;
LayoutUnit annotation_overflow;
LayoutUnit block_end_annotation_space;
bool is_single_use = false;
int lines_until_clamp = 0;
wtf_size_t table_column_count_ = 0;
base::Optional<MathData> math_layout_data_;
};
bool HasRareData() const { return bitfields_.has_rare_data; }
RareData* EnsureRareData();
#if DCHECK_IS_ON()
void AssertSoleBoxFragment() const;
#endif
struct Bitfields {
DISALLOW_NEW();
public:
// We define the default constructor so that the |has_rare_data| bit is
// never uninitialized (potentially allowing a dangling pointer).
Bitfields()
: Bitfields(
/* is_self_collapsing */ false,
/* is_pushed_by_floats */ false,
/* adjoining_object_types */ kAdjoiningNone,
/* has_descendant_that_depends_on_percentage_block_size */
false) {}
Bitfields(bool is_self_collapsing,
bool is_pushed_by_floats,
NGAdjoiningObjectTypes adjoining_object_types,
bool has_descendant_that_depends_on_percentage_block_size)
: has_rare_data(false),
has_rare_data_exclusion_space(false),
has_oof_positioned_offset(false),
can_use_out_of_flow_positioned_first_tier_cache(false),
is_bfc_block_offset_nullopt(false),
has_forced_break(false),
is_self_collapsing(is_self_collapsing),
is_pushed_by_floats(is_pushed_by_floats),
adjoining_object_types(static_cast<unsigned>(adjoining_object_types)),
is_initial_block_size_indefinite(false),
has_descendant_that_depends_on_percentage_block_size(
has_descendant_that_depends_on_percentage_block_size),
subtree_modified_margin_strut(false),
initial_break_before(static_cast<unsigned>(EBreakBetween::kAuto)),
final_break_after(static_cast<unsigned>(EBreakBetween::kAuto)),
status(static_cast<unsigned>(kSuccess)) {}
unsigned has_rare_data : 1;
unsigned has_rare_data_exclusion_space : 1;
unsigned has_oof_positioned_offset : 1;
unsigned can_use_out_of_flow_positioned_first_tier_cache : 1;
unsigned is_bfc_block_offset_nullopt : 1;
unsigned has_forced_break : 1;
unsigned is_self_collapsing : 1;
unsigned is_pushed_by_floats : 1;
unsigned adjoining_object_types : 3; // NGAdjoiningObjectTypes
unsigned is_initial_block_size_indefinite : 1;
unsigned has_descendant_that_depends_on_percentage_block_size : 1;
unsigned subtree_modified_margin_strut : 1;
unsigned initial_break_before : 4; // EBreakBetween
unsigned final_break_after : 4; // EBreakBetween
unsigned status : 3; // EStatus
};
// The constraint space which generated this layout result, may not be valid
// as indicated by |has_valid_space_|.
const NGConstraintSpace space_;
scoped_refptr<const NGPhysicalContainerFragment> physical_fragment_;
// To save space, we union these fields.
// - |rare_data_| is valid if the |Bitfields::has_rare_data| bit is set.
// |bfc_offset_| and |oof_positioned_offset_| are stored within the
// |RareData| object for this case.
// - |oof_positioned_offset_| is valid if the
// |Bitfields::has_oof_positioned_offset| bit is set. As the node is
// OOF-positioned the |bfc_offset_| is *always* the initial value.
// - Otherwise |bfc_offset_| is valid.
union {
NGBfcOffset bfc_offset_;
// This is the final position of an OOF-positioned object in its parent's
// writing-mode. This is set by the |NGOutOfFlowLayoutPart| while
// generating this layout result.
LogicalOffset oof_positioned_offset_;
RareData* rare_data_;
};
LayoutUnit intrinsic_block_size_;
Bitfields bitfields_;
#if DCHECK_IS_ON()
bool has_valid_space_ = false;
#endif
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_LAYOUT_RESULT_H_