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_box_fragment_builder.h"
6 
7 #include "third_party/blink/renderer/core/layout/layout_object.h"
8 #include "third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space.h"
9 #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_break_token.h"
10 #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_fragment_traversal.h"
11 #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h"
12 #include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h"
13 #include "third_party/blink/renderer/core/layout/ng/ng_block_break_token.h"
14 #include "third_party/blink/renderer/core/layout/ng/ng_block_node.h"
15 #include "third_party/blink/renderer/core/layout/ng/ng_break_token.h"
16 #include "third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.h"
17 #include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h"
18 #include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h"
19 #include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
20 #include "third_party/blink/renderer/core/layout/ng/ng_positioned_float.h"
21 #include "third_party/blink/renderer/core/layout/ng/ng_relative_utils.h"
22 #include "third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h"
23 
24 namespace blink {
25 
26 namespace {
27 
28 // std::pair.first points to the start linebox fragment.
29 // std::pair.second points to the end linebox fragment.
30 using LineBoxPair = std::pair<const NGPhysicalLineBoxFragment*,
31                               const NGPhysicalLineBoxFragment*>;
32 
GatherInlineContainerFragmentsFromLinebox(NGBoxFragmentBuilder::InlineContainingBlockMap * inline_containing_block_map,HashMap<const LayoutObject *,LineBoxPair> * containing_linebox_map,const NGPhysicalLineBoxFragment & linebox,const PhysicalOffset linebox_offset)33 void GatherInlineContainerFragmentsFromLinebox(
34     NGBoxFragmentBuilder::InlineContainingBlockMap* inline_containing_block_map,
35     HashMap<const LayoutObject*, LineBoxPair>* containing_linebox_map,
36     const NGPhysicalLineBoxFragment& linebox,
37     const PhysicalOffset linebox_offset) {
38   for (const auto& descendant :
39        NGInlineFragmentTraversal::DescendantsOf(linebox)) {
40     if (!descendant.fragment->IsBox())
41       continue;
42     const LayoutObject* key = descendant.fragment->GetLayoutObject();
43     // Key for inline is the continuation root if it exists.
44     if (key->IsLayoutInline() && key->GetNode())
45       key = key->ContinuationRoot();
46     auto it = inline_containing_block_map->find(key);
47     if (it == inline_containing_block_map->end()) {
48       // Default case, not one of the blocks we are looking for.
49       continue;
50     }
51     base::Optional<NGBoxFragmentBuilder::InlineContainingBlockGeometry>&
52         containing_block_geometry = it->value;
53     LineBoxPair& containing_lineboxes =
54         containing_linebox_map->insert(key, LineBoxPair{nullptr, nullptr})
55             .stored_value->value;
56     DCHECK(containing_block_geometry.has_value() ||
57            !containing_lineboxes.first);
58 
59     // |DescendantsOf| returns the offset from the given fragment. Since
60     // we give it the line box, need to add the |linebox_offset|.
61     PhysicalRect fragment_rect(
62         linebox_offset + descendant.offset_to_container_box,
63         descendant.fragment->Size());
64     if (containing_lineboxes.first == &linebox) {
65       containing_block_geometry->start_fragment_union_rect.Unite(fragment_rect);
66     } else if (!containing_lineboxes.first) {
67       containing_lineboxes.first = &linebox;
68       containing_block_geometry =
69           NGBoxFragmentBuilder::InlineContainingBlockGeometry{fragment_rect,
70                                                               PhysicalRect()};
71     }
72     // Skip fragments within an empty line boxes for the end fragment.
73     if (containing_lineboxes.second == &linebox) {
74       containing_block_geometry->end_fragment_union_rect.Unite(fragment_rect);
75     } else if (!containing_lineboxes.second || !linebox.IsEmptyLineBox()) {
76       containing_lineboxes.second = &linebox;
77       containing_block_geometry->end_fragment_union_rect = fragment_rect;
78     }
79   }
80 }
81 
82 template <class Items>
GatherInlineContainerFragmentsFromItems(const Items & items,const PhysicalOffset & box_offset,NGBoxFragmentBuilder::InlineContainingBlockMap * inline_containing_block_map,HashMap<const LayoutObject *,LineBoxPair> * containing_linebox_map)83 void GatherInlineContainerFragmentsFromItems(
84     const Items& items,
85     const PhysicalOffset& box_offset,
86     NGBoxFragmentBuilder::InlineContainingBlockMap* inline_containing_block_map,
87     HashMap<const LayoutObject*, LineBoxPair>* containing_linebox_map) {
88   const NGPhysicalLineBoxFragment* linebox = nullptr;
89   for (const auto& item : items) {
90     // Track the current linebox.
91     if (const NGPhysicalLineBoxFragment* current_linebox =
92             item->LineBoxFragment()) {
93       linebox = current_linebox;
94       continue;
95     }
96 
97     // We only care about inlines which have generated a box fragment.
98     const NGPhysicalBoxFragment* box = item->BoxFragment();
99     if (!box)
100       continue;
101 
102     // The key for the inline is the continuation root if it exists.
103     const LayoutObject* key = box->GetLayoutObject();
104     if (key->IsLayoutInline() && key->GetNode())
105       key = key->ContinuationRoot();
106 
107     // See if we need the containing block information for this inline.
108     auto it = inline_containing_block_map->find(key);
109     if (it == inline_containing_block_map->end())
110       continue;
111 
112     base::Optional<NGBoxFragmentBuilder::InlineContainingBlockGeometry>&
113         containing_block_geometry = it->value;
114     LineBoxPair& containing_lineboxes =
115         containing_linebox_map->insert(key, LineBoxPair{nullptr, nullptr})
116             .stored_value->value;
117     DCHECK(containing_block_geometry.has_value() ||
118            !containing_lineboxes.first);
119 
120     PhysicalRect fragment_rect = item->RectInContainerBlock();
121     fragment_rect.offset += box_offset;
122     if (containing_lineboxes.first == linebox) {
123       // Unite the start rect with the fragment's rect.
124       containing_block_geometry->start_fragment_union_rect.Unite(fragment_rect);
125     } else if (!containing_lineboxes.first) {
126       DCHECK(!containing_lineboxes.second);
127       // This is the first linebox we've encountered, initialize the containing
128       // block geometry.
129       containing_lineboxes.first = linebox;
130       containing_lineboxes.second = linebox;
131       containing_block_geometry =
132           NGBoxFragmentBuilder::InlineContainingBlockGeometry{fragment_rect,
133                                                               fragment_rect};
134     }
135 
136     if (containing_lineboxes.second == linebox) {
137       // Unite the end rect with the fragment's rect.
138       containing_block_geometry->end_fragment_union_rect.Unite(fragment_rect);
139     } else if (!linebox->IsEmptyLineBox()) {
140       // We've found a new "end" linebox,  update the containing block geometry.
141       containing_lineboxes.second = linebox;
142       containing_block_geometry->end_fragment_union_rect = fragment_rect;
143     }
144   }
145 }
146 
147 }  // namespace
148 
AddBreakBeforeChild(NGLayoutInputNode child,base::Optional<NGBreakAppeal> appeal,bool is_forced_break)149 void NGBoxFragmentBuilder::AddBreakBeforeChild(
150     NGLayoutInputNode child,
151     base::Optional<NGBreakAppeal> appeal,
152     bool is_forced_break) {
153   if (appeal)
154     break_appeal_ = *appeal;
155   if (is_forced_break) {
156     SetHasForcedBreak();
157     // A forced break is considered to always have perfect appeal; they should
158     // never be weighed against other potential breakpoints.
159     DCHECK(!appeal || *appeal == kBreakAppealPerfect);
160   }
161 
162   DCHECK(has_block_fragmentation_);
163 
164   if (!has_inflow_child_break_inside_)
165     has_inflow_child_break_inside_ = !child.IsFloatingOrOutOfFlowPositioned();
166   if (!has_float_break_inside_)
167     has_float_break_inside_ = child.IsFloating();
168 
169   if (auto* child_inline_node = DynamicTo<NGInlineNode>(child)) {
170     if (inline_break_tokens_.IsEmpty()) {
171       // In some cases we may want to break before the first line, as a last
172       // resort. We need a break token for that as well, so that the machinery
173       // will understand that we should resume at the beginning of the inline
174       // formatting context, rather than concluding that we're done with the
175       // whole thing.
176       inline_break_tokens_.push_back(NGInlineBreakToken::Create(
177           *child_inline_node, /* style */ nullptr, /* item_index */ 0,
178           /* text_offset */ 0, NGInlineBreakToken::kDefault));
179     }
180     return;
181   }
182   auto token = NGBlockBreakToken::CreateBreakBefore(child, is_forced_break);
183   child_break_tokens_.push_back(token);
184 }
185 
AddResult(const NGLayoutResult & child_layout_result,const LogicalOffset offset)186 void NGBoxFragmentBuilder::AddResult(const NGLayoutResult& child_layout_result,
187                                      const LogicalOffset offset) {
188   const auto& fragment = child_layout_result.PhysicalFragment();
189   if (items_builder_) {
190     if (const NGPhysicalLineBoxFragment* line =
191             DynamicTo<NGPhysicalLineBoxFragment>(&fragment)) {
192       items_builder_->AddLine(*line, offset);
193       // TODO(kojii): We probably don't need to AddChild this line, but there
194       // maybe OOF objects. Investigate how to handle them.
195     }
196   }
197 
198   const NGMarginStrut end_margin_strut = child_layout_result.EndMarginStrut();
199   // No margins should pierce outside formatting-context roots.
200   DCHECK(!fragment.IsFormattingContextRoot() || end_margin_strut.IsEmpty());
201 
202   AddChild(fragment, offset, /* inline_container */ nullptr, &end_margin_strut,
203            child_layout_result.IsSelfCollapsing());
204   if (fragment.IsBox())
205     PropagateBreak(child_layout_result);
206 }
207 
AddChild(const NGPhysicalContainerFragment & child,const LogicalOffset & child_offset,const LayoutInline * inline_container,const NGMarginStrut * margin_strut,bool is_self_collapsing)208 void NGBoxFragmentBuilder::AddChild(const NGPhysicalContainerFragment& child,
209                                     const LogicalOffset& child_offset,
210                                     const LayoutInline* inline_container,
211                                     const NGMarginStrut* margin_strut,
212                                     bool is_self_collapsing) {
213   LogicalOffset adjusted_offset = child_offset;
214 
215   if (box_type_ != NGPhysicalBoxFragment::NGBoxType::kInlineBox) {
216     if (child.IsCSSBox()) {
217       // Apply the relative position offset.
218       const auto& box_child = To<NGPhysicalBoxFragment>(child);
219       if (box_child.Style().GetPosition() == EPosition::kRelative) {
220         adjusted_offset += ComputeRelativeOffsetForBoxFragment(
221             box_child, GetWritingDirection(), child_available_size_);
222       }
223 
224       // The |may_have_descendant_above_block_start_| flag is used to determine
225       // if a fragment can be re-used when preceding floats are present. This
226       // is relatively rare, and is true if:
227       //  - An inflow child is positioned above our block-start edge.
228       //  - Any inflow descendants (within the same formatting-context) which
229       //    *may* have a child positioned above our block-start edge.
230       if ((child_offset.block_offset < LayoutUnit() &&
231            !box_child.IsOutOfFlowPositioned()) ||
232           (!box_child.IsFormattingContextRoot() &&
233            box_child.MayHaveDescendantAboveBlockStart()))
234         may_have_descendant_above_block_start_ = true;
235     }
236 
237     // If we are a scroll container, we need to track the maximum bounds of any
238     // inflow children (including line-boxes) to calculate the layout-overflow.
239     //
240     // This is used for determining the "padding-box" of the scroll container
241     // which is *sometimes* considered as part of the scrollable area. Inflow
242     // children contribute to this area, out-of-flow positioned children don't.
243     //
244     // Out-of-flow positioned children still contribute to the layout-overflow,
245     // but just don't influence where this padding is.
246     if (Node().IsScrollContainer() && !child.IsOutOfFlowPositioned()) {
247       NGBoxStrut margins;
248       if (child.IsCSSBox()) {
249         margins =
250             ComputeMarginsFor(child.Style(), child_available_size_.inline_size,
251                               GetWritingDirection());
252       }
253 
254       // If we are in block-flow layout we use the end *margin-strut* as the
255       // block-end "margin" (instead of just the block-end margin).
256       if (margin_strut) {
257         NGMarginStrut end_margin_strut = *margin_strut;
258         end_margin_strut.Append(margins.block_end, /* is_quirky */ false);
259 
260         // Self-collapsing blocks are special, their end margin-strut is part
261         // of their inflow position. To correctly determine the "end" margin,
262         // we need to the "final" margin-strut from their end margin-strut.
263         margins.block_end = is_self_collapsing
264                                 ? end_margin_strut.Sum() - margin_strut->Sum()
265                                 : end_margin_strut.Sum();
266       }
267 
268       // Use the original offset (*without* relative-positioning applied).
269       NGFragment fragment(GetWritingDirection(), child);
270       LogicalRect bounds = {child_offset, fragment.Size()};
271 
272       // Margins affect the inflow-bounds in interesting ways.
273       //
274       // For the margin which is closest to the direction which we are
275       // scrolling, we allow negative margins, but only up to the size of the
276       // fragment. For the margin furthest away we disallow negative margins.
277       if (!margins.IsEmpty()) {
278         // Convert the physical overflow directions to logical.
279         const bool has_top_overflow = Node().HasTopOverflow();
280         const bool has_left_overflow = Node().HasLeftOverflow();
281         PhysicalToLogical<bool> converter(GetWritingDirection(),
282                                           has_top_overflow, !has_left_overflow,
283                                           !has_top_overflow, has_left_overflow);
284 
285         if (converter.InlineStart()) {
286           margins.inline_end = margins.inline_end.ClampNegativeToZero();
287           margins.inline_start =
288               std::max(margins.inline_start, -fragment.InlineSize());
289         } else {
290           margins.inline_start = margins.inline_start.ClampNegativeToZero();
291           margins.inline_end =
292               std::max(margins.inline_end, -fragment.InlineSize());
293         }
294         if (converter.BlockStart()) {
295           margins.block_end = margins.block_end.ClampNegativeToZero();
296           margins.block_start =
297               std::max(margins.block_start, -fragment.BlockSize());
298         } else {
299           margins.block_start = margins.block_start.ClampNegativeToZero();
300           margins.block_end =
301               std::max(margins.block_end, -fragment.BlockSize());
302         }
303 
304         // Shift the bounds by the (potentially clamped) margins.
305         bounds.offset -= {margins.inline_start, margins.block_start};
306         bounds.size.inline_size += margins.InlineSum();
307         bounds.size.block_size += margins.BlockSum();
308 
309         // Our bounds size should never go negative.
310         DCHECK_GE(bounds.size.inline_size, LayoutUnit());
311         DCHECK_GE(bounds.size.block_size, LayoutUnit());
312       }
313 
314       // Even an empty (0x0) fragment contributes to the inflow-bounds.
315       if (!inflow_bounds_)
316         inflow_bounds_ = bounds;
317       else
318         inflow_bounds_->UniteEvenIfEmpty(bounds);
319     }
320   }
321 
322   PropagateChildData(child, adjusted_offset, inline_container);
323   AddChildInternal(&child, adjusted_offset);
324 }
325 
AddBreakToken(scoped_refptr<const NGBreakToken> token,bool is_in_parallel_flow)326 void NGBoxFragmentBuilder::AddBreakToken(
327     scoped_refptr<const NGBreakToken> token,
328     bool is_in_parallel_flow) {
329   DCHECK(token.get());
330   child_break_tokens_.push_back(std::move(token));
331   has_inflow_child_break_inside_ |= !is_in_parallel_flow;
332 }
333 
AddOutOfFlowLegacyCandidate(NGBlockNode node,const NGLogicalStaticPosition & static_position,const LayoutInline * inline_container)334 void NGBoxFragmentBuilder::AddOutOfFlowLegacyCandidate(
335     NGBlockNode node,
336     const NGLogicalStaticPosition& static_position,
337     const LayoutInline* inline_container) {
338   oof_positioned_candidates_.emplace_back(
339       node, static_position,
340       inline_container ? To<LayoutInline>(inline_container->ContinuationRoot())
341                        : nullptr);
342 }
343 
BoxType() const344 NGPhysicalFragment::NGBoxType NGBoxFragmentBuilder::BoxType() const {
345   if (box_type_ != NGPhysicalFragment::NGBoxType::kNormalBox)
346     return box_type_;
347 
348   // When implicit, compute from LayoutObject.
349   DCHECK(layout_object_);
350   if (layout_object_->IsFloating())
351     return NGPhysicalFragment::NGBoxType::kFloating;
352   if (layout_object_->IsOutOfFlowPositioned())
353     return NGPhysicalFragment::NGBoxType::kOutOfFlowPositioned;
354   if (layout_object_->IsRenderedLegend())
355     return NGPhysicalFragment::NGBoxType::kRenderedLegend;
356   if (layout_object_->IsInline()) {
357     // Check |IsAtomicInlineLevel()| after |IsInline()| because |LayoutReplaced|
358     // sets |IsAtomicInlineLevel()| even when it's block-level. crbug.com/567964
359     if (layout_object_->IsAtomicInlineLevel())
360       return NGPhysicalFragment::NGBoxType::kAtomicInline;
361     return NGPhysicalFragment::NGBoxType::kInlineBox;
362   }
363   DCHECK(node_) << "Must call SetBoxType if there is no node";
364   DCHECK_EQ(is_new_fc_, node_.CreatesNewFormattingContext())
365       << "Forgot to call builder.SetIsNewFormattingContext";
366   if (is_new_fc_)
367     return NGPhysicalFragment::NGBoxType::kBlockFlowRoot;
368   return NGPhysicalFragment::NGBoxType::kNormalBox;
369 }
370 
JoinedBreakBetweenValue(EBreakBetween break_before) const371 EBreakBetween NGBoxFragmentBuilder::JoinedBreakBetweenValue(
372     EBreakBetween break_before) const {
373   return JoinFragmentainerBreakValues(previous_break_after_, break_before);
374 }
375 
MoveChildrenInBlockDirection(LayoutUnit delta)376 void NGBoxFragmentBuilder::MoveChildrenInBlockDirection(LayoutUnit delta) {
377   DCHECK(is_new_fc_);
378   DCHECK(!has_oof_candidate_that_needs_block_offset_adjustment_);
379   DCHECK_NE(FragmentBlockSize(), kIndefiniteSize);
380   DCHECK(oof_positioned_descendants_.IsEmpty());
381 
382   if (delta == LayoutUnit())
383     return;
384 
385   if (baseline_)
386     *baseline_ += delta;
387   if (last_baseline_)
388     *last_baseline_ += delta;
389 
390   if (inflow_bounds_)
391     inflow_bounds_->offset.block_offset += delta;
392 
393   for (auto& child : children_)
394     child.offset.block_offset += delta;
395 
396   for (auto& candidate : oof_positioned_candidates_)
397     candidate.static_position.offset.block_offset += delta;
398   for (auto& descendant : oof_positioned_fragmentainer_descendants_)
399     descendant.static_position.offset.block_offset += delta;
400 
401   if (NGFragmentItemsBuilder* items_builder = ItemsBuilder())
402     items_builder->MoveChildrenInBlockDirection(delta);
403 }
404 
PropagateBreak(const NGLayoutResult & child_layout_result)405 void NGBoxFragmentBuilder::PropagateBreak(
406     const NGLayoutResult& child_layout_result) {
407   if (LIKELY(!has_block_fragmentation_))
408     return;
409   if (!has_inflow_child_break_inside_ || !has_float_break_inside_) {
410     // Figure out if this child break is in the same flow as this parent. If
411     // it's an out-of-flow positioned box, it's not. If it's in a parallel flow,
412     // it's also not.
413     const auto& child_fragment =
414         To<NGPhysicalBoxFragment>(child_layout_result.PhysicalFragment());
415     if (const auto* token = child_fragment.BreakToken()) {
416       if (!token->IsFinished() &&
417           (!token->IsBlockType() ||
418            !To<NGBlockBreakToken>(token)->IsAtBlockEnd())) {
419         if (child_fragment.IsFloating())
420           has_float_break_inside_ = true;
421         else if (!child_fragment.IsOutOfFlowPositioned())
422           has_inflow_child_break_inside_ = true;
423       }
424     }
425   }
426   if (child_layout_result.HasForcedBreak()) {
427     SetHasForcedBreak();
428   } else if (IsInitialColumnBalancingPass()) {
429     PropagateTallestUnbreakableBlockSize(
430         child_layout_result.TallestUnbreakableBlockSize());
431   } else {
432     PropagateSpaceShortage(child_layout_result.MinimalSpaceShortage());
433   }
434 }
435 
ToBoxFragment(WritingMode block_or_line_writing_mode)436 scoped_refptr<const NGLayoutResult> NGBoxFragmentBuilder::ToBoxFragment(
437     WritingMode block_or_line_writing_mode) {
438 #if DCHECK_IS_ON()
439   if (ItemsBuilder()) {
440     for (const ChildWithOffset& child : Children()) {
441       DCHECK(child.fragment);
442       const NGPhysicalFragment& fragment = *child.fragment;
443       DCHECK(fragment.IsLineBox() ||
444              // TODO(kojii): How to place floats and OOF is TBD.
445              fragment.IsFloatingOrOutOfFlowPositioned());
446     }
447   }
448 #endif
449 
450   if (UNLIKELY(node_ && has_block_fragmentation_)) {
451     if (!inline_break_tokens_.IsEmpty()) {
452       if (auto token = inline_break_tokens_.back()) {
453         if (!token->IsFinished())
454           child_break_tokens_.push_back(std::move(token));
455       }
456     }
457     if (DidBreakSelf() || HasChildBreakInside())
458       break_token_ = NGBlockBreakToken::Create(*this);
459   }
460 
461   if (!has_floating_descendants_for_paint_ && items_builder_) {
462     has_floating_descendants_for_paint_ =
463         items_builder_->HasFloatingDescendantsForPaint();
464   }
465 
466   scoped_refptr<const NGPhysicalBoxFragment> fragment =
467       NGPhysicalBoxFragment::Create(this, block_or_line_writing_mode);
468   fragment->CheckType();
469 
470   return base::AdoptRef(
471       new NGLayoutResult(NGLayoutResult::NGBoxFragmentBuilderPassKey(),
472                          std::move(fragment), this));
473 }
474 
Abort(NGLayoutResult::EStatus status)475 scoped_refptr<const NGLayoutResult> NGBoxFragmentBuilder::Abort(
476     NGLayoutResult::EStatus status) {
477   return base::AdoptRef(new NGLayoutResult(
478       NGLayoutResult::NGBoxFragmentBuilderPassKey(), status, this));
479 }
480 
GetChildOffset(const LayoutObject * object) const481 LogicalOffset NGBoxFragmentBuilder::GetChildOffset(
482     const LayoutObject* object) const {
483   DCHECK(object);
484 
485   if (const NGFragmentItemsBuilder* items_builder = items_builder_) {
486     if (auto offset = items_builder->LogicalOffsetFor(*object))
487       return *offset;
488     NOTREACHED();
489     return LogicalOffset();
490   }
491 
492   for (const auto& child : children_) {
493     if (child.fragment->GetLayoutObject() == object)
494       return child.offset;
495 
496     // TODO(layout-dev): ikilpatrick thinks we may need to traverse
497     // further than the initial line-box children for a nested inline
498     // container. We could not come up with a testcase, it would be
499     // something with split inlines, and nested oof/fixed descendants maybe.
500     if (child.fragment->IsLineBox()) {
501       const auto& line_box_fragment =
502           To<NGPhysicalLineBoxFragment>(*child.fragment);
503       for (const auto& line_box_child : line_box_fragment.Children()) {
504         if (line_box_child->GetLayoutObject() == object) {
505           return child.offset + line_box_child.Offset().ConvertToLogical(
506                                     GetWritingDirection(),
507                                     line_box_fragment.Size(),
508                                     line_box_child->Size());
509         }
510       }
511     }
512   }
513   NOTREACHED();
514   return LogicalOffset();
515 }
516 
ComputeInlineContainerGeometryFromFragmentTree(InlineContainingBlockMap * inline_containing_block_map)517 void NGBoxFragmentBuilder::ComputeInlineContainerGeometryFromFragmentTree(
518     InlineContainingBlockMap* inline_containing_block_map) {
519   if (inline_containing_block_map->IsEmpty())
520     return;
521 
522   // This function has detailed knowledge of inline fragment tree structure,
523   // and will break if this changes.
524   DCHECK_GE(InlineSize(), LayoutUnit());
525   DCHECK_GE(FragmentBlockSize(), LayoutUnit());
526 #if DCHECK_IS_ON()
527   // Make sure all entries are continuation root.
528   for (const auto& entry : *inline_containing_block_map)
529     DCHECK_EQ(entry.key, entry.key->ContinuationRoot());
530 #endif
531 
532   HashMap<const LayoutObject*, LineBoxPair> containing_linebox_map;
533   for (const auto& child : children_) {
534     if (child.fragment->IsLineBox()) {
535       const auto& linebox = To<NGPhysicalLineBoxFragment>(*child.fragment);
536       const PhysicalOffset linebox_offset = child.offset.ConvertToPhysical(
537           GetWritingDirection(), ToPhysicalSize(Size(), GetWritingMode()),
538           linebox.Size());
539       GatherInlineContainerFragmentsFromLinebox(inline_containing_block_map,
540                                                 &containing_linebox_map,
541                                                 linebox, linebox_offset);
542     } else if (child.fragment->IsBox()) {
543       const auto& box_fragment = To<NGPhysicalBoxFragment>(*child.fragment);
544       bool is_anonymous_container =
545           box_fragment.GetLayoutObject() &&
546           box_fragment.GetLayoutObject()->IsAnonymousBlock();
547       if (!is_anonymous_container)
548         continue;
549       // If child is an anonymous container, this might be a special case of
550       // split inlines. The inline container fragments might be inside
551       // anonymous boxes. To find inline container fragments, traverse
552       // lineboxes inside anonymous box.
553       // For more on this special case, see "css container is an inline, with
554       // inline splitting" comment in NGOutOfFlowLayoutPart::LayoutDescendant.
555       const PhysicalOffset box_offset = child.offset.ConvertToPhysical(
556           GetWritingDirection(), ToPhysicalSize(Size(), GetWritingMode()),
557           box_fragment.Size());
558 
559       // Traverse lineboxes of anonymous box.
560       for (const auto& box_child : box_fragment.Children()) {
561         if (box_child->IsLineBox()) {
562           const auto& linebox = To<NGPhysicalLineBoxFragment>(*box_child);
563           const PhysicalOffset linebox_offset = box_child.Offset() + box_offset;
564           GatherInlineContainerFragmentsFromLinebox(inline_containing_block_map,
565                                                     &containing_linebox_map,
566                                                     linebox, linebox_offset);
567         }
568       }
569     }
570   }
571 }
572 
ComputeInlineContainerGeometry(InlineContainingBlockMap * inline_containing_block_map)573 void NGBoxFragmentBuilder::ComputeInlineContainerGeometry(
574     InlineContainingBlockMap* inline_containing_block_map) {
575   if (inline_containing_block_map->IsEmpty())
576     return;
577 
578   // This function requires that we have the final size of the fragment set
579   // upon the builder.
580   DCHECK_GE(InlineSize(), LayoutUnit());
581   DCHECK_GE(FragmentBlockSize(), LayoutUnit());
582 
583 #if DCHECK_IS_ON()
584   // Make sure all entries are a continuation root.
585   for (const auto& entry : *inline_containing_block_map)
586     DCHECK_EQ(entry.key, entry.key->ContinuationRoot());
587 #endif
588 
589   HashMap<const LayoutObject*, LineBoxPair> containing_linebox_map;
590 
591   if (items_builder_) {
592     // To access the items correctly we need to convert them to the physical
593     // coordinate space.
594     DCHECK_EQ(items_builder_->GetWritingMode(), GetWritingMode());
595     DCHECK_EQ(items_builder_->Direction(), Direction());
596     GatherInlineContainerFragmentsFromItems(
597         items_builder_->Items(ToPhysicalSize(Size(), GetWritingMode())),
598         PhysicalOffset(), inline_containing_block_map, &containing_linebox_map);
599     return;
600   }
601 
602   // If we have children which are anonymous block, we might contain split
603   // inlines, this can occur in the following example:
604   // <div>
605   //    Some text <span style="position: relative;">text
606   //    <div>block</div>
607   //    text </span> text.
608   // </div>
609   for (const auto& child : children_) {
610     if (!child.fragment->IsAnonymousBlock())
611       continue;
612 
613     const auto& child_fragment = To<NGPhysicalBoxFragment>(*child.fragment);
614     const auto* items = child_fragment.Items();
615     if (!items)
616       continue;
617 
618     const PhysicalOffset child_offset = child.offset.ConvertToPhysical(
619         GetWritingDirection(), ToPhysicalSize(Size(), GetWritingMode()),
620         child_fragment.Size());
621     GatherInlineContainerFragmentsFromItems(items->Items(), child_offset,
622                                             inline_containing_block_map,
623                                             &containing_linebox_map);
624   }
625 }
626 
SetLastBaselineToBlockEndMarginEdgeIfNeeded()627 void NGBoxFragmentBuilder::SetLastBaselineToBlockEndMarginEdgeIfNeeded() {
628   if (ConstraintSpace()->BaselineAlgorithmType() !=
629       NGBaselineAlgorithmType::kInlineBlock)
630     return;
631 
632   if (!node_.UseBlockEndMarginEdgeForInlineBlockBaseline())
633     return;
634 
635   // When overflow is present (within an atomic-inline baseline context) we
636   // should always use the block-end margin edge as the baseline.
637   NGBoxStrut margins = ComputeMarginsForSelf(*ConstraintSpace(), Style());
638   SetLastBaseline(FragmentBlockSize() + margins.block_end);
639 }
640 
SetMathItalicCorrection(LayoutUnit italic_correction)641 void NGBoxFragmentBuilder::SetMathItalicCorrection(
642     LayoutUnit italic_correction) {
643   if (!math_data_)
644     math_data_.emplace();
645   math_data_->italic_correction_ = italic_correction;
646 }
647 
648 #if DCHECK_IS_ON()
649 
CheckNoBlockFragmentation() const650 void NGBoxFragmentBuilder::CheckNoBlockFragmentation() const {
651   DCHECK(!HasChildBreakInside());
652   DCHECK(!HasInflowChildBreakInside());
653   DCHECK(!DidBreakSelf());
654   DCHECK(!has_forced_break_);
655   DCHECK_EQ(consumed_block_size_, LayoutUnit());
656   DCHECK_EQ(minimal_space_shortage_, LayoutUnit::Max());
657   DCHECK_EQ(initial_break_before_, EBreakBetween::kAuto);
658   DCHECK_EQ(previous_break_after_, EBreakBetween::kAuto);
659 }
660 
661 #endif
662 
663 }  // namespace blink
664