summaryrefslogtreecommitdiffstats
path: root/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h
blob: 59f67c0c133d501be654e6370225eefb8cd0d017 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
// 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.

#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_PHYSICAL_FRAGMENT_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_PHYSICAL_FRAGMENT_H_

#include <unicode/ubidi.h>

#include <iterator>

#include "base/containers/span.h"
#include "base/dcheck_is_on.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/editing/forward.h"
#include "third_party/blink/renderer/core/layout/geometry/physical_offset.h"
#include "third_party/blink/renderer/core/layout/geometry/physical_rect.h"
#include "third_party/blink/renderer/core/layout/geometry/physical_size.h"
#include "third_party/blink/renderer/core/layout/layout_box.h"
#include "third_party/blink/renderer/core/layout/layout_inline.h"
#include "third_party/blink/renderer/core/layout/ng/ng_break_token.h"
#include "third_party/blink/renderer/core/layout/ng/ng_ink_overflow.h"
#include "third_party/blink/renderer/core/layout/ng/ng_link.h"
#include "third_party/blink/renderer/core/layout/ng/ng_style_variant.h"
#include "third_party/blink/renderer/platform/graphics/touch_action.h"

namespace blink {

class ComputedStyle;
class FragmentData;
class Node;
class NGContainerFragmentBuilder;
class NGFragmentItem;
class PaintLayer;
struct LogicalRect;
struct NGFragmentedOutOfFlowData;
struct NGPhysicalOutOfFlowPositionedNode;

enum class NGOutlineType;

// The NGPhysicalFragment contains the output geometry from layout. The
// fragment stores all of its information in the physical coordinate system for
// use by paint, hit-testing etc.
//
// The fragment keeps a pointer back to the LayoutObject which generated it.
// Once we have transitioned fully to LayoutNG it should be a const pointer
// such that paint/hit-testing/etc don't modify it.
//
// Layout code should only access geometry information through the
// NGFragment wrapper classes which transforms information into the logical
// coordinate system.
class CORE_EXPORT NGPhysicalFragment
    : public GarbageCollected<NGPhysicalFragment> {
 public:
  enum NGFragmentType {
    kFragmentBox = 0,
    kFragmentLineBox = 1,
    // When adding new values, make sure the bit size of |type_| is large
    // enough to store.
  };
  enum NGBoxType {
    kNormalBox,
    kInlineBox,
    // A multi-column container creates column boxes as its children, which
    // content is flowed into. https://www.w3.org/TR/css-multicol-1/#column-box
    kColumnBox,
    // A page box fragment. Used by printing.
    kPageBox,
    kAtomicInline,
    kFloating,
    kOutOfFlowPositioned,
    kBlockFlowRoot,
    kRenderedLegend,
    // When adding new values, make sure the bit size of |sub_type_| is large
    // enough to store.

    // Also, add after kMinimumFormattingContextRoot if the box type is a
    // formatting context root, or before otherwise. See
    // IsFormattingContextRoot().
    kMinimumFormattingContextRoot = kAtomicInline
  };

  NGPhysicalFragment(NGContainerFragmentBuilder* builder,
                     WritingMode block_or_line_writing_mode,
                     NGFragmentType type,
                     unsigned sub_type);

  NGPhysicalFragment(const NGPhysicalFragment& other,
                     bool recalculate_layout_overflow);

  ~NGPhysicalFragment();

  void Dispose();

  NGFragmentType Type() const { return static_cast<NGFragmentType>(type_); }
  bool IsContainer() const {
    return Type() == NGFragmentType::kFragmentBox ||
           Type() == NGFragmentType::kFragmentLineBox;
  }
  bool IsBox() const { return Type() == NGFragmentType::kFragmentBox; }
  bool IsLineBox() const { return Type() == NGFragmentType::kFragmentLineBox; }

  // Returns the box type of this fragment.
  NGBoxType BoxType() const {
    DCHECK(IsBox());
    return static_cast<NGBoxType>(sub_type_);
  }
  // True if this is an inline box; e.g., <span>. Atomic inlines such as
  // replaced elements or inline block are not included.
  bool IsInlineBox() const {
    return IsBox() && BoxType() == NGBoxType::kInlineBox;
  }
  bool IsColumnBox() const {
    return IsBox() && BoxType() == NGBoxType::kColumnBox;
  }
  bool IsPageBox() const { return IsBox() && BoxType() == NGBoxType::kPageBox; }
  bool IsFragmentainerBox() const { return IsColumnBox() || IsPageBox(); }
  bool IsColumnSpanAll() const {
    if (const auto* box = DynamicTo<LayoutBox>(GetLayoutObject()))
      return box->IsColumnSpanAll();
    return false;
  }
  // An atomic inline is represented as a kFragmentBox, such as inline block and
  // replaced elements.
  bool IsAtomicInline() const {
    return IsBox() && BoxType() == NGBoxType::kAtomicInline;
  }
  // True if this box is a block-in-inline, or if this line contains a
  // block-in-inline.
  bool IsBlockInInline() const { return is_block_in_inline_; }
  // True if this fragment is in-flow in an inline formatting context.
  bool IsInline() const { return IsInlineBox() || IsAtomicInline(); }
  bool IsFloating() const {
    return IsBox() && BoxType() == NGBoxType::kFloating;
  }
  bool IsOutOfFlowPositioned() const {
    return IsBox() && BoxType() == NGBoxType::kOutOfFlowPositioned;
  }
  bool IsFixedPositioned() const {
    return IsCSSBox() && layout_object_->IsFixedPositioned();
  }
  bool IsFloatingOrOutOfFlowPositioned() const {
    return IsFloating() || IsOutOfFlowPositioned();
  }
  // Return true if this is the legend child of a fieldset that gets special
  // treatment (i.e. placed over the block-start border).
  bool IsRenderedLegend() const {
    return IsBox() && BoxType() == NGBoxType::kRenderedLegend;
  }
  bool IsMathML() const {
    return IsBox() && GetSelfOrContainerLayoutObject()->IsMathML();
  }
  bool IsMathMLFraction() const { return IsBox() && is_math_fraction_; }

  bool IsMathMLOperator() const { return IsBox() && is_math_operator_; }

  // Return true if this fragment corresponds directly to an entry in the CSS
  // box tree [1]. Note that anonymous blocks also exist in the CSS box
  // tree. Returns false otherwise, i.e. if the fragment is generated by the
  // layout engine to contain fragments from CSS boxes (a line or a generated
  // fragmentainer [2], in other words). The main signification of this is
  // whether we can use the LayoutObject associated with this fragment for all
  // purposes.
  //
  // [1] https://www.w3.org/TR/css-display-3/#box-tree
  // [2] https://www.w3.org/TR/css-break-3/#fragmentation-container
  bool IsCSSBox() const { return !IsLineBox() && !IsFragmentainerBox(); }

  bool IsBlockFlow() const;
  bool IsAnonymousBlock() const {
    return IsCSSBox() && layout_object_->IsAnonymousBlock();
  }
  bool IsListMarker() const {
    return IsCSSBox() && layout_object_->IsLayoutNGOutsideListMarker();
  }
  bool IsRubyRun() const { return layout_object_->IsRubyRun(); }

  // Return true if this fragment is for LayoutNGRubyRun, LayoutNGRubyText, or
  // LayoutNGRubyBase. They are handled specially in scrollable overflow
  // computation.
  bool IsRubyBox() const {
    return layout_object_->IsRubyRun() || layout_object_->IsRubyText() ||
           layout_object_->IsRubyBase();
  }

  bool IsSvg() const { return layout_object_->IsSVG(); }
  bool IsSvgText() const { return layout_object_->IsNGSVGText(); }

  bool IsTableNGPart() const { return is_table_ng_part_; }

  bool IsTableNG() const {
    return IsTableNGPart() && layout_object_->IsTable();
  }

  bool IsTableNGRow() const {
    return IsTableNGPart() && layout_object_->IsTableRow();
  }

  bool IsTableNGSection() const {
    return IsTableNGPart() && layout_object_->IsTableSection();
  }

  bool IsTableNGCell() const {
    return IsTableNGPart() && layout_object_->IsTableCell() &&
           !layout_object_->IsTableCellLegacy();
  }

  bool IsGridNG() const { return layout_object_->IsLayoutNGGrid(); }

  bool IsTextControlContainer() const;
  bool IsTextControlPlaceholder() const;

  // Return true if this fragment is a container established by a fieldset
  // element. Such a fragment contains an optional rendered legend fragment and
  // an optional fieldset contents wrapper fragment (which holds everything
  // inside the fieldset except the rendered legend).
  bool IsFieldsetContainer() const { return is_fieldset_container_; }

  // Returns whether the fragment is legacy layout root.
  bool IsLegacyLayoutRoot() const { return is_legacy_layout_root_; }

  // Returns whether the fragment should be atomically painted.
  bool IsPaintedAtomically() const { return is_painted_atomically_; }

  // Returns whether the fragment is a table part with collapsed borders.
  bool HasCollapsedBorders() const { return has_collapsed_borders_; }

  bool IsFormattingContextRoot() const {
    return (IsBox() && BoxType() >= NGBoxType::kMinimumFormattingContextRoot) ||
           IsLegacyLayoutRoot();
  }

  // Returns true if we have a descendant within this formatting context, which
  // is potentially above our block-start edge.
  bool MayHaveDescendantAboveBlockStart() const {
    return may_have_descendant_above_block_start_;
  }

  // The accessors in this class shouldn't be used by layout code directly,
  // instead should be accessed by the NGFragmentBase classes. These accessors
  // exist for paint, hit-testing, etc.

  // Returns the border-box size.
  PhysicalSize Size() const { return size_; }

  // Returns the rect in the local coordinate of this fragment; i.e., offset is
  // (0, 0).
  PhysicalRect LocalRect() const { return {{}, size_}; }

  NGStyleVariant StyleVariant() const {
    return static_cast<NGStyleVariant>(style_variant_);
  }
  bool UsesFirstLineStyle() const {
    return StyleVariant() == NGStyleVariant::kFirstLine;
  }

  // Returns the style for this fragment.
  //
  // For a line box, this returns the style of the containing block. This mostly
  // represents the style for the line box, except 1) |style.Direction()| maybe
  // incorrect, use |BaseDirection()| instead, and 2) margin/border/padding,
  // background etc. do not apply to the line box.
  const ComputedStyle& Style() const {
    return layout_object_->EffectiveStyle(StyleVariant());
  }

  const Document& GetDocument() const {
    DCHECK(layout_object_);
    return layout_object_->GetDocument();
  }
  Node* GetNode() const {
    return IsCSSBox() ? layout_object_->GetNode() : nullptr;
  }
  Node* GeneratingNode() const {
    return IsCSSBox() ? layout_object_->GeneratingNode() : nullptr;
  }
  // The node to return when hit-testing on this fragment. This can be different
  // from GetNode() when this fragment is content of a pseudo node.
  Node* NodeForHitTest() const {
    if (IsFragmentainerBox())
      return nullptr;
    return layout_object_->NodeForHitTest();
  }

  Node* NonPseudoNode() const {
    return IsCSSBox() ? layout_object_->NonPseudoNode() : nullptr;
  }

  bool IsInSelfHitTestingPhase(HitTestAction action) const {
    if (IsFragmentainerBox())
      return false;
    if (const auto* box = DynamicTo<LayoutBox>(GetLayoutObject()))
      return box->IsInSelfHitTestingPhase(action);
    if (IsInlineBox())
      return action == kHitTestForeground;
    // Assuming this is some sort of container, e.g. a fragmentainer (they don't
    // have a LayoutObject associated).
    return action == kHitTestBlockBackground ||
           action == kHitTestChildBlockBackground;
  }

  // Whether there is a PaintLayer associated with the fragment.
  bool HasLayer() const { return IsCSSBox() && layout_object_->HasLayer(); }

  // The PaintLayer associated with the fragment.
  PaintLayer* Layer() const {
    if (!HasLayer())
      return nullptr;
    return To<LayoutBoxModelObject>(layout_object_.Get())->Layer();
  }

  // Whether this object has a self-painting |Layer()|.
  bool HasSelfPaintingLayer() const {
    return HasLayer() && To<LayoutBoxModelObject>(layout_object_.Get())
                             ->HasSelfPaintingLayer();
  }

  // True if overflow != 'visible', except for certain boxes that do not allow
  // overflow clip; i.e., AllowOverflowClip() returns false.
  bool HasNonVisibleOverflow() const {
    return IsCSSBox() && layout_object_->HasNonVisibleOverflow();
  }

  // True if this is considered a scroll-container. See
  // ComputedStyle::IsScrollContainer() for details.
  bool IsScrollContainer() const {
    return IsCSSBox() && layout_object_->IsScrollContainer();
  }

  // Return true if the given object is the effective root scroller in its
  // Document. See |effective root scroller| in page/scrolling/README.md.
  // Note: a root scroller always establishes a PaintLayer.
  // This bit is updated in
  // RootScrollerController::RecomputeEffectiveRootScroller in the LayoutClean
  // document lifecycle phase.
  bool IsEffectiveRootScroller() const {
    return IsCSSBox() && layout_object_->IsEffectiveRootScroller();
  }

  bool ShouldApplyLayoutContainment() const {
    return IsCSSBox() && layout_object_->ShouldApplyLayoutContainment();
  }

  bool ShouldClipOverflowAlongEitherAxis() const {
    return IsCSSBox() && layout_object_->ShouldClipOverflowAlongEitherAxis();
  }

  bool ShouldClipOverflowAlongBothAxis() const {
    return IsCSSBox() && layout_object_->ShouldClipOverflowAlongBothAxis();
  }

  bool ShouldApplyOverflowClipMargin() const {
    return IsCSSBox() && layout_object_->ShouldApplyOverflowClipMargin();
  }

  // Return whether we can traverse this fragment and its children directly, for
  // painting, hit-testing and other layout read operations. If false is
  // returned, we need to traverse the layout object tree instead.
  bool CanTraverse() const {
    return layout_object_->CanTraversePhysicalFragments();
  }

  // This fragment is hidden for paint purpose, but exists for querying layout
  // information. Used for `text-overflow: ellipsis`.
  bool IsHiddenForPaint() const {
    return is_hidden_for_paint_ || layout_object_->IsTruncated();
  }

  // This fragment is opaque for layout and paint, as if it does not exist and
  // does not paint its backgrounds and borders, but it can have regular
  // children and paint properties such as filters can apply.
  bool IsOpaque() const { return is_opaque_; }

  // Return true if this fragment is monolithic, as far as block fragmentation
  // is concerned.
  bool IsMonolithic() const {
    // Line boxes are monolithic, except for line boxes that are just there to
    // contain a block inside an inline, in which case the anonymous block child
    // wrapper inside the line is breakable.
    if (IsLineBox())
      return !IsBlockInInline();
    const LayoutObject* layout_object = GetLayoutObject();
    if (!layout_object || !IsBox() || !layout_object->IsBox())
      return false;
    return To<LayoutBox>(layout_object)->GetNGPaginationBreakability() ==
           LayoutBox::kForbidBreaks;
  }

  // GetLayoutObject should only be used when necessary for compatibility
  // with LegacyLayout.
  //
  // For a line box, |layout_object_| has its containing block but this function
  // returns |nullptr| for the historical reasons. TODO(kojii): We may change
  // this in future. Use |IsLineBox()| instead of testing this is |nullptr|.
  const LayoutObject* GetLayoutObject() const {
    return IsCSSBox() ? layout_object_ : nullptr;
  }
  // TODO(kojii): We should not have mutable version at all, the use of this
  // function should be eliminiated over time.
  LayoutObject* GetMutableLayoutObject() const {
    return IsCSSBox() ? layout_object_ : nullptr;
  }
  // Similar to |GetLayoutObject|, but returns the |LayoutObject| of its
  // container for |!IsCSSBox()| fragments instead of |nullptr|.
  const LayoutObject* GetSelfOrContainerLayoutObject() const {
    return layout_object_;
  }

  const FragmentData* GetFragmentData() const;

  // |NGPhysicalFragment| may live longer than the corresponding |LayoutObject|.
  // Though |NGPhysicalFragment| is immutable, |layout_object_| is cleared to
  // |nullptr| when it was destroyed to avoid reading destroyed objects.
  bool IsLayoutObjectDestroyedOrMoved() const { return !layout_object_; }
  void LayoutObjectWillBeDestroyed() const {
    const_cast<NGPhysicalFragment*>(this)->layout_object_ = nullptr;
  }

  // Returns the latest generation of the post-layout fragment. Returns
  // |nullptr| if |this| is the one.
  //
  // When subtree relayout occurs at the relayout boundary, its containing block
  // may keep the reference to old generations of this fragment. Callers can
  // check if there were newer generations.
  const NGPhysicalFragment* PostLayout() const;

  // Specifies the type of scrollable overflow computation.
  enum TextHeightType {
    // Apply text fragment size as is.
    kNormalHeight,
    // Adjust text fragment size for 'em' height, and skip to unite
    // container's bounding box. This type is useful for ruby annotation.
    kEmHeight
  };
  // Scrollable overflow. including contents, in the local coordinate.
  PhysicalRect ScrollableOverflow(const NGPhysicalBoxFragment& container,
                                  TextHeightType height_type) const;

  // ScrollableOverflow(), with transforms applied wrt container if needed.
  // This does not include any offsets from the parent (including relpos).
  PhysicalRect ScrollableOverflowForPropagation(
      const NGPhysicalBoxFragment& container,
      TextHeightType height_type) const;
  void AdjustScrollableOverflowForPropagation(
      const NGPhysicalBoxFragment& container,
      TextHeightType height_type,
      PhysicalRect* overflow) const;

  // The allowed touch action is the union of the effective touch action
  // (from style) and blocking touch event handlers.
  TouchAction EffectiveAllowedTouchAction() const;

  // Returns if this fragment is inside a non-passive wheel event handler.
  bool InsideBlockingWheelEventHandler() const;

  // Helper functions to convert between |PhysicalRect| and |LogicalRect| of a
  // child.
  LogicalRect ConvertChildToLogical(const PhysicalRect& physical_rect) const;

  String ToString() const;

  void CheckType() const;

  enum DumpFlag {
    DumpHeaderText = 0x1,
    DumpSubtree = 0x2,
    DumpIndentation = 0x4,
    DumpType = 0x8,
    DumpOffset = 0x10,
    DumpSize = 0x20,
    DumpTextOffsets = 0x40,
    DumpSelfPainting = 0x80,
    DumpNodeName = 0x100,
    DumpItems = 0x200,
    DumpLegacyDescendants = 0x400,
    DumpAll = -1
  };
  typedef int DumpFlags;

  // Dump the fragment tree, optionally mark |target| if it's found. If not
  // found, the subtree established by |target| will be dumped as well.
  String DumpFragmentTree(DumpFlags,
                          const NGPhysicalFragment* target = nullptr,
                          absl::optional<PhysicalOffset> = absl::nullopt,
                          unsigned indent = 2) const;

  // Dump the fragment tree, starting at |root| (searching inside legacy
  // subtrees to find all fragments), optionally mark |target| if it's found. If
  // not found, the subtree established by |target| will be dumped as well.
  //
  // Note that if we're in the middle of layout somewhere inside the subtree,
  // behavior is undefined.
  static String DumpFragmentTree(const LayoutObject& root,
                                 DumpFlags,
                                 const NGPhysicalFragment* target = nullptr);

  void Trace(Visitor*) const;
  void TraceAfterDispatch(Visitor*) const;

  // Same as |base::span<const NGLink>|, except that:
  // * Each |NGLink| has the latest generation of post-layout. See
  //   |NGPhysicalFragment::PostLayout()| for more details.
  // * The iterator skips fragments for destroyed or moved |LayoutObject|.
  class PostLayoutChildLinkList {
    STACK_ALLOCATED();

   public:
    PostLayoutChildLinkList(wtf_size_t count, const NGLink* buffer)
        : count_(count), buffer_(buffer) {}

    class ConstIterator
        : public std::iterator<std::input_iterator_tag, NGLink> {
      STACK_ALLOCATED();

     public:
      using iterator_category = std::bidirectional_iterator_tag;
      using value_type = NGLink;
      using difference_type = ptrdiff_t;
      using pointer = value_type*;
      using reference = value_type&;

      ConstIterator(const NGLink* current, wtf_size_t size)
          : current_(current), end_(current + size) {
        SkipInvalidAndSetPostLayout();
      }

      const NGLink& operator*() const { return post_layout_; }
      const NGLink* operator->() const { return &post_layout_; }

      ConstIterator& operator++() {
        ++current_;
        SkipInvalidAndSetPostLayout();
        return *this;
      }
      bool operator==(const ConstIterator& other) const {
        return current_ == other.current_;
      }
      bool operator!=(const ConstIterator& other) const {
        return current_ != other.current_;
      }

     private:
      void SkipInvalidAndSetPostLayout() {
        for (; current_ != end_; ++current_) {
          const NGPhysicalFragment* fragment = current_->fragment;
          if (UNLIKELY(fragment->IsLayoutObjectDestroyedOrMoved()))
            continue;
          if (const NGPhysicalFragment* post_layout = fragment->PostLayout()) {
            post_layout_.fragment = post_layout;
            post_layout_.offset = current_->offset;
            return;
          }
        }
      }

      const NGLink* current_;
      const NGLink* end_;
      NGLink post_layout_;
    };
    using const_iterator = ConstIterator;

    const_iterator begin() const { return const_iterator(buffer_, count_); }
    const_iterator end() const { return const_iterator(buffer_ + count_, 0); }

    wtf_size_t size() const { return count_; }
    bool empty() const { return count_ == 0; }

   private:
    wtf_size_t count_;
    const NGLink* buffer_;
  };

  const NGBreakToken* BreakToken() const { return break_token_; }

  base::span<const NGLink> Children() const;

  PostLayoutChildLinkList PostLayoutChildren() const;

  // Returns true if we have any floating descendants which need to be
  // traversed during the float paint phase.
  bool HasFloatingDescendantsForPaint() const {
    return has_floating_descendants_for_paint_;
  }

  // Returns true if we have any adjoining-object descendants (floats, or
  // inline-level OOF-positioned objects).
  bool HasAdjoiningObjectDescendants() const {
    return has_adjoining_object_descendants_;
  }

  // Returns true if we aren't able to re-use this fragment if the
  // |NGConstraintSpace::PercentageResolutionBlockSize| changes.
  bool DependsOnPercentageBlockSize() const {
    return depends_on_percentage_block_size_;
  }

  void SetChildrenInvalid() const;
  bool ChildrenValid() const { return children_valid_; }

  struct OutOfFlowData : public GarbageCollected<OutOfFlowData> {
   public:
    virtual void Trace(Visitor* visitor) const;
    HeapVector<NGPhysicalOutOfFlowPositionedNode> oof_positioned_descendants;
  };

  // Returns true if some child is OOF in the fragment tree. This happens if
  // it's the containing block of the OOF, or if it's a fragmentation context
  // root containing them.
  bool HasOutOfFlowFragmentChild() const {
    return has_out_of_flow_fragment_child_;
  }

  bool HasOutOfFlowPositionedDescendants() const {
    return oof_data_ && !oof_data_->oof_positioned_descendants.IsEmpty();
  }

  base::span<NGPhysicalOutOfFlowPositionedNode> OutOfFlowPositionedDescendants()
      const {
    if (!HasOutOfFlowPositionedDescendants())
      return base::span<NGPhysicalOutOfFlowPositionedNode>();
    return {oof_data_->oof_positioned_descendants.data(),
            oof_data_->oof_positioned_descendants.size()};
  }

  NGFragmentedOutOfFlowData* FragmentedOutOfFlowData() const;

  // Return true if there are nested multicol container descendants with OOFs
  // inside.
  bool HasNestedMulticolsWithOOFs() const;

  // Figure out if the child has any out-of-flow positioned descendants, in
  // which case we'll need to propagate this to the fragment builder.
  bool NeedsOOFPositionedInfoPropagation() const;

 protected:
  const ComputedStyle& SlowEffectiveStyle() const;

  void AddScrollableOverflowForInlineChild(
      const NGPhysicalBoxFragment& container,
      const ComputedStyle& container_style,
      const NGFragmentItem& line,
      bool has_hanging,
      const NGInlineCursor& cursor,
      TextHeightType height_type,
      PhysicalRect* overflow) const;

  static void AdjustScrollableOverflowForHanging(
      const PhysicalRect& rect,
      const WritingMode container_writing_mode,
      PhysicalRect* overflow);

  void AddOutlineRectsForNormalChildren(
      Vector<PhysicalRect>* outline_rects,
      const PhysicalOffset& additional_offset,
      NGOutlineType outline_type,
      const LayoutBoxModelObject* containing_block) const;
  void AddOutlineRectsForCursor(Vector<PhysicalRect>* outline_rects,
                                const PhysicalOffset& additional_offset,
                                NGOutlineType outline_type,
                                const LayoutBoxModelObject* containing_block,
                                NGInlineCursor* cursor) const;
  void AddOutlineRectsForDescendant(
      const NGLink& descendant,
      Vector<PhysicalRect>* rects,
      const PhysicalOffset& additional_offset,
      NGOutlineType outline_type,
      const LayoutBoxModelObject* containing_block) const;

  static bool DependsOnPercentageBlockSize(const NGContainerFragmentBuilder&);

  OutOfFlowData* OutOfFlowDataFromBuilder(NGContainerFragmentBuilder*);
  OutOfFlowData* FragmentedOutOfFlowDataFromBuilder(
      NGContainerFragmentBuilder*);
  void ClearOutOfFlowData();
  OutOfFlowData* CloneOutOfFlowData() const;

  Member<LayoutObject> layout_object_;
  const PhysicalSize size_;

  unsigned has_floating_descendants_for_paint_ : 1;
  unsigned has_adjoining_object_descendants_ : 1;
  unsigned depends_on_percentage_block_size_ : 1;
  mutable unsigned children_valid_ : 1;

  // The following bitfields are only to be used by NGPhysicalLineBoxFragment
  // (it's defined here to save memory, since that class has no bitfields).
  unsigned has_propagated_descendants_ : 1;
  unsigned has_hanging_ : 1;

  const unsigned type_ : 1;           // NGFragmentType
  const unsigned sub_type_ : 4;       // NGBoxType, NGTextType, or NGLineBoxType
  const unsigned style_variant_ : 2;  // NGStyleVariant
  const unsigned is_hidden_for_paint_ : 1;
  unsigned is_opaque_ : 1;
  unsigned is_block_in_inline_ : 1;
  unsigned is_math_fraction_ : 1;
  unsigned is_math_operator_ : 1;
  unsigned may_have_descendant_above_block_start_ : 1;

  // The following are only used by NGPhysicalBoxFragment but are initialized
  // for all types to allow methods using them to be inlined.
  unsigned is_fieldset_container_ : 1;
  unsigned is_table_ng_part_ : 1;
  unsigned is_legacy_layout_root_ : 1;
  unsigned is_painted_atomically_ : 1;
  unsigned has_collapsed_borders_ : 1;
  unsigned has_baseline_ : 1;
  unsigned has_last_baseline_ : 1;
  const unsigned has_fragmented_out_of_flow_data_ : 1;
  const unsigned has_out_of_flow_fragment_child_ : 1;

  // The following are only used by NGPhysicalLineBoxFragment.
  unsigned base_direction_ : 1;  // TextDirection

  Member<const NGBreakToken> break_token_;
  const Member<OutOfFlowData> oof_data_;
};

CORE_EXPORT std::ostream& operator<<(std::ostream&, const NGPhysicalFragment*);
CORE_EXPORT std::ostream& operator<<(std::ostream&, const NGPhysicalFragment&);

#if !DCHECK_IS_ON()
inline void NGPhysicalFragment::CheckType() const {}
#endif

}  // namespace blink

#if DCHECK_IS_ON()
// Outside the blink namespace for ease of invocation from a debugger.

// Output the fragment tree to the log.
// See DumpFragmentTree().
CORE_EXPORT void ShowFragmentTree(const blink::NGPhysicalFragment*);

// Output the fragment tree(s) inside |root| to the log.
// See DumpFragmentTree(const LayoutObject& ...).
CORE_EXPORT void ShowFragmentTree(
    const blink::LayoutObject& root,
    const blink::NGPhysicalFragment* target = nullptr);

// Output the fragment tree(s) from the entire document to the log.
// See DumpFragmentTree(const LayoutObject& ...).
CORE_EXPORT void ShowEntireFragmentTree(
    const blink::NGPhysicalFragment* target);
#endif  // DCHECK_IS_ON()

#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_PHYSICAL_FRAGMENT_H_