/*
 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
 *           (C) 1999 Antti Koivisto (koivisto@kde.org)
 * Copyright (C) 2003, 2006, 2007 Apple Inc. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 *
 */

#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LAYOUT_BOX_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LAYOUT_BOX_H_

#include <memory>

#include "base/check_op.h"
#include "base/dcheck_is_on.h"
#include "base/memory/stack_allocated.h"
#include "base/notreached.h"
#include "third_party/blink/public/mojom/scroll/scroll_into_view_params.mojom-blink-forward.h"
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/layout/geometry/physical_rect.h"
#include "third_party/blink/renderer/core/layout/layout_box_model_object.h"
#include "third_party/blink/renderer/core/layout/layout_object.h"
#include "third_party/blink/renderer/core/layout/min_max_sizes.h"
#include "third_party/blink/renderer/core/layout/min_max_sizes_cache.h"
#include "third_party/blink/renderer/core/layout/overflow_model.h"
#include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
#include "third_party/blink/renderer/core/style/style_overflow_clip_margin.h"
#include "third_party/blink/renderer/platform/graphics/overlay_scrollbar_clip_behavior.h"
#include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_set.h"

namespace blink {

class AnchorPositionScrollData;
class BlockBreakToken;
class ColumnSpannerPath;
class ConstraintSpace;
class CustomLayoutChild;
class EarlyBreak;
class Element;
class GapGeometry;
class LayoutResult;
class MeasureCache;
class PhysicalBoxFragment;
class ShapeOutsideInfo;
class WritingModeConverter;
enum class LayoutCacheStatus;
struct FragmentGeometry;
struct NonOverflowingScrollRange;
struct PaintInfo;
struct PhysicalBoxStrut;

enum BackgroundRectType {
  kBackgroundPaintedExtent,
  kBackgroundKnownOpaqueRect,
};

enum ShouldClampToContentBox { kDoNotClampToContentBox, kClampToContentBox };

enum ShouldIncludeScrollbarGutter {
  kExcludeScrollbarGutter,
  kIncludeScrollbarGutter
};

struct LayoutBoxRareData final : public GarbageCollected<LayoutBoxRareData> {
 public:
  LayoutBoxRareData();
  LayoutBoxRareData(const LayoutBoxRareData&) = delete;
  LayoutBoxRareData& operator=(const LayoutBoxRareData&) = delete;

  void Trace(Visitor* visitor) const;

  bool has_override_containing_block_content_logical_width_ : 1;
  bool has_previous_content_box_rect_ : 1;

  LayoutUnit override_containing_block_content_logical_width_;

  // Used by BoxPaintInvalidator. Stores the previous content rect after the
  // last paint invalidation. It's valid if has_previous_content_box_rect_ is
  // true.
  PhysicalRect previous_physical_content_box_rect_;

  // Used by CSSLayoutDefinition::Instance::Layout. Represents the script
  // object for this box that web developers can query style, and perform
  // layout upon. Only created if IsCustomItem() is true.
  Member<CustomLayoutChild> layout_child_;

  // Used by BoxPaintInvalidator. Stores one entry for each fragment, holding
  // the previous gap geometry for that fragment, or nullptr for fragments
  // that had no gap geometry.
  Member<GCedHeapVector<Member<const GapGeometry>>> previous_gap_geometries_;
};

// LayoutBox implements the full CSS box model.
//
// LayoutBoxModelObject only introduces some abstractions for LayoutInline and
// LayoutBox. The logic for the model is in LayoutBox, e.g. the storage for the
// rectangle and offset forming the CSS box (frame_location_ and frame_size_)
// and the getters for the different boxes.
//
// LayoutBox is also the uppermost class to support scrollbars, however the
// logic is delegated to PaintLayerScrollableArea.
// Per the CSS specification, scrollbars should "be inserted between the inner
// border edge and the outer padding edge".
// (see http://www.w3.org/TR/CSS21/visufx.html#overflow)
// Also the scrollbar width / height are removed from the content box. Taking
// the following example:
//
// <!DOCTYPE html>
// <style>
// ::-webkit-scrollbar {
//     /* Force non-overlay scrollbars */
//     width: 10px;
//     height: 20px;
// }
// </style>
// <div style="overflow:scroll; width: 100px; height: 100px">
//
// The <div>'s content box is not 100x100 as specified in the style but 90x80 as
// we remove the scrollbars from the box.
//
// The presence of scrollbars is determined by the 'overflow' property and can
// be conditioned on having scrollable overflow (see OverflowModel for more
// details on how we track overflow).
//
// There are 2 types of scrollbars:
// - non-overlay scrollbars take space from the content box.
// - overlay scrollbars don't and just overlay hang off from the border box,
//   potentially overlapping with the padding box's content.
// For more details on scrollbars, see PaintLayerScrollableArea.
//
//
// ***** THE BOX MODEL *****
// The CSS box model is based on a series of nested boxes:
// http://www.w3.org/TR/CSS21/box.html
//
//       |----------------------------------------------------|
//       |                                                    |
//       |                   margin-top                       |
//       |                                                    |
//       |     |-----------------------------------------|    |
//       |     |                                         |    |
//       |     |             border-top                  |    |
//       |     |                                         |    |
//       |     |    |--------------------------|----|    |    |
//       |     |    |                          |    |    |    |
//       |     |    |       padding-top        |####|    |    |
//       |     |    |                          |####|    |    |
//       |     |    |    |----------------|    |####|    |    |
//       |     |    |    |                |    |    |    |    |
//       | ML  | BL | PL |  content box   | PR | SW | BR | MR |
//       |     |    |    |                |    |    |    |    |
//       |     |    |    |----------------|    |    |    |    |
//       |     |    |                          |    |    |    |
//       |     |    |      padding-bottom      |    |    |    |
//       |     |    |--------------------------|----|    |    |
//       |     |    |                      ####|    |    |    |
//       |     |    |     scrollbar height ####| SC |    |    |
//       |     |    |                      ####|    |    |    |
//       |     |    |-------------------------------|    |    |
//       |     |                                         |    |
//       |     |           border-bottom                 |    |
//       |     |                                         |    |
//       |     |-----------------------------------------|    |
//       |                                                    |
//       |                 margin-bottom                      |
//       |                                                    |
//       |----------------------------------------------------|
//
// BL = border-left
// BR = border-right
// ML = margin-left
// MR = margin-right
// PL = padding-left
// PR = padding-right
// SC = scroll corner (contains UI for resizing (see the 'resize' property)
// SW = scrollbar width
//
// Note that the vertical scrollbar (if existing) will be on the left in
// right-to-left direction and horizontal writing-mode. The horizontal scrollbar
// (if existing) is always at the bottom.
//
// Those are just the boxes from the CSS model. Extra boxes are tracked by Blink
// (e.g. the overflows). Thus it is paramount to know which box a function is
// manipulating. Also of critical importance is the coordinate system used (see
// the COORDINATE SYSTEMS section in LayoutBoxModelObject).
class CORE_EXPORT LayoutBox : public LayoutBoxModelObject {
 public:
  explicit LayoutBox(ContainerNode*);
  void Trace(Visitor*) const override;

