1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h"
6
7 #include <memory>
8 #include <utility>
9
10 #include "third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space.h"
11 #include "third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.h"
12 #include "third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h"
13 #include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
14 #include "third_party/blink/renderer/core/layout/ng/ng_positioned_float.h"
15 #include "third_party/blink/renderer/platform/wtf/size_assertions.h"
16
17 namespace blink {
18
19 namespace {
20
21 struct SameSizeAsNGLayoutResult : public RefCounted<SameSizeAsNGLayoutResult> {
22 const NGConstraintSpace space;
23 void* physical_fragment;
24 union {
25 NGBfcOffset bfc_offset;
26 LogicalOffset oof_positioned_offset;
27 void* rare_data;
28 };
29 LayoutUnit intrinsic_block_size;
30 unsigned bitfields[1];
31
32 #if DCHECK_IS_ON()
33 bool has_valid_space;
34 #endif
35 };
36
37 ASSERT_SIZE(NGLayoutResult, SameSizeAsNGLayoutResult);
38
39 } // namespace
40
41 // static
42 scoped_refptr<const NGLayoutResult>
CloneWithPostLayoutFragments(const NGLayoutResult & other,const base::Optional<PhysicalRect> updated_layout_overflow)43 NGLayoutResult::CloneWithPostLayoutFragments(
44 const NGLayoutResult& other,
45 const base::Optional<PhysicalRect> updated_layout_overflow) {
46 return base::AdoptRef(new NGLayoutResult(
47 other, NGPhysicalBoxFragment::CloneWithPostLayoutFragments(
48 To<NGPhysicalBoxFragment>(other.PhysicalFragment()),
49 updated_layout_overflow)));
50 }
51
NGLayoutResult(NGBoxFragmentBuilderPassKey passkey,scoped_refptr<const NGPhysicalContainerFragment> physical_fragment,NGBoxFragmentBuilder * builder)52 NGLayoutResult::NGLayoutResult(
53 NGBoxFragmentBuilderPassKey passkey,
54 scoped_refptr<const NGPhysicalContainerFragment> physical_fragment,
55 NGBoxFragmentBuilder* builder)
56 : NGLayoutResult(std::move(physical_fragment),
57 static_cast<NGContainerFragmentBuilder*>(builder)) {
58 bitfields_.is_initial_block_size_indefinite =
59 builder->is_initial_block_size_indefinite_;
60 bitfields_.subtree_modified_margin_strut =
61 builder->subtree_modified_margin_strut_;
62 intrinsic_block_size_ = builder->intrinsic_block_size_;
63 if (builder->overflow_block_size_ != kIndefiniteSize &&
64 builder->overflow_block_size_ != intrinsic_block_size_) {
65 EnsureRareData()->overflow_block_size = builder->overflow_block_size_;
66 }
67 if (builder->custom_layout_data_) {
68 EnsureRareData()->custom_layout_data =
69 std::move(builder->custom_layout_data_);
70 }
71 if (builder->lines_until_clamp_)
72 EnsureRareData()->lines_until_clamp = *builder->lines_until_clamp_;
73 if (builder->annotation_overflow_)
74 EnsureRareData()->annotation_overflow = builder->annotation_overflow_;
75 if (builder->block_end_annotation_space_) {
76 EnsureRareData()->block_end_annotation_space =
77 builder->block_end_annotation_space_;
78 }
79
80 if (builder->has_block_fragmentation_) {
81 RareData* rare_data = EnsureRareData();
82
83 // We don't support fragment caching when block-fragmenting, so mark the
84 // result as non-reusable.
85 rare_data->is_single_use = true;
86
87 if (builder->tallest_unbreakable_block_size_ >= LayoutUnit()) {
88 rare_data->tallest_unbreakable_block_size =
89 builder->tallest_unbreakable_block_size_;
90
91 // This field shares storage with "minimal space shortage", so both
92 // cannot be set at the same time.
93 DCHECK_EQ(builder->minimal_space_shortage_, LayoutUnit::Max());
94 } else if (builder->minimal_space_shortage_ != LayoutUnit::Max()) {
95 rare_data->minimal_space_shortage = builder->minimal_space_shortage_;
96 }
97
98 if (builder->column_spanner_)
99 rare_data->column_spanner = builder->column_spanner_;
100
101 bitfields_.initial_break_before =
102 static_cast<unsigned>(builder->initial_break_before_);
103 bitfields_.final_break_after =
104 static_cast<unsigned>(builder->previous_break_after_);
105 bitfields_.has_forced_break = builder->has_forced_break_;
106 }
107 if (builder->table_column_count_)
108 EnsureRareData()->table_column_count_ = *builder->table_column_count_;
109 if (builder->math_data_.has_value())
110 EnsureRareData()->math_layout_data_ = builder->math_data_;
111 }
112
NGLayoutResult(NGLineBoxFragmentBuilderPassKey passkey,scoped_refptr<const NGPhysicalContainerFragment> physical_fragment,NGLineBoxFragmentBuilder * builder)113 NGLayoutResult::NGLayoutResult(
114 NGLineBoxFragmentBuilderPassKey passkey,
115 scoped_refptr<const NGPhysicalContainerFragment> physical_fragment,
116 NGLineBoxFragmentBuilder* builder)
117 : NGLayoutResult(std::move(physical_fragment),
118 static_cast<NGContainerFragmentBuilder*>(builder)) {}
119
NGLayoutResult(NGBoxFragmentBuilderPassKey key,EStatus status,NGBoxFragmentBuilder * builder)120 NGLayoutResult::NGLayoutResult(NGBoxFragmentBuilderPassKey key,
121 EStatus status,
122 NGBoxFragmentBuilder* builder)
123 : NGLayoutResult(/* physical_fragment */ nullptr,
124 static_cast<NGContainerFragmentBuilder*>(builder)) {
125 bitfields_.status = status;
126 if (builder->lines_until_clamp_)
127 EnsureRareData()->lines_until_clamp = *builder->lines_until_clamp_;
128 DCHECK_NE(status, kSuccess)
129 << "Use the other constructor for successful layout";
130 }
131
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)132 NGLayoutResult::NGLayoutResult(const NGLayoutResult& other,
133 const NGConstraintSpace& new_space,
134 const NGMarginStrut& new_end_margin_strut,
135 LayoutUnit bfc_line_offset,
136 base::Optional<LayoutUnit> bfc_block_offset,
137 LayoutUnit block_offset_delta)
138 : space_(new_space),
139 physical_fragment_(other.physical_fragment_),
140 intrinsic_block_size_(other.intrinsic_block_size_),
141 bitfields_(other.bitfields_) {
142 if (HasRareData()) {
143 rare_data_ = new RareData(*other.rare_data_);
144 rare_data_->bfc_line_offset = bfc_line_offset;
145 rare_data_->bfc_block_offset = bfc_block_offset;
146 } else if (!bitfields_.has_oof_positioned_offset) {
147 bfc_offset_.line_offset = bfc_line_offset;
148 bfc_offset_.block_offset = bfc_block_offset.value_or(LayoutUnit());
149 bitfields_.is_bfc_block_offset_nullopt = !bfc_block_offset.has_value();
150 } else {
151 DCHECK(physical_fragment_->IsOutOfFlowPositioned());
152 DCHECK_EQ(bfc_line_offset, LayoutUnit());
153 DCHECK(bfc_block_offset && bfc_block_offset.value() == LayoutUnit());
154 oof_positioned_offset_ = LogicalOffset();
155 }
156
157 NGExclusionSpace new_exclusion_space = MergeExclusionSpaces(
158 other, space_.ExclusionSpace(), bfc_line_offset, block_offset_delta);
159
160 if (new_exclusion_space != space_.ExclusionSpace()) {
161 bitfields_.has_rare_data_exclusion_space = true;
162 EnsureRareData()->exclusion_space = std::move(new_exclusion_space);
163 } else {
164 space_.ExclusionSpace().MoveDerivedGeometry(new_exclusion_space);
165 }
166
167 if (new_end_margin_strut != NGMarginStrut() || HasRareData())
168 EnsureRareData()->end_margin_strut = new_end_margin_strut;
169
170 #if DCHECK_IS_ON()
171 has_valid_space_ = other.has_valid_space_;
172 #endif
173 }
174
NGLayoutResult(const NGLayoutResult & other,scoped_refptr<const NGPhysicalContainerFragment> physical_fragment)175 NGLayoutResult::NGLayoutResult(
176 const NGLayoutResult& other,
177 scoped_refptr<const NGPhysicalContainerFragment> physical_fragment)
178 : space_(other.space_),
179 physical_fragment_(std::move(physical_fragment)),
180 intrinsic_block_size_(other.intrinsic_block_size_),
181 bitfields_(other.bitfields_) {
182 if (HasRareData()) {
183 rare_data_ = new RareData(*other.rare_data_);
184 } else if (!bitfields_.has_oof_positioned_offset) {
185 bfc_offset_ = other.bfc_offset_;
186 } else {
187 DCHECK(physical_fragment_->IsOutOfFlowPositioned());
188 oof_positioned_offset_ = other.oof_positioned_offset_;
189 }
190
191 DCHECK_EQ(physical_fragment_->Size(), other.physical_fragment_->Size());
192
193 #if DCHECK_IS_ON()
194 has_valid_space_ = other.has_valid_space_;
195 #endif
196 }
197
NGLayoutResult(scoped_refptr<const NGPhysicalContainerFragment> physical_fragment,NGContainerFragmentBuilder * builder)198 NGLayoutResult::NGLayoutResult(
199 scoped_refptr<const NGPhysicalContainerFragment> physical_fragment,
200 NGContainerFragmentBuilder* builder)
201 : space_(builder->space_ ? NGConstraintSpace(*builder->space_)
202 : NGConstraintSpace()),
203 physical_fragment_(std::move(physical_fragment)),
204 bitfields_(
205 /* is_self_collapsing */ builder->is_self_collapsing_,
206 /* is_pushed_by_floats */ builder->is_pushed_by_floats_,
207 /* adjoining_object_types */ builder->adjoining_object_types_,
208 /* has_descendant_that_depends_on_percentage_block_size */
209 builder->has_descendant_that_depends_on_percentage_block_size_) {
210 #if DCHECK_IS_ON()
211 if (bitfields_.is_self_collapsing && physical_fragment_) {
212 // A new formatting-context shouldn't be self-collapsing.
213 DCHECK(!physical_fragment_->IsFormattingContextRoot());
214
215 // Self-collapsing children must have a block-size of zero.
216 NGFragment fragment(physical_fragment_->Style().GetWritingDirection(),
217 *physical_fragment_);
218 DCHECK_EQ(LayoutUnit(), fragment.BlockSize());
219 }
220 #endif
221
222 if (builder->end_margin_strut_ != NGMarginStrut())
223 EnsureRareData()->end_margin_strut = builder->end_margin_strut_;
224 if (builder->annotation_overflow_ > LayoutUnit())
225 EnsureRareData()->annotation_overflow = builder->annotation_overflow_;
226 if (builder->block_end_annotation_space_) {
227 EnsureRareData()->block_end_annotation_space =
228 builder->block_end_annotation_space_;
229 }
230 if (builder->unpositioned_list_marker_) {
231 EnsureRareData()->unpositioned_list_marker =
232 builder->unpositioned_list_marker_;
233 }
234 if (builder->exclusion_space_ != space_.ExclusionSpace()) {
235 bitfields_.has_rare_data_exclusion_space = true;
236 EnsureRareData()->exclusion_space = std::move(builder->exclusion_space_);
237 } else {
238 space_.ExclusionSpace().MoveDerivedGeometry(builder->exclusion_space_);
239 }
240
241 // If we produced a fragment that we didn't break inside, provide the best
242 // early possible breakpoint that we found inside. This early breakpoint will
243 // be propagated to the container for further consideration. If we didn't
244 // produce a fragment, on the other hand, it means that we're going to
245 // re-layout now, and break at the early breakpoint (i.e. the status is
246 // kNeedsEarlierBreak).
247 if (builder->early_break_ &&
248 (!physical_fragment_ || !physical_fragment_->BreakToken())) {
249 auto* rare_data = EnsureRareData();
250 rare_data->early_break = builder->early_break_;
251 rare_data->early_break_appeal = builder->break_appeal_;
252 }
253
254 if (HasRareData()) {
255 rare_data_->bfc_line_offset = builder->bfc_line_offset_;
256 rare_data_->bfc_block_offset = builder->bfc_block_offset_;
257 } else {
258 bfc_offset_.line_offset = builder->bfc_line_offset_;
259 bfc_offset_.block_offset =
260 builder->bfc_block_offset_.value_or(LayoutUnit());
261 bitfields_.is_bfc_block_offset_nullopt =
262 !builder->bfc_block_offset_.has_value();
263 }
264
265 #if DCHECK_IS_ON()
266 has_valid_space_ = builder->space_;
267 #endif
268 }
269
~NGLayoutResult()270 NGLayoutResult::~NGLayoutResult() {
271 if (HasRareData())
272 delete rare_data_;
273 }
274
MergeExclusionSpaces(const NGLayoutResult & other,const NGExclusionSpace & new_input_exclusion_space,LayoutUnit bfc_line_offset,LayoutUnit block_offset_delta)275 NGExclusionSpace NGLayoutResult::MergeExclusionSpaces(
276 const NGLayoutResult& other,
277 const NGExclusionSpace& new_input_exclusion_space,
278 LayoutUnit bfc_line_offset,
279 LayoutUnit block_offset_delta) {
280 NGBfcDelta offset_delta = {bfc_line_offset - other.BfcLineOffset(),
281 block_offset_delta};
282
283 return NGExclusionSpace::MergeExclusionSpaces(
284 /* old_output */ other.ExclusionSpace(),
285 /* old_input */ other.space_.ExclusionSpace(),
286 /* new_input */ new_input_exclusion_space, offset_delta);
287 }
288
EnsureRareData()289 NGLayoutResult::RareData* NGLayoutResult::EnsureRareData() {
290 if (!HasRareData()) {
291 base::Optional<LayoutUnit> bfc_block_offset;
292 if (!bitfields_.is_bfc_block_offset_nullopt)
293 bfc_block_offset = bfc_offset_.block_offset;
294 rare_data_ = new RareData(bfc_offset_.line_offset, bfc_block_offset);
295 bitfields_.has_rare_data = true;
296 }
297
298 return rare_data_;
299 }
300
301 #if DCHECK_IS_ON()
CheckSameForSimplifiedLayout(const NGLayoutResult & other,bool check_same_block_size) const302 void NGLayoutResult::CheckSameForSimplifiedLayout(
303 const NGLayoutResult& other,
304 bool check_same_block_size) const {
305 To<NGPhysicalBoxFragment>(*physical_fragment_)
306 .CheckSameForSimplifiedLayout(
307 To<NGPhysicalBoxFragment>(*other.physical_fragment_),
308 check_same_block_size);
309
310 DCHECK(LinesUntilClamp() == other.LinesUntilClamp());
311 DCHECK(UnpositionedListMarker() == other.UnpositionedListMarker());
312 ExclusionSpace().CheckSameForSimplifiedLayout(other.ExclusionSpace());
313
314 // We ignore |BfcBlockOffset|, and |BfcLineOffset| as "simplified" layout
315 // will move the layout result if required.
316
317 // We ignore the |intrinsic_block_size_| as if a scrollbar gets added/removed
318 // this may change (even if the size of the fragment remains the same).
319
320 DCHECK(EndMarginStrut() == other.EndMarginStrut());
321 DCHECK_EQ(MinimalSpaceShortage(), other.MinimalSpaceShortage());
322
323 DCHECK_EQ(bitfields_.has_forced_break, other.bitfields_.has_forced_break);
324 DCHECK_EQ(bitfields_.is_self_collapsing, other.bitfields_.is_self_collapsing);
325 DCHECK_EQ(bitfields_.is_pushed_by_floats,
326 other.bitfields_.is_pushed_by_floats);
327 DCHECK_EQ(bitfields_.adjoining_object_types,
328 other.bitfields_.adjoining_object_types);
329
330 DCHECK_EQ(bitfields_.subtree_modified_margin_strut,
331 other.bitfields_.subtree_modified_margin_strut);
332
333 DCHECK_EQ(CustomLayoutData(), other.CustomLayoutData());
334
335 DCHECK_EQ(bitfields_.initial_break_before,
336 other.bitfields_.initial_break_before);
337 DCHECK_EQ(bitfields_.final_break_after, other.bitfields_.final_break_after);
338
339 DCHECK_EQ(
340 bitfields_.has_descendant_that_depends_on_percentage_block_size,
341 other.bitfields_.has_descendant_that_depends_on_percentage_block_size);
342 DCHECK_EQ(bitfields_.status, other.bitfields_.status);
343 }
344 #endif
345
346 #if DCHECK_IS_ON()
AssertSoleBoxFragment() const347 void NGLayoutResult::AssertSoleBoxFragment() const {
348 DCHECK(physical_fragment_->IsBox());
349 DCHECK(To<NGPhysicalBoxFragment>(PhysicalFragment()).IsFirstForNode());
350 DCHECK(!physical_fragment_->BreakToken());
351 }
352 #endif
353
354 } // namespace blink
355