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