  // Just use `LayoutObject::IsInline` instead.
  bool IsAtomicInline() const = delete;
  bool IsNonAtomicInline() const = delete;

  PaintLayerType LayerTypeRequired() const override;

  bool BackgroundIsKnownToBeOpaqueInRect(
      const PhysicalRect& local_rect) const override;

  virtual bool BackgroundShouldAlwaysBeClipped() const {
    NOT_DESTROYED();
    return false;
  }

  // Return true if changes to transforms may require layout.
  //
  // This is the case for anchors that are affected by transforms, as that may
  // affect anything that is anchored to it.
  bool TransformsChangeMayRequireLayout() const;

  // Returns the LogicalRect of this box for LocationContainer()'s writing-mode.
  // The coordinate origin is the border corner of the LocationContainer().
  // This function doesn't take into account of TextDirection.
  LogicalRect LogicalRectInContainer() const;

  // Returns the inline-size for this box's writing-mode.  It might be
  // different from container's writing-mode.
  LayoutUnit LogicalWidth() const {
    NOT_DESTROYED();
    PhysicalSize size = StitchedSize();
    return StyleRef().IsHorizontalWritingMode() ? size.width : size.height;
  }
  // Returns the block-size for this box's writing-mode.  It might be
  // different from container's writing-mode.
  LayoutUnit LogicalHeight() const {
    NOT_DESTROYED();
    PhysicalSize size = StitchedSize();
    return StyleRef().IsHorizontalWritingMode() ? size.height : size.width;
  }

  LayoutUnit StitchedBlockSize() const;

  // Return the size of all fragments stitched together in the block direction.
  //
  // <div style="columns:2; column-fill:auto; height:100px;">
  //   <div id="box" style="width:80px;">
  //     <div style="height:70px;"></div>
  //     <div style="height:70px;"></div>
  //   </div>
  // </div>
  //
  // #box creates two fragments, one in each column:
  //   First fragment: PhysicalSize(80, 100)
  //   Second fragment: PhysicalSize(80, 40)
  //
  // This will return PhysicalSize(80, 140).
  virtual PhysicalSize StitchedSize() const;

  void SetLocation(PhysicalOffset location) {
    NOT_DESTROYED();
    if (location == frame_location_) {
      return;
    }
    frame_location_ = location;
    LocationChanged();
  }

  // The ancestor box that this object's PhysicalLocation is relative to.
  virtual LayoutBox* LocationContainer() const;

  // Note that those functions have their origin at this box's CSS border box.
  // As such their location doesn't account for 'top'/'left'.
  PhysicalRect PhysicalBorderBoxRect() const {
    NOT_DESTROYED();
    return PhysicalRect(PhysicalOffset(), StitchedSize());
  }

  enum ContractionEdge {
    kContractToPaddingEdge,
    kContractToContentEdge,
  };
  PhysicalRect PhysicalContractedBoxRect(ContractionEdge) const;

  // Get the padding box rectangle (same as "client rect").
  PhysicalRect PhysicalPaddingBoxRect() const {
    NOT_DESTROYED();
    return PhysicalContractedBoxRect(kContractToPaddingEdge);
  }
  // Get the content box rectangle.
  PhysicalRect PhysicalContentBoxRect() const {
    NOT_DESTROYED();
    return PhysicalContractedBoxRect(kContractToContentEdge);
  }

  LayoutUnit ContentLogicalWidth() const {
    NOT_DESTROYED();
    const PhysicalSize size = PhysicalContentBoxRect().size;
    return IsHorizontalWritingMode() ? size.width : size.height;
  }

  // The content box converted to absolute coords (taking transforms into
  // account).
  gfx::QuadF AbsoluteContentQuad(MapCoordinatesFlags = {}) const;

  // The enclosing rectangle of the background with given opacity requirement.
  PhysicalRect PhysicalBackgroundRect(BackgroundRectType) const;

  void AddOutlineRects(OutlineRectCollector&,
                       OutlineInfo*,
                       const PhysicalOffset& additional_offset,
                       OutlineType) const override;

  bool CanResize() const;

  PhysicalRect ScrollableOverflowRect() const {
    NOT_DESTROYED();
    return ScrollableOverflowIsSet()
               ? overflow_->scrollable_overflow->ScrollableOverflowRect()
               : PhysicalPaddingBoxRect();
  }

  PhysicalRect VisualOverflowRect() const final;
  // VisualOverflow has DCHECK for reading before it is computed. These
  // functions pretend there is no visual overflow when it is not computed.
  // TODO(crbug.com/1205708): Audit the usages and fix issues.
#if DCHECK_IS_ON()
  PhysicalRect VisualOverflowRectAllowingUnset() const;
#else
  ALWAYS_INLINE PhysicalRect VisualOverflowRectAllowingUnset() const {
    NOT_DESTROYED();
    return VisualOverflowRect();
  }
#endif

  PhysicalRect SelfVisualOverflowRect() const {
    NOT_DESTROYED();
    return VisualOverflowIsSet()
               ? overflow_->visual_overflow->SelfVisualOverflowRect()
               : PhysicalBorderBoxRect();
  }
  PhysicalRect ContentsVisualOverflowRect() const {
    NOT_DESTROYED();
    return VisualOverflowIsSet()
               ? overflow_->visual_overflow->ContentsVisualOverflowRect()
               : PhysicalRect();
  }

  // These methods don't mean the box *actually* has top/left overflow. They
  // mean that *if* the box overflows, it will overflow to the top/left rather
  // than the bottom/right. This happens when child content is laid out
  // right-to-left (e.g. direction:rtl) or or bottom-to-top (e.g. direction:rtl
  // writing-mode:vertical-rl).
  virtual bool HasTopOverflow() const;
  virtual bool HasLeftOverflow() const;

