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