blob: fce19442662c7a803c221da4281fa45ecedce157 [file] [log] [blame]
// Copyright 2016 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.
#include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h"
#include <memory>
#include <utility>
#include "third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.h"
#include "third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h"
#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
#include "third_party/blink/renderer/core/layout/ng/ng_positioned_float.h"
#include "third_party/blink/renderer/platform/wtf/size_assertions.h"
namespace blink {
namespace {
struct SameSizeAsNGLayoutResult : public RefCounted<SameSizeAsNGLayoutResult> {
const NGConstraintSpace space;
void* physical_fragment;
union {
NGBfcOffset bfc_offset;
LogicalOffset oof_positioned_offset;
void* rare_data;
};
LayoutUnit intrinsic_block_size;
unsigned bitfields[1];
#if DCHECK_IS_ON()
bool has_valid_space;
#endif
};
ASSERT_SIZE(NGLayoutResult, SameSizeAsNGLayoutResult);
} // namespace
// static
scoped_refptr<const NGLayoutResult>
NGLayoutResult::CloneWithPostLayoutFragments(
const NGLayoutResult& other,
const base::Optional<PhysicalRect> updated_layout_overflow) {
return base::AdoptRef(new NGLayoutResult(
other, NGPhysicalBoxFragment::CloneWithPostLayoutFragments(
To<NGPhysicalBoxFragment>(other.PhysicalFragment()),
updated_layout_overflow)));
}
NGLayoutResult::NGLayoutResult(
NGBoxFragmentBuilderPassKey passkey,
scoped_refptr<const NGPhysicalContainerFragment> physical_fragment,
NGBoxFragmentBuilder* builder)
: NGLayoutResult(std::move(physical_fragment),
static_cast<NGContainerFragmentBuilder*>(builder)) {
bitfields_.is_initial_block_size_indefinite =
builder->is_initial_block_size_indefinite_;
bitfields_.subtree_modified_margin_strut =
builder->subtree_modified_margin_strut_;
intrinsic_block_size_ = builder->intrinsic_block_size_;
if (builder->overflow_block_size_ != kIndefiniteSize &&
builder->overflow_block_size_ != intrinsic_block_size_) {
EnsureRareData()->overflow_block_size = builder->overflow_block_size_;
}
if (builder->custom_layout_data_) {
EnsureRareData()->custom_layout_data =
std::move(builder->custom_layout_data_);
}
if (builder->lines_until_clamp_)
EnsureRareData()->lines_until_clamp = *builder->lines_until_clamp_;
if (builder->annotation_overflow_)
EnsureRareData()->annotation_overflow = builder->annotation_overflow_;
if (builder->block_end_annotation_space_) {
EnsureRareData()->block_end_annotation_space =
builder->block_end_annotation_space_;
}
if (builder->has_block_fragmentation_) {
RareData* rare_data = EnsureRareData();
// We don't support fragment caching when block-fragmenting, so mark the
// result as non-reusable.
rare_data->is_single_use = true;
if (builder->tallest_unbreakable_block_size_ >= LayoutUnit()) {
rare_data->tallest_unbreakable_block_size =
builder->tallest_unbreakable_block_size_;
// This field shares storage with "minimal space shortage", so both
// cannot be set at the same time.
DCHECK_EQ(builder->minimal_space_shortage_, LayoutUnit::Max());
} else if (builder->minimal_space_shortage_ != LayoutUnit::Max()) {
rare_data->minimal_space_shortage = builder->minimal_space_shortage_;
}
rare_data->has_violating_break = builder->has_violating_break_;
if (builder->column_spanner_)
rare_data->column_spanner = builder->column_spanner_;
bitfields_.initial_break_before =
static_cast<unsigned>(builder->initial_break_before_);
bitfields_.final_break_after =
static_cast<unsigned>(builder->previous_break_after_);
bitfields_.has_forced_break = builder->has_forced_break_;
}
if (builder->table_column_count_)
EnsureRareData()->table_column_count_ = *builder->table_column_count_;
if (builder->math_data_.has_value())
EnsureRareData()->math_layout_data_ = builder->math_data_;
}
NGLayoutResult::NGLayoutResult(
NGLineBoxFragmentBuilderPassKey passkey,
scoped_refptr<const NGPhysicalContainerFragment> physical_fragment,
NGLineBoxFragmentBuilder* builder)
: NGLayoutResult(std::move(physical_fragment),
static_cast<NGContainerFragmentBuilder*>(builder)) {}
NGLayoutResult::NGLayoutResult(NGBoxFragmentBuilderPassKey key,
EStatus status,
NGBoxFragmentBuilder* builder)
: NGLayoutResult(/* physical_fragment */ nullptr,
static_cast<NGContainerFragmentBuilder*>(builder)) {
bitfields_.status = status;
if (builder->lines_until_clamp_)
EnsureRareData()->lines_until_clamp = *builder->lines_until_clamp_;
DCHECK_NE(status, kSuccess)
<< "Use the other constructor for successful layout";
}
NGLayoutResult::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)
: space_(new_space),
physical_fragment_(other.physical_fragment_),
intrinsic_block_size_(other.intrinsic_block_size_),
bitfields_(other.bitfields_) {
if (HasRareData()) {
rare_data_ = new RareData(*other.rare_data_);
rare_data_->bfc_line_offset = bfc_line_offset;
rare_data_->bfc_block_offset = bfc_block_offset;
} else if (!bitfields_.has_oof_positioned_offset) {
bfc_offset_.line_offset = bfc_line_offset;
bfc_offset_.block_offset = bfc_block_offset.value_or(LayoutUnit());
bitfields_.is_bfc_block_offset_nullopt = !bfc_block_offset.has_value();
} else {
DCHECK(physical_fragment_->IsOutOfFlowPositioned());
DCHECK_EQ(bfc_line_offset, LayoutUnit());
DCHECK(bfc_block_offset && bfc_block_offset.value() == LayoutUnit());
oof_positioned_offset_ = LogicalOffset();
}
NGExclusionSpace new_exclusion_space = MergeExclusionSpaces(
other, space_.ExclusionSpace(), bfc_line_offset, block_offset_delta);
if (new_exclusion_space != space_.ExclusionSpace()) {
bitfields_.has_rare_data_exclusion_space = true;
EnsureRareData()->exclusion_space = std::move(new_exclusion_space);
} else {
space_.ExclusionSpace().MoveDerivedGeometry(new_exclusion_space);
}
if (new_end_margin_strut != NGMarginStrut() || HasRareData())
EnsureRareData()->end_margin_strut = new_end_margin_strut;
#if DCHECK_IS_ON()
has_valid_space_ = other.has_valid_space_;
#endif
}
NGLayoutResult::NGLayoutResult(
const NGLayoutResult& other,
scoped_refptr<const NGPhysicalContainerFragment> physical_fragment)
: space_(other.space_),
physical_fragment_(std::move(physical_fragment)),
intrinsic_block_size_(other.intrinsic_block_size_),
bitfields_(other.bitfields_) {
if (HasRareData()) {
rare_data_ = new RareData(*other.rare_data_);
} else if (!bitfields_.has_oof_positioned_offset) {
bfc_offset_ = other.bfc_offset_;
} else {
DCHECK(physical_fragment_->IsOutOfFlowPositioned());
oof_positioned_offset_ = other.oof_positioned_offset_;
}
DCHECK_EQ(physical_fragment_->Size(), other.physical_fragment_->Size());
#if DCHECK_IS_ON()
has_valid_space_ = other.has_valid_space_;
#endif
}
NGLayoutResult::NGLayoutResult(
scoped_refptr<const NGPhysicalContainerFragment> physical_fragment,
NGContainerFragmentBuilder* builder)
: space_(builder->space_ ? NGConstraintSpace(*builder->space_)
: NGConstraintSpace()),
physical_fragment_(std::move(physical_fragment)),
bitfields_(
/* is_self_collapsing */ builder->is_self_collapsing_,
/* is_pushed_by_floats */ builder->is_pushed_by_floats_,
/* adjoining_object_types */ builder->adjoining_object_types_,
/* has_descendant_that_depends_on_percentage_block_size */
builder->has_descendant_that_depends_on_percentage_block_size_) {
#if DCHECK_IS_ON()
if (bitfields_.is_self_collapsing && physical_fragment_) {
// A new formatting-context shouldn't be self-collapsing.
DCHECK(!physical_fragment_->IsFormattingContextRoot());
// Self-collapsing children must have a block-size of zero.
NGFragment fragment(physical_fragment_->Style().GetWritingDirection(),
*physical_fragment_);
DCHECK_EQ(LayoutUnit(), fragment.BlockSize());
}
#endif
if (builder->end_margin_strut_ != NGMarginStrut())
EnsureRareData()->end_margin_strut = builder->end_margin_strut_;
if (builder->annotation_overflow_ > LayoutUnit())
EnsureRareData()->annotation_overflow = builder->annotation_overflow_;
if (builder->block_end_annotation_space_) {
EnsureRareData()->block_end_annotation_space =
builder->block_end_annotation_space_;
}
if (builder->unpositioned_list_marker_) {
EnsureRareData()->unpositioned_list_marker =
builder->unpositioned_list_marker_;
}
if (builder->exclusion_space_ != space_.ExclusionSpace()) {
bitfields_.has_rare_data_exclusion_space = true;
EnsureRareData()->exclusion_space = std::move(builder->exclusion_space_);
} else {
space_.ExclusionSpace().MoveDerivedGeometry(builder->exclusion_space_);
}
// If we produced a fragment that we didn't break inside, provide the best
// early possible breakpoint that we found inside. This early breakpoint will
// be propagated to the container for further consideration. If we didn't
// produce a fragment, on the other hand, it means that we're going to
// re-layout now, and break at the early breakpoint (i.e. the status is
// kNeedsEarlierBreak).
if (builder->early_break_ &&
(!physical_fragment_ || !physical_fragment_->BreakToken())) {
auto* rare_data = EnsureRareData();
rare_data->early_break = builder->early_break_;
rare_data->early_break_appeal = builder->break_appeal_;
}
if (HasRareData()) {
rare_data_->bfc_line_offset = builder->bfc_line_offset_;
rare_data_->bfc_block_offset = builder->bfc_block_offset_;
} else {
bfc_offset_.line_offset = builder->bfc_line_offset_;
bfc_offset_.block_offset =
builder->bfc_block_offset_.value_or(LayoutUnit());
bitfields_.is_bfc_block_offset_nullopt =
!builder->bfc_block_offset_.has_value();
}
#if DCHECK_IS_ON()
has_valid_space_ = builder->space_;
#endif
}
NGLayoutResult::~NGLayoutResult() {
if (HasRareData())
delete rare_data_;
}
NGExclusionSpace NGLayoutResult::MergeExclusionSpaces(
const NGLayoutResult& other,
const NGExclusionSpace& new_input_exclusion_space,
LayoutUnit bfc_line_offset,
LayoutUnit block_offset_delta) {
NGBfcDelta offset_delta = {bfc_line_offset - other.BfcLineOffset(),
block_offset_delta};
return NGExclusionSpace::MergeExclusionSpaces(
/* old_output */ other.ExclusionSpace(),
/* old_input */ other.space_.ExclusionSpace(),
/* new_input */ new_input_exclusion_space, offset_delta);
}
NGLayoutResult::RareData* NGLayoutResult::EnsureRareData() {
if (!HasRareData()) {
base::Optional<LayoutUnit> bfc_block_offset;
if (!bitfields_.is_bfc_block_offset_nullopt)
bfc_block_offset = bfc_offset_.block_offset;
rare_data_ = new RareData(bfc_offset_.line_offset, bfc_block_offset);
bitfields_.has_rare_data = true;
}
return rare_data_;
}
#if DCHECK_IS_ON()
void NGLayoutResult::CheckSameForSimplifiedLayout(
const NGLayoutResult& other,
bool check_same_block_size) const {
To<NGPhysicalBoxFragment>(*physical_fragment_)
.CheckSameForSimplifiedLayout(
To<NGPhysicalBoxFragment>(*other.physical_fragment_),
check_same_block_size);
DCHECK(LinesUntilClamp() == other.LinesUntilClamp());
DCHECK(UnpositionedListMarker() == other.UnpositionedListMarker());
ExclusionSpace().CheckSameForSimplifiedLayout(other.ExclusionSpace());
// We ignore |BfcBlockOffset|, and |BfcLineOffset| as "simplified" layout
// will move the layout result if required.
// We ignore the |intrinsic_block_size_| as if a scrollbar gets added/removed
// this may change (even if the size of the fragment remains the same).
DCHECK(EndMarginStrut() == other.EndMarginStrut());
DCHECK_EQ(MinimalSpaceShortage(), other.MinimalSpaceShortage());
DCHECK_EQ(TableColumnCount(), other.TableColumnCount());
DCHECK_EQ(bitfields_.has_forced_break, other.bitfields_.has_forced_break);
DCHECK_EQ(bitfields_.is_self_collapsing, other.bitfields_.is_self_collapsing);
DCHECK_EQ(bitfields_.is_pushed_by_floats,
other.bitfields_.is_pushed_by_floats);
DCHECK_EQ(bitfields_.adjoining_object_types,
other.bitfields_.adjoining_object_types);
DCHECK_EQ(bitfields_.subtree_modified_margin_strut,
other.bitfields_.subtree_modified_margin_strut);
DCHECK_EQ(CustomLayoutData(), other.CustomLayoutData());
DCHECK_EQ(bitfields_.initial_break_before,
other.bitfields_.initial_break_before);
DCHECK_EQ(bitfields_.final_break_after, other.bitfields_.final_break_after);
DCHECK_EQ(
bitfields_.has_descendant_that_depends_on_percentage_block_size,
other.bitfields_.has_descendant_that_depends_on_percentage_block_size);
DCHECK_EQ(bitfields_.status, other.bitfields_.status);
}
#endif
#if DCHECK_IS_ON()
void NGLayoutResult::AssertSoleBoxFragment() const {
DCHECK(physical_fragment_->IsBox());
DCHECK(To<NGPhysicalBoxFragment>(PhysicalFragment()).IsFirstForNode());
DCHECK(!physical_fragment_->BreakToken());
}
#endif
} // namespace blink