  // Sets the scrollable-overflow from the current set of layout-results.
  void SetScrollableOverflowFromLayoutResults();

  void AddSelfVisualOverflow(const PhysicalRect& r);
  void AddContentsVisualOverflow(const PhysicalRect& r);
  void UpdateHasSubpixelVisualEffectOutsets(const PhysicalBoxStrut&);

  void ClearVisualOverflow();

  bool CanUseFragmentsForVisualOverflow() const;
  void CopyVisualOverflowFromFragments();

  virtual void UpdateAfterLayout();

  // CSS intrinsic sizing getters.
  // https://drafts.csswg.org/css-sizing-4/#intrinsic-size-override
  LayoutUnit OverrideIntrinsicContentInlineSize() const;
  LayoutUnit OverrideIntrinsicContentBlockSize() const;

  // Returns element-native intrinsic size. Returns kIndefiniteSize if no such
  // size.
  LayoutUnit DefaultIntrinsicContentInlineSize() const;
  LayoutUnit DefaultIntrinsicContentBlockSize(
      bool children_have_geometry) const;

  bool UsesOverlayScrollbars() const;

  // scrollWidth/scrollHeight will be the same as clientWidth/clientHeight
  // unless the object has overflow:hidden/scroll/auto specified and also has
  // overflow. These methods are virtual so that objects like textareas can
  // scroll shadow content (but pretend that they are the objects that are
  // scrolling).

  // Replaced ScrollLeft/Top by using Element::GetLayoutBoxForScrolling to
  // return the correct ScrollableArea.
  // TODO(cathiechen): We should do the same with ScrollWidth|Height .
  virtual LayoutUnit ScrollWidth() const;
  virtual LayoutUnit ScrollHeight() const;

  PhysicalBoxStrut MarginOutsets() const override;

  // Get the scroll marker group associated with this box, if any.
  LayoutBlock* GetScrollMarkerGroup();

  // Get the scroller that owns this scroll marker group.
  LayoutBlock* ScrollerFromScrollMarkerGroup() const;

  void QuadsInAncestorInternal(Vector<gfx::QuadF>&,
                               const LayoutBoxModelObject* ancestor,
                               MapCoordinatesFlags,
                               BoxQuadType) const override;
  gfx::RectF LocalBoundingBoxRectForAccessibility(
      IncludeDescendants include_descendants) const override;

  void LayoutSubtreeRoot();

  void Paint(const PaintInfo&) const override;

  virtual bool IsInSelfHitTestingPhase(HitTestPhase phase) const {
    NOT_DESTROYED();
    return phase == HitTestPhase::kForeground;
  }

  bool HitTestAllPhases(HitTestResult&,
                        const HitTestLocation&,
                        const PhysicalOffset& accumulated_offset) final;

  // Fast check if |NodeAtPoint| may find a hit.
  bool MayIntersect(const HitTestResult& result,
                    const HitTestLocation& hit_test_location,
                    const PhysicalOffset& accumulated_offset) const;
  bool HasHitTestableOverflow() const;

  LayoutUnit OverrideContainingBlockContentLogicalWidth() const;
  bool HasOverrideContainingBlockContentLogicalWidth() const;
  void SetOverrideContainingBlockContentLogicalWidth(LayoutUnit);

  bool HasInlineFragments() const final;
  wtf_size_t FirstInlineFragmentItemIndex() const final;
  void ClearFirstInlineFragmentItemIndex() final;
  void SetFirstInlineFragmentItemIndex(wtf_size_t) final;

  static void InvalidateItems(const LayoutResult&);

  void AddMeasureLayoutResult(const LayoutResult*);
  void SetCachedLayoutResult(const LayoutResult*, wtf_size_t index);

  // Store one layout result (with its physical fragment) at the specified
  // index.
  //
  // If there's already a result at the specified index, use
  // ReplaceLayoutResult() to do the job. Otherwise, use AppendLayoutResult().
  //
  // If it's going to be the last result, we'll also perform any necessary
  // finalization (see FinalizeLayoutResults()), and also delete all the old
  // entries following it (if there used to be more results in a previous
  // layout).
  //
  // In a few specific cases we'll even delete the entries following this
  // result, even if it's *not* going to be the last one. This is necessary when
  // we might read out the layout results again before we've got to the end (OOF
  // block fragmentation, etc.). In all other cases, we'll leave the old results
  // until we're done, as deleting entries will trigger unnecessary paint
  // invalidation. With any luck, we'll end up with the same number of results
  // as the last time, so that paint invalidation might not be necessary.
  void SetLayoutResult(const LayoutResult*, wtf_size_t index);

  // Append one layout result at the end.
  void AppendLayoutResult(const LayoutResult*);

  // Replace a specific layout result. Also perform finalization if it's the
  // last result (see FinalizeLayoutResults()), but this function does not
  // delete any (old) results following this one. Callers should generally use
  // SetLayoutResult() instead of this one, unless they have good reasons not
  // to.
  void ReplaceLayoutResult(const LayoutResult*, wtf_size_t index);

  void ShrinkLayoutResults(wtf_size_t results_to_keep);

  // Perform any finalization needed after all the layout results have been
  // added.
  void FinalizeLayoutResults();

  void RebuildFragmentTreeSpine();

  const LayoutResult* GetCachedLayoutResult(const BlockBreakToken*) const;
  const LayoutResult* GetCachedMeasureResult(
      const ConstraintSpace&,
      std::optional<FragmentGeometry>* fragment_geometry) const;

  // Call in situations where we know that there's at most one fragment. A
  // DCHECK will fail if there are multiple fragments.
  const LayoutResult* GetSingleCachedLayoutResult() const;

  // Retrieves the last (retrieved or set) measure LayoutResult, for
  // unit-testing purposes only.
  const LayoutResult* GetSingleCachedMeasureResultForTesting() const;

  // Returns the last layout result for this block flow with the given
  // constraint space and break token, or null if it is not up-to-date or
  // otherwise unavailable.
  //
  // This method (while determining if the layout result can be reused), *may*
  // calculate the |initial_fragment_geometry| of the node.
  //
  // |out_cache_status| indicates what type of layout pass is required.
  //
  // TODO(ikilpatrick): Move this function into BlockNode.
  const LayoutResult* CachedLayoutResult(
      const ConstraintSpace&,
      const BlockBreakToken*,
      const EarlyBreak*,
      const ColumnSpannerPath*,
      std::optional<FragmentGeometry>* initial_fragment_geometry,
      LayoutCacheStatus* out_cache_status);

  using LayoutResultList = HeapVector<Member<const LayoutResult>, 1>;
  class PhysicalFragmentList {
    STACK_ALLOCATED();

   public:
    explicit PhysicalFragmentList(const LayoutResultList& layout_results)
        : layout_results_(layout_results) {}

    wtf_size_t Size() const { return layout_results_.size(); }
    bool IsEmpty() const { return layout_results_.empty(); }

    bool MayHaveFragmentItems() const;
    bool HasFragmentItems() const {
      return MayHaveFragmentItems() && SlowHasFragmentItems();
    }
    bool SlowHasFragmentItems() const;

    wtf_size_t IndexOf(const PhysicalBoxFragment& fragment) const;
    bool Contains(const PhysicalBoxFragment& fragment) const;

    // Note: We can't use std::views.  It's banned in Chromium.
    class CORE_EXPORT Iterator {
      STACK_ALLOCATED();

     public:
      using iterator_category = std::forward_iterator_tag;
      using value_type = PhysicalBoxFragment;
      using difference_type = std::ptrdiff_t;
      using pointer = PhysicalBoxFragment*;
      using reference = PhysicalBoxFragment&;

      constexpr Iterator() = default;
      explicit Iterator(const LayoutResultList::const_iterator& iterator)
          : iterator_(iterator) {}

      const PhysicalBoxFragment& operator*() const;

      UNSAFE_BUFFER_USAGE Iterator& operator++() {
        // SAFETY: This is not safe. We should not use this operator directly.
        UNSAFE_BUFFERS(++iterator_);
        return *this;
      }
      UNSAFE_BUFFER_USAGE Iterator operator++(int) {
        Iterator copy = *this;
        // SAFETY: This is not safe. We should not use this operator directly.
        UNSAFE_BUFFERS(++*this);
        return copy;
      }

      bool operator==(const Iterator& other) const {
        return iterator_ == other.iterator_;
      }

     private:
      LayoutResultList::const_iterator iterator_;
    };

    Iterator begin() const { return Iterator(layout_results_.begin()); }
    Iterator end() const { return Iterator(layout_results_.end()); }

    const PhysicalBoxFragment& front() const;
    const PhysicalBoxFragment& back() const;

   private:
    const LayoutResultList& layout_results_;
  };

  PhysicalFragmentList PhysicalFragments() const {
    NOT_DESTROYED();
    return PhysicalFragmentList(layout_results_);
  }
  const LayoutResult* GetLayoutResult(wtf_size_t i) const;
  const LayoutResultList& GetLayoutResults() const {
    NOT_DESTROYED();
    return layout_results_;
  }
  const PhysicalBoxFragment* GetPhysicalFragment(wtf_size_t i) const;
  const FragmentData* FragmentDataFromPhysicalFragment(
      const PhysicalBoxFragment&) const;
  wtf_size_t PhysicalFragmentCount() const {
    NOT_DESTROYED();
    return layout_results_.size();
  }

  bool IsFragmentLessBox() const final {
    NOT_DESTROYED();
    return !PhysicalFragmentCount();
  }

  bool IsValidColumnSpannerInTree() const final {
    NOT_DESTROYED();
    return IsValidColumnSpannerInTree(StyleRef());
  }

  // Provide a ComputedStyle argument, so that this function may be used
  // reliably during style changes.
  bool IsValidColumnSpannerInTree(const ComputedStyle&) const;

  // Return true if this box in itself is a valid column spanner, without
  // checking the ancestry whether it will actually become one. In order to
  // return true, `column-span` needs to be `all` and there are also certain
  // additional requirements to the box itself.
  bool IsSelfValidColumnSpanner(const ComputedStyle&) const;
  bool IsSelfValidColumnSpanner() const {
    NOT_DESTROYED();
    return IsSelfValidColumnSpanner(StyleRef());
  }

  // Return true if the ancestry between this box and the nearest multicol
  // container allows column spanners. Among other things, this box needs to be
  // in the block formatting context established by the columns, and there may
  // not be any transforms on the path. Note that this function doesn't care if
  // this box itself is `column-span:all` or not. It just checks if the ancestry
  // would allow for spanners at this location.
  bool DoesAncestryAllowColumnSpanner(const ComputedStyle&) const;
  bool DoesAncestryAllowColumnSpanner() const {
    NOT_DESTROYED();
    return DoesAncestryAllowColumnSpanner(StyleRef());
  }

  // Return true if this box prevents descendants from becoming column spanners.
  // This only performs checks on the box itself, and does not care whether or
  // not the box is inside an ancestry that allows spanners.
  bool ShouldPreventColumnSpannerDescendants() const;

  // Mark (any) column spanner candidate descendants for layout. Descendants
  // with `column-span:all` may have become valid spanners, because this box no
  // longer prevents them from becoming a spanner (e.g. if a box used to
  // establish a transform, but not anymore (transforms disqualify descendants
  // from becoming spanners). Similarly, descendants with `column-span:all` that
  // were previously valid spanners, may now have become invalid, because this
  // box now e.g. sets a transform.
  void MarkColumnSpannerCandidatesForLayoutIfNeeded();

  bool MapToVisualRectInAncestorSpaceInternal(
      const LayoutBoxModelObject* ancestor,
      TransformState&,
      VisualRectFlags) const override;

  LayoutUnit ContainingBlockLogicalHeightForRelPositioned() const;

  LayoutUnit ContainingBlockLogicalWidthForContent() const override;

  // Return both scrollbars and scrollbar gutters (defined by scrollbar-gutter).
  inline PhysicalBoxStrut ComputeScrollbars() const {
    NOT_DESTROYED();
    if (CanSkipComputeScrollbars())
      return PhysicalBoxStrut();
    else
      return ComputeScrollbarsInternal();
  }
  inline BoxStrut ComputeLogicalScrollbars() const {
    NOT_DESTROYED();
    if (CanSkipComputeScrollbars()) {
      return BoxStrut();
    } else {
      return ComputeScrollbarsInternal().ConvertToLogical(
          StyleRef().GetWritingDirection());
    }
  }

  bool IsUserScrollable() const;
  // Scrolls this box to reveal the specified position during autoscroll
  // (e.g., during drag selection). Returns true if any scrolling occurred.
  virtual bool Autoscroll(const PhysicalOffset&);
  PhysicalOffset CalculateAutoscrollDirection(
      const gfx::PointF& point_in_root_frame) const;
  static LayoutBox* FindAutoscrollable(LayoutObject*,
                                       bool is_middle_click_autoscroll);
  static bool HasHorizontallyScrollableAncestor(LayoutObject*);

  DISABLE_CFI_PERF bool HasAutoVerticalScrollbar() const {
    NOT_DESTROYED();
    return HasNonVisibleOverflow() && StyleRef().HasAutoVerticalScroll();
  }
  DISABLE_CFI_PERF bool HasAutoHorizontalScrollbar() const {
    NOT_DESTROYED();
    return HasNonVisibleOverflow() && StyleRef().HasAutoHorizontalScroll();
  }
  DISABLE_CFI_PERF bool ScrollsOverflow() const {
    NOT_DESTROYED();
    return HasNonVisibleOverflow() && StyleRef().ScrollsOverflow();
  }
  // We place block-direction scrollbar on the left only if the writing-mode
  // is horizontal, so ShouldPlaceVerticalScrollbarOnLeft() is the same as
  // ShouldPlaceBlockDirectionScrollbarOnLogicalLeft(). The two forms can be
  // used in different contexts, e.g. the former for physical coordinate
  // contexts, and the later for logical coordinate contexts.
  bool ShouldPlaceVerticalScrollbarOnLeft() const {
    NOT_DESTROYED();
    return ShouldPlaceBlockDirectionScrollbarOnLogicalLeft();
  }
  virtual bool ShouldPlaceBlockDirectionScrollbarOnLogicalLeft() const {
    NOT_DESTROYED();
    return StyleRef().ShouldPlaceBlockDirectionScrollbarOnLogicalLeft();
  }

  bool HasScrollableOverflowX() const {
    NOT_DESTROYED();
    return ScrollsOverflowX() &&
           ScrollWidth() != PhysicalPaddingBoxRect().Width();
  }
  bool HasScrollableOverflowY() const {
    NOT_DESTROYED();
    return ScrollsOverflowY() &&
           ScrollHeight() != PhysicalPaddingBoxRect().Height();
  }
  bool ScrollsOverflowX() const {
    NOT_DESTROYED();
    return HasNonVisibleOverflow() && StyleRef().ScrollsOverflowX();
  }
  bool ScrollsOverflowY() const {
    NOT_DESTROYED();
    return HasNonVisibleOverflow() && StyleRef().ScrollsOverflowY();
  }

  // Return true if this box is monolithic, i.e. unbreakable in a fragmentation
  // context.
  virtual bool IsMonolithic() const;

  bool HasUnsplittableScrollingOverflow() const;

  PhysicalRect LocalCaretRect(int caret_offset,
                              CaretShape caret_shape) const override;

  // Returns the intersection of all overflow clips which apply.
  PhysicalRect OverflowClipRect() const {
    NOT_DESTROYED();
    return OverflowClipRect(kIgnoreOverlayScrollbarSize);
  }
  virtual PhysicalRect OverflowClipRect(OverlayScrollbarClipBehavior) const;
  virtual PhysicalRect OverflowClipRectForScrollNode() const;
  PhysicalRect CSSClipRect() const;

  // The outsets from this box's border-box that the element's content should be
  // clipped to, including overflow-clip-margin.
  PhysicalBoxStrut BorderOutsetsForClipping() const;

  // Returns the combination of overflow clip, contain: paint clip and CSS clip
  // for this object.
  PhysicalRect ClippingRect() const;

  void ImageChanged(WrappedImagePtr, CanDeferInvalidation) override;
  ResourcePriority ComputeResourcePriority() const override;

  PositionWithAffinity PositionForPointInFragments(const PhysicalOffset&) const;

  virtual bool CreatesNewFormattingContext() const {
    NOT_DESTROYED();
    return true;
  }

  // Returns true if this object is a form-control element (excluding
  // <fieldset>) or a fallback image.
  bool IsSemiReplaced() const;

  // Return true if this block establishes a fragmentation context root (e.g. a
  // multicol container).
  virtual bool IsFragmentationContextRoot() const {
    NOT_DESTROYED();
    return false;
  }

  bool IsWritingModeRoot() const {
    NOT_DESTROYED();
    return !Parent() ||
           Parent()->StyleRef().GetWritingMode() != StyleRef().GetWritingMode();
  }

  bool IsCustomItem() const;

  bool IsFlexItem() const {
    NOT_DESTROYED();
    return !IsInline() && !IsOutOfFlowPositioned() && Parent() &&
           Parent()->IsFlexibleBox();
  }

  bool IsGridItem() const {
    NOT_DESTROYED();
    return Parent() && Parent()->IsLayoutGrid();
  }

  bool IsGridLanesItem() const {
    NOT_DESTROYED();
    return Parent() && Parent()->IsLayoutGridLanes();
  }

  bool IsMathItem() const {
    NOT_DESTROYED();
    return Parent() && Parent()->IsMathML();
  }

  PhysicalOffset OffsetPoint(const Element* parent) const final;

  // Create a new WritingModeConverter to handle offsets and rectangles inside
  // this container. This ignores TextDirection.
  WritingModeConverter CreateWritingModeConverter() const;

  virtual PhysicalOffset PhysicalLocation() const;

  PhysicalRect BoundingBoxRelativeToFirstFragment() const override;

  bool HasSelfVisualOverflow() const {
    NOT_DESTROYED();
    return VisualOverflowIsSet() &&
           !PhysicalBorderBoxRect().Contains(
               overflow_->visual_overflow->SelfVisualOverflowRect());
  }

  bool HasVisualOverflow() const {
    NOT_DESTROYED();
    return VisualOverflowIsSet();
  }
  bool HasScrollableOverflow() const {
    NOT_DESTROYED();
    return ScrollableOverflowIsSet();
  }

  // Returns true if reading flow should be used on this LayoutBox's content.
  // https://drafts.csswg.org/css-display-4/#reading-flow
  bool IsReadingFlowContainer() const;
  // Returns the nodes corresponding to this LayoutBox's layout children,
  // sorted in reading flow if IsReadingFlowContainer().
  const GCedHeapVector<Member<Node>>& ReadingFlowNodes() const;

  // See README.md for an explanation of scroll origin.
  gfx::Vector2d OriginAdjustmentForScrollbars() const;
  gfx::Point ScrollOrigin() const;
  PhysicalOffset ScrolledContentOffset() const;

  // Scroll offset as snapped to physical pixels. This value should be used in
  // any values used after layout and inside "layout code" that cares about
  // where the content is displayed, rather than what the ideal offset is. For
  // most other cases ScrolledContentOffset is probably more appropriate. This
  // is the offset that's actually drawn to the screen.
  // TODO(crbug.com/962299): Pixel-snapping before PrePaint (when we know the
  // paint offset) is incorrect.
  gfx::Vector2d PixelSnappedScrolledContentOffset() const;

  // Maps from scrolling contents space to box space and apply overflow
  // clip if needed. Returns true if no clipping applied or the flattened quad
  // bounds actually intersects the clipping region. If edgeInclusive is true,
  // then this method may return true even if the resulting rect has zero area.
  //
  // When applying offsets and not clips, the TransformAccumulation is
  // respected. If there is a clip, the TransformState is flattened first.
  bool MapContentsRectToBoxSpace(TransformState&,
                                 TransformState::TransformAccumulation,
                                 const LayoutObject& contents,
                                 VisualRectFlags = {}) const;

  // True if the contents scroll relative to this object. |this| must be a
  // containing block for |contents|.
  bool ContainedContentsScroll(const LayoutObject& contents) const;

  // Applies the box clip. This is like mapScrollingContentsRectToBoxSpace,
  // except it does not apply scroll.
  bool ApplyBoxClips(TransformState&,
                     TransformState::TransformAccumulation,
                     VisualRectFlags) const;

  // The optional |size| parameter is used if the size of the object isn't
  // correct yet.
  gfx::PointF PerspectiveOrigin(const PhysicalSize* size = nullptr) const;

  // Maps the visual rect state |transform_state| from this box into its
  // container, applying adjustments for the given container offset,
  // scrolling, container clipping, and transform (including container
  // perspective).
  bool MapVisualRectToContainer(const LayoutObject* container_object,
                                const PhysicalOffset& container_offset,
                                const LayoutObject* ancestor,
                                VisualRectFlags,
                                TransformState&) const;

  virtual LayoutBox* CreateAnonymousBoxWithSameTypeAs(
      const LayoutObject*) const {
    NOT_DESTROYED();
    NOTREACHED();
  }

  // Get the LayoutBox for the actual content. That's usually `this`, but if the
  // element creates multiple boxes (e.g. fieldsets and their anonymous content
  // child box), it may return something else. The box returned will be the one
  // that's created according to display type, scrollable overflow, and so on.
  virtual LayoutBox* ContentLayoutBox() {
    NOT_DESTROYED();
    return this;
  }

  ShapeOutsideInfo* GetShapeOutsideInfo() const;

  // CustomLayoutChild only exists if this LayoutBox is a IsCustomItem (aka. a
  // child of a LayoutCustom). This is created/destroyed when this LayoutBox is
  // inserted/removed from the layout tree.
  CustomLayoutChild* GetCustomLayoutChild() const;
  void AddCustomLayoutChildIfNeeded();
  void ClearCustomLayoutChild();

  bool HitTestOverflowControl(HitTestResult&,
                              const HitTestLocation&,
                              const PhysicalOffset&) const;

  // Returns true if the box intersects the viewport visible to the user.
  bool IntersectsVisibleViewport() const;

  void EnsureIsReadyForPaintInvalidation() override;
  void ClearPaintFlags() override;


  class MutableForPainting : public LayoutObject::MutableForPainting {
   public:
    void SavePreviousSize() {
      GetLayoutBox().previous_size_ = GetLayoutBox().StitchedSize();
    }
    void ClearPreviousSize() { GetLayoutBox().previous_size_ = PhysicalSize(); }
    void SavePreviousOverflowData();
    void ClearPreviousOverflowData() {
      DCHECK(!GetLayoutBox().HasVisualOverflow());
      DCHECK(!GetLayoutBox().HasScrollableOverflow());
      GetLayoutBox().overflow_ = nullptr;
    }
    void SavePreviousContentBoxRect() {
      auto& rare_data = GetLayoutBox().EnsureRareData();
      rare_data.has_previous_content_box_rect_ = true;
      rare_data.previous_physical_content_box_rect_ =
          GetLayoutBox().PhysicalContentBoxRect();
    }
    void ClearPreviousContentBoxRect() {
      if (auto* rare_data = GetLayoutBox().rare_data_.Get())
        rare_data->has_previous_content_box_rect_ = false;
    }

    void SavePreviousGapGeometries();
    void ClearPreviousGapGeometries();

    // Called from LayoutShiftTracker when we attach this LayoutBox to a node
    // for which we saved these values when the node was detached from its
    // original LayoutBox.
    void SetPreviousGeometryForLayoutShiftTracking(
        const PhysicalOffset& paint_offset,
        const PhysicalSize& size,
        const PhysicalRect& visual_overflow_rect);

    void UpdateBackgroundPaintLocation(bool needs_root_element_group);

   protected:
    friend class LayoutBox;
    MutableForPainting(const LayoutBox& box)
        : LayoutObject::MutableForPainting(box) {}
    LayoutBox& GetLayoutBox() {
      return static_cast<LayoutBox&>(layout_object_);
    }
  };

  MutableForPainting GetMutableForPainting() const {
    NOT_DESTROYED();
    return MutableForPainting(*this);
  }

  PhysicalSize PreviousSize() const {
    NOT_DESTROYED();
    return previous_size_;
  }
  PhysicalRect PreviousPhysicalContentBoxRect() const {
    NOT_DESTROYED();
    return rare_data_ && rare_data_->has_previous_content_box_rect_
               ? rare_data_->previous_physical_content_box_rect_
               : PhysicalRect(PhysicalOffset(), PreviousSize());
  }
  PhysicalRect PreviousVisualOverflowRect() const {
    NOT_DESTROYED();
    return overflow_ && overflow_->previous_overflow_data
               ? overflow_->previous_overflow_data
                     ->previous_visual_overflow_rect
               : PhysicalRect(PhysicalOffset(), PreviousSize());
  }
  PhysicalRect PreviousScrollableOverflowRect() const {
    NOT_DESTROYED();
    return overflow_ && overflow_->previous_overflow_data
               ? overflow_->previous_overflow_data
                     ->previous_scrollable_overflow_rect
               : PhysicalRect(PhysicalOffset(), PreviousSize());
  }
  PhysicalRect PreviousSelfVisualOverflowRect() const {
    NOT_DESTROYED();
    return overflow_ && overflow_->previous_overflow_data
               ? overflow_->previous_overflow_data
                     ->previous_self_visual_overflow_rect
               : PhysicalRect(PhysicalOffset(), PreviousSize());
  }

  const GCedHeapVector<Member<const GapGeometry>>* PreviousGapGeometries()
      const {
    NOT_DESTROYED();
    return rare_data_ ? rare_data_->previous_gap_geometries_.Get() : nullptr;
  }

  // Returns the cached intrinsic logical widths when no children depend on the
  // block constraints.
  MinMaxSizesResult CachedIndefiniteIntrinsicLogicalWidths() const {
    NOT_DESTROYED();
    DCHECK(!IntrinsicLogicalWidthsDirty());
    return {intrinsic_logical_widths_,
            IntrinsicLogicalWidthsDependsOnBlockConstraints()};
  }

  // Returns the cached intrinsic logical widths if the initial block-size
  // matches.
  std::optional<MinMaxSizesResult> CachedIntrinsicLogicalWidths(
      LayoutUnit initial_block_size) const {
    NOT_DESTROYED();
    DCHECK(!IntrinsicLogicalWidthsDirty());
    if (initial_block_size == kIndefiniteSize) {
      if (IndefiniteIntrinsicLogicalWidthsDirty()) {
        return std::nullopt;
      }
      return MinMaxSizesResult(
          intrinsic_logical_widths_,
          IntrinsicLogicalWidthsDependsOnBlockConstraints());
    }
    if (min_max_sizes_cache_) {
      if (DefiniteIntrinsicLogicalWidthsDirty()) {
        return std::nullopt;
      }
      return min_max_sizes_cache_->Find(initial_block_size);
    }
    return std::nullopt;
  }

  // Sets the min/max sizes for this box.
  void SetIntrinsicLogicalWidths(LayoutUnit initial_block_size,
                                 const MinMaxSizesResult& result) {
    NOT_DESTROYED();
    // Write to the "indefinite" cache slot if:
    //  - If the initial block-size is indefinite.
    //  - If we don't have any children which depend on the initial block-size
    //    (it can change and we wouldn't give a different answer).
    if (initial_block_size == kIndefiniteSize ||
        !result.depends_on_block_constraints) {
      intrinsic_logical_widths_ = result.sizes;
      SetIntrinsicLogicalWidthsDependsOnBlockConstraints(
          result.depends_on_block_constraints);
      SetIndefiniteIntrinsicLogicalWidthsDirty(false);
    } else {
      if (!min_max_sizes_cache_) {
        min_max_sizes_cache_ = MakeGarbageCollected<MinMaxSizesCache>();
      } else if (DefiniteIntrinsicLogicalWidthsDirty()) {
        min_max_sizes_cache_->Clear();
      }
      min_max_sizes_cache_->Add(result.sizes, initial_block_size,
                                result.depends_on_block_constraints);
      SetDefiniteIntrinsicLogicalWidthsDirty(false);
    }
    ClearIntrinsicLogicalWidthsDirty();
  }

  // Make it public.
  using LayoutObject::BackgroundIsKnownToBeObscured;

  // Sets the coordinates of find-in-page scrollbar tickmarks, bypassing
  // DocumentMarkerController.  This is used by the PDF plugin.
  void OverrideTickmarks(Vector<gfx::Rect> tickmarks);

  // Issues a paint invalidation on the layout viewport's vertical scrollbar
  // (which is responsible for painting the tickmarks).
  void InvalidatePaintForTickmarks();

  bool MayHaveFragmentItems() const {
    NOT_DESTROYED();
    // When the tree is not clean, `ChildrenInline()` is not reliable.
    return (ChildrenInline() || NeedsLayout()) &&
           PhysicalFragments().MayHaveFragmentItems();
  }
  bool HasFragmentItems() const {
    NOT_DESTROYED();
    // See `MayHaveFragmentItems()`.
    return (ChildrenInline() || NeedsLayout()) &&
           PhysicalFragments().HasFragmentItems();
  }
#if EXPENSIVE_DCHECKS_ARE_ON()
  void CheckMayHaveFragmentItems() const;
#endif

  // Returns true if this box is fixed position and will not move with
  // scrolling. If the caller can pre-calculate |container_for_fixed_position|,
  // it should pass it to avoid recalculation.
  bool IsFixedToView(
      const LayoutObject* container_for_fixed_position = nullptr) const;

  // See StickyPositionScrollingConstraints::constraining_rect.
  PhysicalRect ComputeStickyConstrainingRect() const;

  AnchorPositionScrollData* GetAnchorPositionScrollData() const;
  bool NeedsAnchorPositionScrollAdjustment() const;
  PhysicalOffset AnchorPositionScrollTranslationOffset() const;

  bool AnchorPositionScrollAdjustmentAfectedByViewportScrolling() const;

  bool HasScrollbarGutters(ScrollbarOrientation orientation) const;

  // This should be called when the border-box size of this box is changed.
  void SizeChanged();

  // Finds the target anchor element for the given name in the containing block.
  // https://drafts.csswg.org/css-anchor-position-1/#target-anchor-element
  const LayoutObject* FindTargetAnchor(const ScopedCSSName&) const;

  // Returns this element's implicit anchor element if there is one and it is an
  // acceptable anchor element.
  // https://drafts.csswg.org/css-anchor-position-1/#ref-for-valdef-anchor-implicit
  const LayoutObject* AcceptableImplicitAnchor() const;

  const GCedHeapVector<NonOverflowingScrollRange>* NonOverflowingScrollRanges()
      const;

  const BoxStrut& OutOfFlowInsetsForGetComputedStyle() const;

  Element* AccessibilityAnchor() const;
  const GCedHeapHashSet<Member<Element>>* DisplayLocksAffectedByAnchors() const;
  void NotifyContainingDisplayLocksForAnchorPositioning(
      const GCedHeapHashSet<Member<Element>>*
          past_display_locks_affected_by_anchors,
      const GCedHeapHashSet<Member<Element>>* display_locks_affected_by_anchors)
      const;
  bool NeedsAnchorPositionScrollAdjustmentInX() const;
  bool NeedsAnchorPositionScrollAdjustmentInY() const;

  using LayoutObject::GetBackgroundPaintLocation;

 protected:
  ~LayoutBox() override;

  bool IsEligibleForPaintOrLayoutContainment() const override {
    NOT_DESTROYED();
    return true;
  }

  bool IsEligibleForSizeContainment() const override {
    NOT_DESTROYED();
    return true;
  }

  virtual OverflowClipAxes ComputeOverflowClipAxes() const;

  void WillBeDestroyed(const ComputedStyle*) override;

  void InsertedIntoTree() override;
  void WillBeRemovedFromTree() override;

  void StyleWillChange(StyleDifference,
                       const ComputedStyle* old_style,
                       const ComputedStyle& new_style,
                       StyleChangeContext&) override;
  void StyleDidChange(StyleDifference,
                      const ComputedStyle* old_style,
                      const ComputedStyle& new_style,
                      const StyleChangeContext&) override;
  virtual bool ShouldBeHandledAsFloating(const ComputedStyle& style) const;
  bool ShouldBeHandledAsFloating() const {
    NOT_DESTROYED();
    return ShouldBeHandledAsFloating(StyleRef());
  }
  void UpdateFromStyle() override;

  void InLayoutNGInlineFormattingContextWillChange(bool) final;

  PhysicalRect BackgroundPaintedExtent() const;
  virtual bool ForegroundIsKnownToBeOpaqueInRect(
      const PhysicalRect& local_rect,
      unsigned max_depth_to_test) const;
  virtual bool ComputeBackgroundIsKnownToBeObscured() const;
  bool ComputeCanCompositeBackgroundAttachmentFixed() const override;

  void InvalidatePaint(const PaintInvalidatorContext&) const override;

  void ExcludeScrollbars(
      PhysicalRect&,
      OverlayScrollbarClipBehavior = kIgnoreOverlayScrollbarSize,
      ShouldIncludeScrollbarGutter = kIncludeScrollbarGutter) const;

  PhysicalOffset OffsetFromContainerInternal(
      const LayoutObject*,
      MapCoordinatesFlags mode) const override;

  // For atomic inlines, returns its resolved direction in text flow. Not to be
  // confused with the CSS property 'direction'.
  // Returns the CSS 'direction' property value when it is not atomic inline.
  TextDirection ResolvedDirection() const;

  // RecalcScrollableOverflow implementations for LayoutNG.
  RecalcScrollableOverflowResult RecalcScrollableOverflowNG();
  RecalcScrollableOverflowResult RecalcChildScrollableOverflowNG();

 private:
  inline bool ScrollableOverflowIsSet() const {
    NOT_DESTROYED();
    return overflow_ && overflow_->scrollable_overflow;
  }
#if DCHECK_IS_ON()
  void CheckIsVisualOverflowComputed() const;
#else
  ALWAYS_INLINE void CheckIsVisualOverflowComputed() const { NOT_DESTROYED(); }
#endif
  inline bool VisualOverflowIsSet() const {
    NOT_DESTROYED();
    CheckIsVisualOverflowComputed();
    return overflow_ && overflow_->visual_overflow;
  }

  void SetVisualOverflow(const PhysicalRect& self,
                         const PhysicalRect& contents);
  void CopyVisualOverflowFromFragmentsWithoutInvalidations();

  void UpdateShapeOutsideInfoAfterStyleChange(const ComputedStyle&,
                                              const ComputedStyle* old_style);
  void UpdateGridPositionAfterStyleChange(const ComputedStyle*);
  void UpdateScrollSnapMappingAfterStyleChange(const ComputedStyle& old_style);

  LayoutBoxRareData& EnsureRareData() {
    NOT_DESTROYED();
    if (!rare_data_)
      rare_data_ = MakeGarbageCollected<LayoutBoxRareData>();
    return *rare_data_.Get();
  }

  bool IsBox() const final {
    NOT_DESTROYED();
    return true;
  }

  void LocationChanged();

  void InflateVisualRectForFilter(TransformState&) const;
  void InflateVisualRectForFilterUnderContainer(
      TransformState&,
      const LayoutObject& container,
      const LayoutBoxModelObject* ancestor_to_stop_at) const;

  PhysicalRect DebugRect() const override;

  RasterEffectOutset VisualRectOutsetForRasterEffects() const override;

  inline bool CanSkipComputeScrollbars() const {
    NOT_DESTROYED();
    return (StyleRef().IsOverflowVisibleAlongBothAxes() ||
            !HasNonVisibleOverflow() ||
            (GetScrollableArea() &&
             !GetScrollableArea()->HasHorizontalScrollbar() &&
             !GetScrollableArea()->HasVerticalScrollbar())) &&
           StyleRef().IsScrollbarGutterAuto();
  }

  PhysicalBoxStrut ComputeScrollbarsInternal(
      ShouldClampToContentBox = kDoNotClampToContentBox,
      OverlayScrollbarClipBehavior = kIgnoreOverlayScrollbarSize,
      ShouldIncludeScrollbarGutter = kIncludeScrollbarGutter) const;

  bool BackgroundClipBorderBoxIsEquivalentToPaddingBox() const;
  BackgroundPaintLocation ComputeBackgroundPaintLocation(
      bool needs_root_element_group) const;

  // Compute the border-box size from physical fragments.
  PhysicalSize ComputeSize() const;
  void InvalidateCachedGeometry();

  // Clear LayoutObject fields of physical fragments.
  void DisassociatePhysicalFragments();

 protected:
  // The CSS border box rect for this box.
  //
  // The location is the distance from the border edge of the first fragment of
  // this object, to the border edge of the first fragment of
  // LocationContainer(). It doesn't include transforms, relative position
  // offsets etc.
  PhysicalOffset frame_location_;

  // TODO(crbug.com/1353190): Remove frame_size_.
  PhysicalSize frame_size_;

 private:
  // Previous value of frame_size_, updated after paint invalidation.
  PhysicalSize previous_size_;

 protected:
  MinMaxSizes intrinsic_logical_widths_;
  Member<MinMaxSizesCache> min_max_sizes_cache_;

  Member<MeasureCache> measure_cache_;
  LayoutResultList layout_results_;

  friend class LayoutBoxTest;

 private:
  // The index of the first fragment item associated with this object in
  // |FragmentItems::Items()|. Zero means there are no such item.
  // Valid only when IsInLayoutNGInlineFormattingContext().
  wtf_size_t first_fragment_item_index_ = 0u;

  Member<BoxOverflowModel> overflow_;
  Member<LayoutBoxRareData> rare_data_;
};

template <>
struct DowncastTraits<LayoutBox> {
  static bool AllowFrom(const LayoutObject& object) { return object.IsBox(); }
};

inline wtf_size_t LayoutBox::FirstInlineFragmentItemIndex() const {
  NOT_DESTROYED();
  if (!IsInLayoutNGInlineFormattingContext())
    return 0u;
  return first_fragment_item_index_;
}

}  // namespace blink

#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LAYOUT_BOX_H_
