/*
 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
 *           (C) 1999 Antti Koivisto (koivisto@kde.org)
 *           (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com)
 *           (C) 2005, 2006 Samuel Weinig (sam.weinig@gmail.com)
 * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc.
 *               All rights reserved.
 * Copyright (C) 2013 Adobe Systems Incorporated. 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.
 *
 */

#include "third_party/blink/renderer/core/layout/layout_box.h"

#include <math.h>

#include <algorithm>
#include <utility>

#include "base/memory/values_equivalent.h"
#include "cc/input/scroll_snap_data.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/public/platform/web_theme_engine.h"
#include "third_party/blink/public/strings/grit/blink_strings.h"
#include "third_party/blink/renderer/core/css/properties/longhands.h"
#include "third_party/blink/renderer/core/display_lock/display_lock_utilities.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/dom/focus_params.h"
#include "third_party/blink/renderer/core/dom/scroll_marker_group_pseudo_element.h"
#include "third_party/blink/renderer/core/dom/scroll_marker_pseudo_element.h"
#include "third_party/blink/renderer/core/editing/editing_utilities.h"
#include "third_party/blink/renderer/core/editing/ime/input_method_controller.h"
#include "third_party/blink/renderer/core/editing/position_with_affinity.h"
#include "third_party/blink/renderer/core/frame/local_dom_window.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/frame/local_frame_client.h"
#include "third_party/blink/renderer/core/frame/local_frame_view.h"
#include "third_party/blink/renderer/core/frame/settings.h"
#include "third_party/blink/renderer/core/html/canvas/html_canvas_element.h"
#include "third_party/blink/renderer/core/html/forms/html_button_element.h"
#include "third_party/blink/renderer/core/html/forms/html_field_set_element.h"
#include "third_party/blink/renderer/core/html/forms/html_input_element.h"
#include "third_party/blink/renderer/core/html/forms/html_legend_element.h"
#include "third_party/blink/renderer/core/html/forms/html_opt_group_element.h"
#include "third_party/blink/renderer/core/html/forms/html_option_element.h"
#include "third_party/blink/renderer/core/html/forms/html_select_element.h"
#include "third_party/blink/renderer/core/html/forms/html_text_area_element.h"
#include "third_party/blink/renderer/core/html/html_div_element.h"
#include "third_party/blink/renderer/core/html/html_element.h"
#include "third_party/blink/renderer/core/html/html_frame_element_base.h"
#include "third_party/blink/renderer/core/html/html_image_element.h"
#include "third_party/blink/renderer/core/html/shadow/shadow_element_names.h"
#include "third_party/blink/renderer/core/html/shadow/shadow_element_utils.h"
#include "third_party/blink/renderer/core/input/event_handler.h"
#include "third_party/blink/renderer/core/input_type_names.h"
#include "third_party/blink/renderer/core/layout/anchor_position_scroll_data.h"
#include "third_party/blink/renderer/core/layout/box_fragment_builder.h"
#include "third_party/blink/renderer/core/layout/constraint_space.h"
#include "third_party/blink/renderer/core/layout/constraint_space_builder.h"
#include "third_party/blink/renderer/core/layout/custom/custom_layout_child.h"
#include "third_party/blink/renderer/core/layout/custom/layout_custom.h"
#include "third_party/blink/renderer/core/layout/custom/layout_worklet.h"
#include "third_party/blink/renderer/core/layout/custom/layout_worklet_global_scope_proxy.h"
#include "third_party/blink/renderer/core/layout/custom_scrollbar.h"
#include "third_party/blink/renderer/core/layout/disable_layout_side_effects_scope.h"
#include "third_party/blink/renderer/core/layout/forms/layout_fieldset.h"
#include "third_party/blink/renderer/core/layout/forms/layout_text_control.h"
#include "third_party/blink/renderer/core/layout/fragmentation_utils.h"
#include "third_party/blink/renderer/core/layout/gap/gap_geometry.h"
#include "third_party/blink/renderer/core/layout/geometry/box_strut.h"
#include "third_party/blink/renderer/core/layout/geometry/physical_rect.h"
#include "third_party/blink/renderer/core/layout/hit_test_result.h"
#include "third_party/blink/renderer/core/layout/inline/inline_cursor.h"
#include "third_party/blink/renderer/core/layout/layout_box_model_object.h"
#include "third_party/blink/renderer/core/layout/layout_embedded_content.h"
#include "third_party/blink/renderer/core/layout/layout_inline.h"
#include "third_party/blink/renderer/core/layout/layout_object.h"
#include "third_party/blink/renderer/core/layout/layout_object_inlines.h"
#include "third_party/blink/renderer/core/layout/layout_result.h"
#include "third_party/blink/renderer/core/layout/layout_utils.h"
#include "third_party/blink/renderer/core/layout/layout_view.h"
#include "third_party/blink/renderer/core/layout/length_utils.h"
#include "third_party/blink/renderer/core/layout/logical_box_fragment.h"
#include "third_party/blink/renderer/core/layout/measure_cache.h"
#include "third_party/blink/renderer/core/layout/shapes/shape_outside_info.h"
#include "third_party/blink/renderer/core/layout/table/layout_table.h"
#include "third_party/blink/renderer/core/layout/table/layout_table_cell.h"
#include "third_party/blink/renderer/core/layout/text_utils.h"
#include "third_party/blink/renderer/core/loader/resource/image_resource_content.h"
#include "third_party/blink/renderer/core/page/autoscroll_controller.h"
#include "third_party/blink/renderer/core/page/chrome_client.h"
#include "third_party/blink/renderer/core/page/page.h"
#include "third_party/blink/renderer/core/paint/box_paint_invalidator.h"
#include "third_party/blink/renderer/core/paint/object_paint_invalidator.h"
#include "third_party/blink/renderer/core/paint/outline_painter.h"
#include "third_party/blink/renderer/core/paint/paint_layer.h"
#include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
#include "third_party/blink/renderer/core/resize_observer/resize_observer_size.h"
#include "third_party/blink/renderer/core/scroll/scroll_into_view_util.h"
#include "third_party/blink/renderer/core/scroll/scrollbar_theme.h"
#include "third_party/blink/renderer/core/style/computed_style_base_constants.h"
#include "third_party/blink/renderer/core/style/shadow_list.h"
#include "third_party/blink/renderer/core/style/style_overflow_clip_margin.h"
#include "third_party/blink/renderer/platform/geometry/float_rounded_rect.h"
#include "third_party/blink/renderer/platform/geometry/length_functions.h"
#include "third_party/blink/renderer/platform/geometry/physical_offset.h"
#include "third_party/blink/renderer/platform/instrumentation/histogram.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
#include "third_party/blink/renderer/platform/text/platform_locale.h"
#include "third_party/blink/renderer/platform/theme/web_theme_engine_helper.h"
#include "third_party/blink/renderer/platform/wtf/size_assertions.h"
#include "ui/gfx/geometry/quad_f.h"
#include "ui/gfx/geometry/rect_conversions.h"

namespace blink {

using mojom::blink::FormControlType;

// Used by flexible boxes when flexing this element and by table cells.
using OverrideSizeMap = HashMap<const LayoutBox*, LayoutUnit>;

// Size of border belt for autoscroll. When mouse pointer in border belt,
// autoscroll is started.
#if BUILDFLAG(IS_IOS)
static constexpr int kAutoscrollBeltSizeInDIPs = 20;
// Size of border bottom belt for autoscroll on iOS. When users drag the right
// handle of selection on iOS, the Y value of point that passed from
// BETextInput's autoscrollToPoint is smaller that the Y value that users touch.
static constexpr int kAutoscrollBeltSizeInDIPsBottom = 60;
#else
// TODO(crbug.com/398066579): Figure out if these can be in DIPs.
static constexpr int kAutoscrollBeltSize = 20;
static constexpr int kAutoscrollBeltSizeBottom = 20;
#endif  // BUILDFLAG(IS_IOS)

static const unsigned kBackgroundObscurationTestMaxDepth = 4;

struct SameSizeAsLayoutBox : public LayoutBoxModelObject {
  PhysicalOffset frame_location_;
  PhysicalSize frame_size_;
  PhysicalSize previous_size;
  MinMaxSizes intrinsic_logical_widths;
  Member<void*> min_max_sizes_cache;
  Member<void*> cache;
  HeapVector<Member<const LayoutResult>, 1> layout_results;
  wtf_size_t first_fragment_item_index_;
  Member<void*> members[2];
};

ASSERT_SIZE(LayoutBox, SameSizeAsLayoutBox);

namespace {

LayoutUnit TextAreaIntrinsicInlineSize(const HTMLTextAreaElement& textarea,
                                       const LayoutBox& box) {
  // Always add the scrollbar thickness for 'overflow:auto'.
  const auto& style = box.StyleRef();
  int scrollbar_thickness = 0;
  if (style.OverflowBlockDirection() == EOverflow::kScroll ||
      style.OverflowBlockDirection() == EOverflow::kAuto) {
    scrollbar_thickness = layout_text_control::ScrollbarThickness(box);
  }

  return LayoutUnit(ceilf(layout_text_control::GetAvgCharWidth(style) *
                          textarea.cols())) +
         scrollbar_thickness;
}

LayoutUnit TextFieldIntrinsicInlineSize(const HTMLInputElement& input,
                                        const LayoutBox& box) {
  int factor;
  const bool includes_decoration = input.GetSizeWithDecoration(factor);
  if (factor <= 0)
    factor = 20;

  const float char_width = layout_text_control::GetAvgCharWidth(box.StyleRef());
  float float_result = char_width * factor;

  float max_char_width = 0.f;
  const Font* font = box.StyleRef().GetFont();
  if (layout_text_control::HasValidAvgCharWidth(*font)) {
    max_char_width = font->PrimaryFont()->MaxCharWidth();
  }

  // For text inputs, IE adds some extra width.
  if (max_char_width > char_width)
    float_result += max_char_width - char_width;

  LayoutUnit result(ceilf(float_result));
  if (includes_decoration) {
    const auto* spin_button =
        To<HTMLElement>(input.UserAgentShadowRoot()->getElementById(
            shadow_element_names::kIdSpinButton));
    if (LayoutBox* spin_box =
            spin_button ? spin_button->GetLayoutBox() : nullptr) {
      const Length& logical_width = spin_box->StyleRef().LogicalWidth();
      result += spin_box->BorderPaddingInlineSize();
      // Since the width of spin_box is not calculated yet,
      // spin_box->LogicalWidth() returns 0. Use the computed logical
      // width instead.
      if (logical_width.IsPercent()) {
        const float value = logical_width.Percent();
        if (value != 100.f) {
          result += result * value / (100.f - value);
        }
      } else if (logical_width.IsFixed()) {
        result += logical_width.Pixels();
      }
    }
  }
  return result;
}

LayoutUnit TextAreaIntrinsicBlockSize(const HTMLTextAreaElement& textarea,
                                      const LayoutBox& box) {
  // Only add the scrollbar thickness for 'overflow: scroll'.
  int scrollbar_thickness = 0;
  if (box.StyleRef().OverflowInlineDirection() == EOverflow::kScroll) {
    scrollbar_thickness = layout_text_control::ScrollbarThickness(box);
  }

  const auto* inner_editor = textarea.InnerEditorElement();
  const LayoutBox* editor_box =
      inner_editor ? inner_editor->GetLayoutBox() : nullptr;
  const LayoutBox* inner_box =
      editor_box ? DynamicTo<LayoutBox>(editor_box->SlowFirstChild()) : nullptr;

  const LayoutBox& target_box = ([&]() -> const LayoutBox& {
    if (inner_box) {
      return *inner_box;
    }
    if (editor_box) {
      return *editor_box;
    }
    return box;
  })();

  return target_box.FirstLineStyleRef().ComputedLineHeightAsFixed() *
             textarea.rows() +
         scrollbar_thickness;
}

LayoutUnit TextFieldIntrinsicBlockSize(const HTMLInputElement& input,
                                       const LayoutBox& box) {
  const auto* inner_editor = input.InnerEditorElement();
  // inner_editor's LayoutBox can be nullptr because web authors can set
  // display:none to ::-webkit-textfield-decoration-container element.
  const LayoutBox& target_box = (inner_editor && inner_editor->GetLayoutBox())
                                    ? *inner_editor->GetLayoutBox()
                                    : box;
  return target_box.FirstLineStyleRef().ComputedLineHeightAsFixed();
}

LayoutUnit FileUploadControlIntrinsicInlineSize(const HTMLInputElement& input,
                                                const LayoutBox& box) {
  // This should match to margin-inline-end of ::-webkit-file-upload-button UA
  // style.
  constexpr int kAfterButtonSpacing = 4;
  // Figure out how big the filename space needs to be for a given number of
  // characters (using "0" as the nominal character).
  constexpr int kDefaultWidthNumChars = 34;
  constexpr UChar kCharacter = '0';
  const String character_as_string = String(base::span_from_ref(kCharacter));
  const float min_default_label_width =
      kDefaultWidthNumChars *
      ComputeTextWidth(character_as_string, box.StyleRef());

  const String label =
      input.GetLocale().QueryString(IDS_FORM_FILE_NO_FILE_LABEL);
  float default_label_width = ComputeTextWidth(label, box.StyleRef());
  if (HTMLInputElement* button = input.UploadButton()) {
    if (auto* button_box = button->GetLayoutBox()) {
      const ComputedStyle& button_style = button_box->StyleRef();
      WritingMode mode = button_style.GetWritingMode();
      ConstraintSpaceBuilder builder(mode, button_style.GetWritingDirection(),
                                     /* is_new_fc */ true);
      LayoutUnit max = BlockNode(button_box)
                           .ComputeMinMaxSizes(mode, SizeType::kIntrinsic,
                                               builder.ToConstraintSpace())
                           .sizes.max_size;
      default_label_width +=
          max + (kAfterButtonSpacing * box.StyleRef().EffectiveZoom());
    }
  }
  return LayoutUnit(
      ceilf(std::max(min_default_label_width, default_label_width)));
}

LayoutUnit SliderIntrinsicInlineSize(const LayoutBox& box) {
  constexpr int kDefaultTrackLength = 129;
  return LayoutUnit(kDefaultTrackLength * box.StyleRef().EffectiveZoom());
}

LogicalSize ThemePartIntrinsicSize(const LayoutBox& box,
                                   WebThemeEngine::Part part) {
  const auto& style = box.StyleRef();
  PhysicalSize size(
      WebThemeEngineHelper::GetNativeThemeEngine()->GetSize(part));
  size.Scale(style.EffectiveZoom());
  return ToLogicalSize(size, style.GetWritingMode());
}

LayoutUnit ListBoxDefaultItemHeight(const LayoutBox& box) {
  constexpr int kDefaultPaddingBottom = 1;

  const SimpleFontData* font_data = box.StyleRef().GetFont()->PrimaryFont();
  if (!font_data)
    return LayoutUnit();
  return LayoutUnit(font_data->GetFontMetrics().Height() +
                    kDefaultPaddingBottom);
}

// TODO(crbug.com/1040826): This function is written in LayoutObject API
// so that this works in both of the legacy layout and LayoutNG. We
// should have LayoutNG-specific code.
LayoutUnit ListBoxItemBlockSize(const HTMLSelectElement& select,
                                const LayoutBox& box) {
  const auto& items = select.GetListItems();
  if (items.empty() || box.ShouldApplySizeContainment())
    return ListBoxDefaultItemHeight(box);

  LayoutUnit max_block_size;
  for (Element* element : items) {
    if (auto* optgroup = DynamicTo<HTMLOptGroupElement>(element))
      element = &optgroup->OptGroupLabelElement();
    LayoutUnit item_block_size;
    if (auto* layout_box = element->GetLayoutBox()) {
      PhysicalSize size = layout_box->StitchedSize();
      item_block_size =
          box.StyleRef().IsHorizontalWritingMode() ? size.height : size.width;
    } else {
      item_block_size = ListBoxDefaultItemHeight(box);
    }
    max_block_size = std::max(max_block_size, item_block_size);
  }
  return max_block_size;
}

LayoutUnit MenuListIntrinsicInlineSize(const HTMLSelectElement& select,
                                       const LayoutBox& box) {
  const ComputedStyle& style = box.StyleRef();
  float max_option_width = 0;
  if (!box.ShouldApplySizeContainment()) {
    for (const auto& option : select.GetOptionList()) {
      String text =
          style.ApplyTextTransform(option.TextIndentedToRespectGroupLabel());
      // We apply SELECT's style, not OPTION's style because max_option_width is
      // used to determine intrinsic width of the menulist box.
      max_option_width =
          std::max(max_option_width, ComputeTextWidth(text, style));
    }
  }

  if (select.IsMultiple()) {
    // For <select multiple size=1>, we also need to account for the "x
    // selected" text when 0, 2, or more options are selected. Only
    // considering the max number of options is probably good enough.
    max_option_width = std::max(
        max_option_width,
        ComputeTextWidth(select.MultipleOptionsSelectedText(select.length()),
                         style));
  }

  LayoutTheme& theme = LayoutTheme::GetTheme();
  int paddings = theme.PopupInternalPaddingStart(style) +
                 theme.PopupInternalPaddingEnd(box.GetFrame(), style);
  return LayoutUnit(ceilf(max_option_width)) + LayoutUnit(paddings);
}

LayoutUnit MenuListIntrinsicBlockSize(const HTMLSelectElement& select,
                                      const LayoutBox& box) {
  if (!box.StyleRef().HasEffectiveAppearance())
    return kIndefiniteSize;
  const ComputedStyle& style = box.StyleRef();
  const SimpleFontData* font_data = style.GetFont()->PrimaryFont();
  DCHECK(font_data);
  const LayoutBox* inner_box = select.InnerElement().GetLayoutBox();
  LayoutUnit inner_block_size;
  if (inner_box) {
    inner_block_size = inner_box->BorderPaddingBlockSize();
  } else {
    // content-visibility:hidden skips layout for the inner element, but the
    // menulist intrinsic height still includes its themed block padding.
    LayoutTheme& theme = LayoutTheme::GetTheme();
    inner_block_size = LayoutUnit(theme.PopupInternalPaddingTop(style)) +
                       LayoutUnit(theme.PopupInternalPaddingBottom(style));
  }
  return (font_data ? font_data->GetFontMetrics().Height() : 0) +
         inner_block_size;
}

#if DCHECK_IS_ON()
void CheckDidAddFragment(const LayoutBox& box,
                         const PhysicalBoxFragment& new_fragment,
                         wtf_size_t new_fragment_index = kNotFound) {
  // If |HasFragmentItems|, |ChildrenInline()| should be true.
  // |HasFragmentItems| uses this condition to optimize .
  if (new_fragment.HasItems())
    DCHECK(box.ChildrenInline());

  wtf_size_t index = 0;
  for (const PhysicalBoxFragment& fragment : box.PhysicalFragments()) {
    DCHECK_EQ(fragment.IsFirstForNode(), index == 0);
    if (const FragmentItems* fragment_items = fragment.Items()) {
      fragment_items->CheckAllItemsAreValid();
    }
    // Don't check past the fragment just added. Those entries may be invalid at
    // this point.
    if (index == new_fragment_index)
      break;
    ++index;
  }
}
#else
inline void CheckDidAddFragment(const LayoutBox& box,
                                const PhysicalBoxFragment& fragment,
                                wtf_size_t new_fragment_index = kNotFound) {}
#endif

// Applies the overflow clip to |result|. For any axis that is clipped, |result|
// is reset to |no_overflow_rect|. If neither axis is clipped, nothing is
// changed.
void ApplyOverflowClip(OverflowClipAxes overflow_clip_axes,
                       const PhysicalRect& no_overflow_rect,
                       PhysicalRect& result) {
  if (overflow_clip_axes & kOverflowClipX) {
    result.SetX(no_overflow_rect.X());
    result.SetWidth(no_overflow_rect.Width());
  }
  if (overflow_clip_axes & kOverflowClipY) {
    result.SetY(no_overflow_rect.Y());
    result.SetHeight(no_overflow_rect.Height());
  }
}

int HypotheticalScrollbarThickness(const LayoutBox& box,
                                   ScrollbarOrientation scrollbar_orientation) {
  if (PaintLayerScrollableArea* scrollable_area = box.GetScrollableArea()) {
    return scrollable_area->HypotheticalScrollbarThickness(
        scrollbar_orientation, /* should_include_overlay_thickness */ true);
  }
  Page* page = box.GetFrame()->GetPage();
  ScrollbarTheme& theme = page->GetScrollbarTheme();

  ChromeClient& chrome_client = page->GetChromeClient();
  Document& document = box.GetDocument();
  float scale_from_dip =
      chrome_client.WindowToViewportScalar(document.GetFrame(), 1.0f);
  return theme.ScrollbarThickness(scale_from_dip,
                                  box.StyleRef().UsedScrollbarWidth());
}

void RecalcFragmentScrollableOverflow(RecalcScrollableOverflowResult& result,
                                      const PhysicalBoxFragment& fragment) {
  for (const auto& child : fragment.PostLayoutChildren()) {
    if (child->GetLayoutObject()) {
      if (const auto* box = DynamicTo<PhysicalBoxFragment>(child.get())) {
        if (LayoutBox* owner_box = box->MutableOwnerLayoutBox())
          result.Unite(owner_box->RecalcScrollableOverflow());
      }
    } else if (const auto* child_box_fragment =
                   DynamicTo<PhysicalBoxFragment>(child.get())) {
      // We enter this branch when the |child| is a fragmentainer.
      RecalcFragmentScrollableOverflow(result, *child_box_fragment);
    }
  }
}

const PhysicalBoxFragment* FragmentForEdge(const LayoutBox& box,
                                           const PhysicalBoxSides& edges) {
  // Should only be here if there are multiple fragments. There's a fast-path
  // otherwise.
  DCHECK_GT(box.PhysicalFragmentCount(), 1u);

  // One, and only one, edge should be set.
  DCHECK_EQ(edges.top + edges.right + edges.bottom + edges.left, 1);

  LogicalBoxSides logical_edges =
      edges.ToLogical(box.StyleRef().GetWritingDirection());
  if (logical_edges.block_end) {
    // The edge we're looking for is the block-end. It is found in the last
    // non-overflowing fragment.
    for (wtf_size_t idx = box.PhysicalFragmentCount() - 1; idx > 0; idx--) {
      const PhysicalBoxFragment* fragment = box.GetPhysicalFragment(idx);
      if (!FindPreviousBreakToken(*fragment)->IsAtBlockEnd()) {
        return fragment;
      }
    }
  }
  return box.GetPhysicalFragment(0);
}

const PhysicalBoxFragment* FragmentForLeftEdge(const LayoutBox& box) {
  PhysicalBoxSides edges(false);
  edges.left = true;
  return FragmentForEdge(box, edges);
}

const PhysicalBoxFragment* FragmentForRightEdge(const LayoutBox& box) {
  PhysicalBoxSides edges(false);
  edges.right = true;
  return FragmentForEdge(box, edges);
}

const PhysicalBoxFragment* FragmentForTopEdge(const LayoutBox& box) {
  PhysicalBoxSides edges(false);
  edges.top = true;
  return FragmentForEdge(box, edges);
}

const PhysicalBoxFragment* FragmentForBottomEdge(const LayoutBox& box) {
  PhysicalBoxSides edges(false);
  edges.bottom = true;
  return FragmentForEdge(box, edges);
}

}  // namespace

LayoutBoxRareData::LayoutBoxRareData()
    :  // TODO(rego): We should store these based on physical direction.
      has_override_containing_block_content_logical_width_(false),
      has_previous_content_box_rect_(false) {}

void LayoutBoxRareData::Trace(Visitor* visitor) const {
  visitor->Trace(layout_child_);
  visitor->Trace(previous_gap_geometries_);
}

LayoutBox::LayoutBox(ContainerNode* node) : LayoutBoxModelObject(node) {
}

void LayoutBox::Trace(Visitor* visitor) const {
  visitor->Trace(min_max_sizes_cache_);
  visitor->Trace(measure_cache_);
  visitor->Trace(layout_results_);
  visitor->Trace(overflow_);
  visitor->Trace(rare_data_);
  LayoutBoxModelObject::Trace(visitor);
}

LayoutBox::~LayoutBox() = default;

PaintLayerType LayoutBox::LayerTypeRequired() const {
  NOT_DESTROYED();
  if (IsStacked() || HasHiddenBackface() ||
      // A normal flow replaced stacking context is not stacked by itself,
      // but needs a PaintLayer to manage stacked children.
      (RuntimeEnabledFeatures::StackingContextIsNotStackedEnabled() &&
       IsReplacedNormalFlowStackingContext(StyleRef()))) {
    return kNormalPaintLayer;
  }

  if (HasNonVisibleOverflow() && !IsLayoutReplaced()) {
    return kOverflowClipPaintLayer;
  }

  if (IsOverscrollContainer()) {
    return kForcedPaintLayer;
  }

  return kNoPaintLayer;
}

bool LayoutBox::TransformsChangeMayRequireLayout() const {
  for (const PhysicalBoxFragment& fragment : PhysicalFragments()) {
    if (fragment.HasAnchorsToPropagate()) {
      return true;
    }
  }
  return false;
}

void LayoutBox::WillBeDestroyed(const ComputedStyle* style) {
  NOT_DESTROYED();

  ShapeOutsideInfo::RemoveInfo(*this);

  DisassociatePhysicalFragments();

  LayoutBoxModelObject::WillBeDestroyed(style);
}

void LayoutBox::DisassociatePhysicalFragments() {
  NOT_DESTROYED();
  if (FirstInlineFragmentItemIndex()) {
    FragmentItems::LayoutObjectWillBeDestroyed(*this);
    ClearFirstInlineFragmentItemIndex();
  }
  if (measure_cache_) {
    measure_cache_->LayoutObjectWillBeDestroyed();
  }
  for (const auto& result : layout_results_) {
    result->GetPhysicalFragment().LayoutObjectWillBeDestroyed();
  }
}

void LayoutBox::InsertedIntoTree() {
  NOT_DESTROYED();
  LayoutBoxModelObject::InsertedIntoTree();
  AddCustomLayoutChildIfNeeded();
}

void LayoutBox::WillBeRemovedFromTree() {
  NOT_DESTROYED();
  ClearCustomLayoutChild();

  // Notify the display-locks that anchors within a sub-tree may disappear.
  if (Style() && StyleRef().HasOutOfFlowPosition()) {
    NotifyContainingDisplayLocksForAnchorPositioning(
        DisplayLocksAffectedByAnchors(), nullptr);
  }

  LayoutBoxModelObject::WillBeRemovedFromTree();
}

void LayoutBox::StyleWillChange(StyleDifference diff,
                                const ComputedStyle* old_style,
                                const ComputedStyle& new_style,
                                StyleChangeContext& style_change_context) {
  NOT_DESTROYED();
  if (old_style) {
    // When a layout hint happens and an object's position style changes, we
    // have to do a layout to dirty the layout tree using the old position
    // value now.
    if (diff.NeedsFullLayout() && Parent()) {
      if (old_style->GetPosition() != new_style.GetPosition()) {
        MarkContainerChainForLayout();
      }
    }
    style_change_context.did_prevent_spanner_descendants =
        IsInsideMulticol() && ShouldPreventColumnSpannerDescendants();
  }

  LayoutBoxModelObject::StyleWillChange(diff, old_style, new_style,
                                        style_change_context);
}

void LayoutBox::StyleDidChange(StyleDifference diff,
                               const ComputedStyle* old_style,
                               const ComputedStyle& new_style,
                               const StyleChangeContext& style_change_context) {
  NOT_DESTROYED();
  LayoutBoxModelObject::StyleDidChange(diff, old_style, new_style,
                                       style_change_context);

  // Reflection works through PaintLayer. Some child classes e.g. LayoutSVGBlock
  // don't create layers and ignore reflections.
  if (HasReflection() && !HasLayer())
    SetHasReflection(false);

  SetOverflowClipAxes(ComputeOverflowClipAxes());

  // If our zoom factor changes and we have a defined scrollLeft/Top, we need to
  // adjust that value into the new zoomed coordinate space.  Note that the new
  // scroll offset may be outside the normal min/max range of the scrollable
  // area, which is weird but OK, because the scrollable area will update its
  // min/max in updateAfterLayout().
  if (IsScrollContainer() && old_style &&
      old_style->EffectiveZoom() != new_style.EffectiveZoom()) {
    PaintLayerScrollableArea* scrollable_area = GetScrollableArea();
    DCHECK(scrollable_area);
    // We use GetScrollOffset() rather than ScrollPosition(), because scroll
    // offset is the distance from the beginning of flow for the box, which is
    // the dimension we want to preserve.
    ScrollOffset offset = scrollable_area->GetScrollOffset();
    if (!offset.IsZero()) {
      offset.Scale(new_style.EffectiveZoom() / old_style->EffectiveZoom());
      scrollable_area->SetScrollOffsetUnconditionally(offset);
    }
  }

  if (old_style && old_style->IsScrollContainer() != IsScrollContainer()) {
    if (auto* layer = EnclosingLayer())
      layer->ScrollContainerStatusChanged();
  }

  UpdateShapeOutsideInfoAfterStyleChange(new_style, old_style);
  UpdateGridPositionAfterStyleChange(old_style);

  if (old_style) {
    UpdateScrollSnapMappingAfterStyleChange(*old_style);

    if (ShouldClipOverflowAlongEitherAxis()) {
      // The overflow clip paint property depends on border sizes through
      // overflowClipRect(), and border radii, so we update properties on
      // border size or radii change.
      //
      // For some controls, it depends on paddings.
      if (!old_style->BorderSizeEquals(new_style) ||
          diff.border_radius_changed ||
          (diff.border_shape_changed &&
           (new_style.HasBorderShape() || old_style->HasBorderShape())) ||
          (new_style.OverflowClipMargin() &&
           new_style.OverflowClipMargin()->GetReferenceBox() ==
               StyleOverflowClipMargin::ReferenceBox::kContentBox &&
           !old_style->PaddingEqual(new_style))) {
        SetNeedsPaintPropertyUpdate();
      }
    }

    // When box-shadow changes while border-shape is active, force a paint
    // property update to ensure the InnerBorderShapeClip overflow hierarchy is
    // re-evaluated consistently with the visual overflow recomputation.
    if (!base::ValuesEquivalent(old_style->BoxShadow(),
                                new_style.BoxShadow()) &&
        (new_style.HasBorderShape() || old_style->HasBorderShape())) {
      SetNeedsPaintPropertyUpdate();
    }

    if (IsInLayoutNGInlineFormattingContext() && IsInline() &&
        old_style->Direction() != new_style.Direction()) {
      SetNeedsCollectInlines();
    }

    if (IsBackgroundAttachmentFixedObject() &&
        new_style.BackgroundLayers().Clip() !=
            old_style->BackgroundLayers().Clip()) {
      SetNeedsPaintPropertyUpdate();
    }

    bool should_prevent_now =
        IsInsideMulticol() && ShouldPreventColumnSpannerDescendants();
    if (style_change_context.did_prevent_spanner_descendants !=
        should_prevent_now) {
      // Certain styles (transforms, formatting context roots, etc.) prevent
      // column spanners inside. If such styles have now been set, we need to
      // turn now-invalid spanners into regular block-level elements that
      // participate in the fragmentation context. If such styles have now been
      // removed, we need to turn regular block-level elements into now-valid
      // spanners. See https://drafts.csswg.org/css-multicol-1/#spanning-columns
      MarkColumnSpannerCandidatesForLayoutIfNeeded();
    }

    // When a fixed-position element changes between top-relative and
    // bottom-relative positioning, invalidate paint properties to ensure
    // IsAffectedByOuterViewportBoundsDelta() is re-evaluated on the next
    // document lifecycle update. This mirrors the style-level checks in
    // CompositingReasonsForViewportScrollEffect(), and must be kept in sync
    // with that function.
    if (new_style.GetPosition() == EPosition::kFixed) {
      if (old_style->IsFixedToBottom() != new_style.IsFixedToBottom()) {
        SetNeedsPaintPropertyUpdate();
      } else if (new_style.IsFixedToBottom() &&
                 (old_style->IsBottomRelativeToSafeAreaInset() !=
                  new_style.IsBottomRelativeToSafeAreaInset())) {
        SetNeedsPaintPropertyUpdate();
      }
    }
  }

  if (diff.transform_changed && TransformsChangeMayRequireLayout()) {
    SetNeedsLayoutAndFullPaintInvalidation(
        layout_invalidation_reason::kStyleChange);
  }

  if (diff.needs_box_paint_property_update) {
    SetNeedsPaintPropertyUpdate();
  }

  // The background of the root or body element could propagate up to the
  // canvas. Just dirty the entire canvas when our style changes substantially.
  if (diff.NeedsNormalPaintInvalidation()) {
    if (IsDocumentElement() || IsBody()) {
      View()->SetShouldDoFullPaintInvalidation();
    }
  }

  if (diff.NeedsFullLayout()) {
    if (IsValidColumnSpannerInTree(*old_style) !=
        IsValidColumnSpannerInTree(new_style)) {
      MarkParentForSpannerOrOutOfFlowPositionedChange();
    }
  }

  // Update the script style map, from the new computed style.
  if (IsCustomItem())
    GetCustomLayoutChild()->styleMap()->UpdateStyle(GetDocument(), new_style);
}

void LayoutBox::UpdateShapeOutsideInfoAfterStyleChange(
    const ComputedStyle& style,
    const ComputedStyle* old_style) {
  NOT_DESTROYED();
  const ShapeValue* shape_outside = style.ShapeOutside();
  const ShapeValue* old_shape_outside =
      old_style ? old_style->ShapeOutside()
                : ComputedStyleInitialValues::InitialShapeOutside();

  const Length& shape_margin = style.ShapeMargin();
  const Length& old_shape_margin =
      old_style ? old_style->ShapeMargin()
                : ComputedStyleInitialValues::InitialShapeMargin();

  float shape_image_threshold = style.ShapeImageThreshold();
  float old_shape_image_threshold =
      old_style ? old_style->ShapeImageThreshold()
                : ComputedStyleInitialValues::InitialShapeImageThreshold();

  // FIXME: A future optimization would do a deep comparison for equality. (bug
  // 100811)
  if (shape_outside == old_shape_outside && shape_margin == old_shape_margin &&
      shape_image_threshold == old_shape_image_threshold)
    return;

  if (!shape_outside)
    ShapeOutsideInfo::RemoveInfo(*this);
  else
    ShapeOutsideInfo::EnsureInfo(*this).MarkShapeAsDirty();

  if (!IsFloating()) {
    return;
  }

  if (shape_outside || shape_outside != old_shape_outside) {
    if (auto* containing_block = ContainingBlock()) {
      containing_block->SetChildNeedsLayout();
    }
  }
}

namespace {

bool GridStyleChanged(const ComputedStyle* old_style,
                      const ComputedStyle& current_style) {
  return old_style->GridColumnStart() != current_style.GridColumnStart() ||
         old_style->GridColumnEnd() != current_style.GridColumnEnd() ||
         old_style->GridRowStart() != current_style.GridRowStart() ||
         old_style->GridRowEnd() != current_style.GridRowEnd() ||
         old_style->Order() != current_style.Order() ||
         old_style->HasOutOfFlowPosition() !=
             current_style.HasOutOfFlowPosition();
}

bool AlignmentChanged(const ComputedStyle* old_style,
                      const ComputedStyle& current_style) {
  return old_style->AlignSelf() != current_style.AlignSelf() ||
         old_style->JustifySelf() != current_style.JustifySelf();
}

}  // namespace

void LayoutBox::UpdateGridPositionAfterStyleChange(
    const ComputedStyle* old_style) {
  NOT_DESTROYED();

  if (!old_style)
    return;

  LayoutObject* parent = Parent();
  const bool was_out_of_flow = old_style->HasOutOfFlowPosition();
  const bool is_out_of_flow = StyleRef().HasOutOfFlowPosition();

  LayoutBlock* containing_block = ContainingBlock();
  if ((containing_block && containing_block->IsLayoutGridOrGridLanes()) &&
      GridStyleChanged(old_style, StyleRef())) {
    // Out-of-flow items do not impact grid/grid-lanes placement.
    // TODO(kschmi): Scope this so that it only dirties the grid/grid-lanes when
    // track sizing depends on item sizes.
    if (!was_out_of_flow || !is_out_of_flow)
      containing_block->SetGridPlacementDirty(true);

    // For out-of-flow elements with grid/grid-lanes container as containing
    // block, we need to run the entire algorithm to place and size them
    // correctly. As a result, we trigger a full layout.
    if (is_out_of_flow) {
      containing_block->SetNeedsLayout(layout_invalidation_reason::kGridChanged,
                                       kMarkContainerChain);
    }
  }

  // GridNG computes static positions for out-of-flow elements at layout time,
  // with alignment offsets baked in. So if alignment changes, we need to
  // schedule a layout.
  if (is_out_of_flow && parent && AlignmentChanged(old_style, StyleRef())) {
    parent->SetNeedsLayout(
        layout_invalidation_reason::kOutOfFlowAlignmentChanged,
        kMarkContainerChain);
  }
}

void LayoutBox::UpdateScrollSnapMappingAfterStyleChange(
    const ComputedStyle& old_style) {
  NOT_DESTROYED();
  // scroll-snap-type and scroll-padding invalidate the snap container.
  if (old_style.GetScrollSnapType() != StyleRef().GetScrollSnapType() ||
      old_style.ScrollPaddingBottom() != StyleRef().ScrollPaddingBottom() ||
      old_style.ScrollPaddingLeft() != StyleRef().ScrollPaddingLeft() ||
      old_style.ScrollPaddingTop() != StyleRef().ScrollPaddingTop() ||
      old_style.ScrollPaddingRight() != StyleRef().ScrollPaddingRight()) {
    if (!NeedsLayout() && IsScrollContainer()) {
      GetScrollableArea()->EnqueueForSnapUpdateIfNeeded();
    }
  }

  // scroll-snap-align invalidates layout as we need to propagate the
  // snap-areas up the fragment-tree.
  if (old_style.GetScrollSnapAlign() != StyleRef().GetScrollSnapAlign()) {
    if (auto* containing_block = ContainingBlock()) {
      containing_block->SetNeedsLayout(layout_invalidation_reason::kStyleChange,
                                       kMarkContainerChain);
    }
  }

  auto SnapAreaDidChange = [&]() {
    auto* snap_container = ContainingScrollContainer();
    if (snap_container && !snap_container->NeedsLayout()) {
      snap_container->GetScrollableArea()->EnqueueForSnapUpdateIfNeeded();
    }
  };

  // scroll-snap-stop and scroll-margin invalidate the snap area.
  if (old_style.ScrollSnapStop() != StyleRef().ScrollSnapStop() ||
      old_style.ScrollMarginBottom() != StyleRef().ScrollMarginBottom() ||
      old_style.ScrollMarginLeft() != StyleRef().ScrollMarginLeft() ||
      old_style.ScrollMarginTop() != StyleRef().ScrollMarginTop() ||
      old_style.ScrollMarginRight() != StyleRef().ScrollMarginRight()) {
    SnapAreaDidChange();
  }

  // Transform invalidates the snap area.
  if (old_style.Transform() != StyleRef().Transform())
    SnapAreaDidChange();
}

bool LayoutBox::ShouldBeHandledAsFloating(const ComputedStyle& style) const {
  NOT_DESTROYED();
  return style.IsFloating() &&
         ToPositionedState(style.GetPosition()) != kIsOutOfFlowPositioned &&
         !style.IsInsideDisplayIgnoringFloatingChildren();
}

void LayoutBox::UpdateFromStyle() {
  NOT_DESTROYED();
  LayoutBoxModelObject::UpdateFromStyle();

  const ComputedStyle& style_to_use = StyleRef();
  SetFloating(ShouldBeHandledAsFloating(style_to_use));
  SetHasTransformRelatedProperty(
      IsSVGChild() ? style_to_use.HasTransformRelatedPropertyForSVG()
                   : style_to_use.HasTransformRelatedProperty());
  SetHasReflection(style_to_use.BoxReflect());

  bool should_clip_overflow = (!StyleRef().IsOverflowVisibleAlongBothAxes() ||
                               ShouldApplyPaintContainment()) &&
                              RespectsCSSOverflow();
  if (should_clip_overflow != HasNonVisibleOverflow()) {
    // The overflow clip paint property depends on whether overflow clip is
    // present so we need to update paint properties if this changes.
    SetNeedsPaintPropertyUpdate();
    if (Layer())
      Layer()->SetNeedsCompositingInputsUpdate();
  }
  SetHasNonVisibleOverflow(should_clip_overflow);
}

void LayoutBox::LayoutSubtreeRoot() {
  NOT_DESTROYED();

  // Our own style may have changed which would disqualify us as a layout root
  // (e.g. our containment/writing-mode/formatting-context status/etc changed).
  // Skip subtree layout, and ensure our container chain needs layout.
  if (SelfNeedsFullLayout()) {
    MarkContainerChainForLayout();
    return;
  }

  const auto* previous_result = GetSingleCachedLayoutResult();
  DCHECK(previous_result);
  const auto& space = previous_result->GetConstraintSpaceForCaching();
  DCHECK_EQ(space.GetWritingMode(), StyleRef().GetWritingMode());
  const LayoutResult* result = BlockNode(this).Layout(space);
  GetDocument().GetFrame()->GetInputMethodController().DidLayoutSubtree(*this);

  if (IsOutOfFlowPositioned()) {
    result->CopyMutableOutOfFlowData(*previous_result);
  }

  // Even if we are a subtree layout root we need to mark our containing-block
  // for layout if:
  //  - Our baselines have shifted.
  //  - We've propagated any layout-objects (which affect our container chain).
  //
  // NOTE: We could weaken the constraints in ObjectIsRelayoutBoundary, and use
  // this technique to detect size-changes, etc if we wanted to expand this
  // optimization.
  const auto& previous_fragment =
      To<PhysicalBoxFragment>(previous_result->GetPhysicalFragment());
  const auto& fragment = To<PhysicalBoxFragment>(result->GetPhysicalFragment());
  if (previous_fragment.FirstBaseline() != fragment.FirstBaseline() ||
      previous_fragment.LastBaseline() != fragment.LastBaseline() ||
      fragment.HasPropagatedLayoutObjects()) {
    if (auto* containing_block = ContainingBlock()) {
      containing_block->SetNeedsLayout(
          layout_invalidation_reason::kChildChanged, kMarkContainerChain);
    }
  }
}

bool LayoutBox::UsesOverlayScrollbars() const {
  NOT_DESTROYED();
  if (StyleRef().HasCustomScrollbarStyle(DynamicTo<Element>(GetNode()))) {
    return false;
  }
  if (GetFrame()->GetPage()->GetScrollbarTheme().UsesOverlayScrollbars())
    return true;
  return false;
}

LayoutUnit LayoutBox::ScrollWidth() const {
  NOT_DESTROYED();
  if (IsScrollContainer())
    return GetScrollableArea()->ScrollWidth();
  if (StyleRef().IsScrollbarGutterStable() &&
      StyleRef().OverflowBlockDirection() == EOverflow::kHidden) {
    if (auto* scrollable_area = GetScrollableArea())
      return scrollable_area->ScrollWidth();
    else
      return ScrollableOverflowRect().Width();
  }
  // For objects with scrollable overflow, this matches IE.
  const PhysicalRect overflow_rect = ScrollableOverflowRect();
  if (!StyleRef().GetWritingDirection().IsFlippedX()) {
    return std::max(PhysicalPaddingBoxRect().Width(),
                    overflow_rect.Right() - BorderOutsets().left);
  }
  return PhysicalPaddingBoxRect().Width() -
         std::min(LayoutUnit(), overflow_rect.X() - BorderOutsets().left);
}

LayoutUnit LayoutBox::ScrollHeight() const {
  NOT_DESTROYED();
  if (IsScrollContainer())
    return GetScrollableArea()->ScrollHeight();
  if (StyleRef().IsScrollbarGutterStable() &&
      StyleRef().OverflowBlockDirection() == EOverflow::kHidden) {
    if (auto* scrollable_area = GetScrollableArea())
      return scrollable_area->ScrollHeight();
    else
      return ScrollableOverflowRect().Height();
  }
  // For objects with visible overflow, this matches IE.
  // FIXME: Need to work right with writing modes.
  return std::max(PhysicalPaddingBoxRect().Height(),
                  ScrollableOverflowRect().Bottom() - BorderOutsets().top);
}

PhysicalBoxStrut LayoutBox::MarginOutsets() const {
  NOT_DESTROYED();
  if (PhysicalFragmentCount()) {
    // We get margin data from the first physical fragment. Margins are
    // per-LayoutBox data, and we don't need to take care of block
    // fragmentation.
    return GetPhysicalFragment(0)->Margins();
  }
  return PhysicalBoxStrut();
}

LayoutBlock* LayoutBox::GetScrollMarkerGroup() {
  NOT_DESTROYED();
  if (StyleRef().ScrollMarkerGroupNone()) {
    return nullptr;
  }
  LayoutBox* content_box = ContentLayoutBox();
  if (!content_box || (!content_box->IsScrollContainer() &&
                       !content_box->IsDocumentElement())) {
    return nullptr;
  }
  if (auto* element = DynamicTo<Element>(GetNode())) {
    PseudoElement* pseudo =
        element->GetPseudoElement(kPseudoIdScrollMarkerGroupBefore);
    if (!pseudo) {
      pseudo = element->GetPseudoElement(kPseudoIdScrollMarkerGroupAfter);
    }
    if (pseudo) {
      return To<LayoutBlock>(pseudo->GetLayoutObject());
    }
  }
  return nullptr;
}

LayoutBlock* LayoutBox::ScrollerFromScrollMarkerGroup() const {
  NOT_DESTROYED();
  DCHECK(IsScrollMarkerGroup());
  auto* pseudo_element = DynamicTo<PseudoElement>(GetNode());
  if (const Element* originating_element = pseudo_element->parentElement()) {
    auto* box = originating_element->GetLayoutBoxForScrolling();
    return box && box->GetScrollableArea()->ScrollableAxes()
               ? DynamicTo<LayoutBlock>(box)
               : nullptr;
  }
  return nullptr;
}

void LayoutBox::QuadsInAncestorInternal(Vector<gfx::QuadF>& quads,
                                        const LayoutBoxModelObject* ancestor,
                                        MapCoordinatesFlags mode,
                                        BoxQuadType box_type) const {
  NOT_DESTROYED();
  const PhysicalBoxFragment* first_fragment = nullptr;
  for (const PhysicalBoxFragment& fragment : PhysicalFragments()) {
    // Calculate the offset relatively to the first fragment, which in turn
    // will be mapped correctly to the ancestor.
    PhysicalOffset offset;
    if (!first_fragment) {
      first_fragment = &fragment;
    } else {
      offset = fragment.OffsetFromRootFragmentationContext() -
               first_fragment->OffsetFromRootFragmentationContext();
    }
    PhysicalRect rect = LocalRectForBoxQuad(fragment, box_type);
    rect.offset += offset;
    quads.push_back(LocalRectToAncestorQuad(rect, ancestor, mode));
  }
}

gfx::RectF LayoutBox::LocalBoundingBoxRectForAccessibility(
    IncludeDescendants include_descendants) const {
  NOT_DESTROYED();
  PhysicalSize size = StitchedSize();
  return gfx::RectF(0, 0, size.width.ToFloat(), size.height.ToFloat());
}

void LayoutBox::UpdateAfterLayout() {
  NOT_DESTROYED();

  SetNeedsOverflowRecalc(OverflowRecalcType::kOnlyVisualOverflowRecalc);
  SetScrollableOverflowFromLayoutResults();

  if (IsLayoutView() && !GetDocument().Printing()) {
    // Unlike every other layer, the root PaintLayer takes its size from the
    // layout viewport size. The call to AdjustViewSize() will update the
    // frame's contents size, which will also update the page's minimum scale
    // factor. The call to ResizeAfterLayout() will calculate the layout
    // viewport size based on the page minimum scale factor, and then update the
    // LocalFrameView with the new size.
    LocalFrame& frame = GetFrameView()->GetFrame();
    GetFrameView()->AdjustViewSize();
    if (frame.IsMainFrame()) {
      frame.GetChromeClient().ResizeAfterLayout();
    }
    if (IsScrollContainer()) {
      GetScrollableArea()->ClampScrollOffsetAfterOverflowChange();
    }
  }

  // Transform-origin depends on box size, so we need to update the layer
  // transform after layout.
  if (HasLayer()) {
    Layer()->UpdateTransform();
    Layer()->UpdateScrollingAfterLayout();
  }

  GetFrame()->GetInputMethodController().DidUpdateLayout(*this);
  if (IsPositioned())
    GetFrame()->GetInputMethodController().DidLayoutSubtree(*this);

  ClearNeedsLayout();

  // We should notify the display lock that we've done layout on self, and if
  // it's not blocked, on children.
  if (auto* context = GetDisplayLockContext()) {
    if (!ChildLayoutBlockedByDisplayLock()) {
      context->DidLayoutChildren();
    }
  }
}

LayoutUnit LayoutBox::OverrideIntrinsicContentInlineSize() const {
  NOT_DESTROYED();

  // We only override a size contained dimension.
  if (!ShouldApplyInlineSizeContainment()) {
    return kIndefiniteSize;
  }

  const auto& style = StyleRef();
  StyleIntrinsicLength intrinsic_length =
      style.EffectiveContainIntrinsicInlineSize();

  if (intrinsic_length.HasAuto()) {
    const auto* context = GetDisplayLockContext();
    const bool is_locked = context && context->IsLocked();
    const auto* elem = DynamicTo<Element>(GetNode());
    const bool is_vt_scope =
        style.HasSizeContainmentForViewTransitionScope() &&
        RuntimeEnabledFeatures::ScopedViewTransitionSizeContainmentEnabled();
    if (is_locked || is_vt_scope) {
      if (elem) {
        if (const auto inline_size = elem->LastRememberedInlineSize()) {
          // ResizeObserverSize is adjusted to be in CSS space, we need to
          // adjust it back to Layout space by applying the effective zoom.
          return LayoutUnit::FromFloatRound(*inline_size *
                                            style.EffectiveZoom());
        }
      }
    }
  }

  if (const auto& length = intrinsic_length.GetLength()) {
    DCHECK(length->IsFixed());
    return LayoutUnit(length->Pixels());
  }

  return kIndefiniteSize;
}

LayoutUnit LayoutBox::OverrideIntrinsicContentBlockSize() const {
  NOT_DESTROYED();

  // We only override a size contained dimension.
  if (!ShouldApplyBlockSizeContainment()) {
    return kIndefiniteSize;
  }

  const auto& style = StyleRef();
  StyleIntrinsicLength intrinsic_length =
      style.EffectiveContainIntrinsicBlockSize();

  if (intrinsic_length.HasAuto()) {
    const auto* context = GetDisplayLockContext();
    const bool is_locked = context && context->IsLocked();
    const auto* elem = DynamicTo<Element>(GetNode());
    const bool is_vt_scope =
        style.HasSizeContainmentForViewTransitionScope() &&
        RuntimeEnabledFeatures::ScopedViewTransitionSizeContainmentEnabled();
    if (is_locked || is_vt_scope) {
      if (elem) {
        if (const auto block_size = elem->LastRememberedBlockSize()) {
          // ResizeObserverSize is adjusted to be in CSS space, we need to
          // adjust it back to Layout space by applying the effective zoom.
          return LayoutUnit::FromFloatRound(*block_size *
                                            style.EffectiveZoom());
        }
      }
    }
  }

  if (const auto& length = intrinsic_length.GetLength()) {
    DCHECK(length->IsFixed());
    return LayoutUnit(length->Pixels());
  }

  return kIndefiniteSize;
}

LayoutUnit LayoutBox::DefaultIntrinsicContentInlineSize() const {
  NOT_DESTROYED();

  if (!IsA<Element>(GetNode()))
    return kIndefiniteSize;
  const Element& element = *To<Element>(GetNode());

  const bool apply_fixed_size = StyleRef().ApplyControlFixedSize(&element);
  const auto* select = DynamicTo<HTMLSelectElement>(element);
  if (select && select->UsesMenuList() &&
      !select->SupportsBaseAppearance(StyleRef().EffectiveAppearance()))
      [[unlikely]] {
    return apply_fixed_size ? MenuListIntrinsicInlineSize(*select, *this)
                            : kIndefiniteSize;
  }
  const auto* input = DynamicTo<HTMLInputElement>(element);
  if (input) [[unlikely]] {
    if (input->IsTextField() && apply_fixed_size) {
      return TextFieldIntrinsicInlineSize(*input, *this);
    }
    FormControlType type = input->FormControlType();
    if (type == FormControlType::kInputFile && apply_fixed_size) {
      return FileUploadControlIntrinsicInlineSize(*input, *this);
    }
    if (type == FormControlType::kInputRange) {
      return SliderIntrinsicInlineSize(*this);
    }
    auto effective_appearance = StyleRef().EffectiveAppearance();
    if (effective_appearance == AppearanceValue::kCheckbox) {
      return ThemePartIntrinsicSize(*this, WebThemeEngine::kPartCheckbox)
          .inline_size;
    }
    if (effective_appearance == AppearanceValue::kRadio) {
      return ThemePartIntrinsicSize(*this, WebThemeEngine::kPartRadio)
          .inline_size;
    }
    return kIndefiniteSize;
  }
  const auto* textarea = DynamicTo<HTMLTextAreaElement>(element);
  if (textarea && apply_fixed_size) [[unlikely]] {
    return TextAreaIntrinsicInlineSize(*textarea, *this);
  }
  if (IsSliderContainer(element))
    return SliderIntrinsicInlineSize(*this);

  return kIndefiniteSize;
}

LayoutUnit LayoutBox::DefaultIntrinsicContentBlockSize(
    bool children_have_geometry) const {
  NOT_DESTROYED();

  auto effective_appearance = StyleRef().EffectiveAppearance();
  if (effective_appearance == AppearanceValue::kCheckbox) {
    return ThemePartIntrinsicSize(*this, WebThemeEngine::kPartCheckbox)
        .block_size;
  }
  if (effective_appearance == AppearanceValue::kRadio) {
    return ThemePartIntrinsicSize(*this, WebThemeEngine::kPartRadio).block_size;
  }

  if (!StyleRef().ApplyControlFixedSize(GetNode())) {
    return kIndefiniteSize;
  }
  if (const auto* select = DynamicTo<HTMLSelectElement>(GetNode())) {
    if (!select->SupportsBaseAppearance(effective_appearance)) {
      if (!select->UsesMenuList()) {
        if (!children_have_geometry) {
          return kIndefiniteSize;
        }
        return ListBoxItemBlockSize(*select, *this) * select->ListBoxSize() -
               ComputeLogicalScrollbars().BlockSum();
      } else {
        return MenuListIntrinsicBlockSize(*select, *this);
      }
    }
  }
  if (IsTextField()) {
    return TextFieldIntrinsicBlockSize(*To<HTMLInputElement>(GetNode()), *this);
  }
  if (IsTextArea()) {
    return TextAreaIntrinsicBlockSize(*To<HTMLTextAreaElement>(GetNode()),
                                      *this);
  }

  return kIndefiniteSize;
}

LogicalRect LayoutBox::LogicalRectInContainer() const {
  NOT_DESTROYED();
  return LocationContainer()->CreateWritingModeConverter().ToLogical(
      PhysicalRect(PhysicalLocation(), StitchedSize()));
}

gfx::QuadF LayoutBox::AbsoluteContentQuad(MapCoordinatesFlags flags) const {
  NOT_DESTROYED();
  PhysicalRect rect = PhysicalContentBoxRect();
  return LocalRectToAbsoluteQuad(rect, flags);
}

PhysicalRect LayoutBox::PhysicalBackgroundRect(
    BackgroundRectType rect_type) const {
  NOT_DESTROYED();
  // If the background transfers to view, the used background of this object
  // is transparent.
  if (rect_type == kBackgroundKnownOpaqueRect && BackgroundTransfersToView())
    return PhysicalRect();

  std::optional<EFillBox> background_box;
  Color background_color = ResolveColor(GetCSSPropertyBackgroundColor());
  // Find the largest background rect of the given opaqueness.
  for (const FillLayer* cur = &(StyleRef().BackgroundLayers()); cur;
       cur = cur->Next()) {
    EFillBox current_clip = cur->Clip();
    if (rect_type == kBackgroundKnownOpaqueRect) {
      if (IsSpecialClipFillBox(current_clip)) {
        continue;
      }

      if (cur->GetBlendMode() != BlendMode::kNormal ||
          cur->Composite() != kCompositeSourceOver)
        continue;

      bool layer_known_opaque = false;
      // Check if the image is opaque and fills the clip.
      if (const StyleImage* image = cur->GetImage()) {
        if ((cur->Repeat().x == EFillRepeat::kRepeatFill ||
             cur->Repeat().x == EFillRepeat::kRoundFill) &&
            (cur->Repeat().y == EFillRepeat::kRepeatFill ||
             cur->Repeat().y == EFillRepeat::kRoundFill) &&
            image->KnownToBeOpaque(GetDocument(), StyleRef())) {
          layer_known_opaque = true;
        }
      }

      // The background color is painted into the last layer.
      if (!cur->Next() && background_color.IsOpaque()) {
        layer_known_opaque = true;
      }

      // If neither the image nor the color are opaque then skip this layer.
      if (!layer_known_opaque)
        continue;
    } else {
      // Ignore invisible background layers for kBackgroundPaintedExtent.
      DCHECK_EQ(rect_type, kBackgroundPaintedExtent);
      if (!cur->GetImage() &&
          (cur->Next() || background_color.IsFullyTransparent())) {
        continue;
      }
      // A content-box clipped fill layer can be scrolled into the padding box
      // of the overflow container.
      if (current_clip == EFillBox::kContent &&
          cur->Attachment() == EFillAttachment::kLocal) {
        current_clip = EFillBox::kPadding;
      }
    }

    // Restrict clip if attachment is local.
    if (current_clip == EFillBox::kBorder &&
        cur->Attachment() == EFillAttachment::kLocal)
      current_clip = EFillBox::kPadding;

    background_box = background_box
                         ? EnclosingFillBox(*background_box, current_clip)
                         : current_clip;
  }

  if (!background_box)
    return PhysicalRect();

  if (IsSpecialClipFillBox(*background_box)) {
    DCHECK_NE(rect_type, kBackgroundKnownOpaqueRect);
    *background_box = EFillBox::kBorder;
  }

  if (rect_type == kBackgroundPaintedExtent &&
      *background_box == EFillBox::kBorder &&
      BackgroundClipBorderBoxIsEquivalentToPaddingBox()) {
    *background_box = EFillBox::kPadding;
  }

  switch (*background_box) {
    case EFillBox::kBorder:
      return PhysicalBorderBoxRect();
    case EFillBox::kPadding:
      return PhysicalPaddingBoxRect();
    case EFillBox::kContent:
      return PhysicalContentBoxRect();
    default:
      NOTREACHED();
  }
}

void LayoutBox::AddOutlineRects(OutlineRectCollector& collector,
                                OutlineInfo* info,
                                const PhysicalOffset& additional_offset,
                                OutlineType) const {
  NOT_DESTROYED();
  collector.AddRect(PhysicalRect(additional_offset, StitchedSize()));
  if (info)
    *info = OutlineInfo::GetFromStyle(StyleRef());
}

bool LayoutBox::CanResize() const {
  NOT_DESTROYED();
  // We need a special case for <iframe> because they never have
  // hasOverflowClip(). However, they do "implicitly" clip their contents, so
  // we want to allow resizing them also.
  return (IsScrollContainer() || IsLayoutIFrame()) && StyleRef().HasResize();
}

bool LayoutBox::HasScrollbarGutters(ScrollbarOrientation orientation) const {
  NOT_DESTROYED();
  if (StyleRef().IsScrollbarGutterAuto())
    return false;

  // A box that doesn't respect CSS overflow can never show scrollbars itself,
  // so it reserves no scrollbar gutter.
  if (!RespectsCSSOverflow()) {
    return false;
  }

  DCHECK(StyleRef().IsScrollbarGutterStable());

  // Scrollbar-gutter propagates to the viewport
  // (see:|StyleResolver::PropagateStyleToViewport|).
  if (orientation == kVerticalScrollbar) {
    EOverflow overflow = StyleRef().OverflowY();
    return StyleRef().IsHorizontalWritingMode() &&
           (overflow == EOverflow::kAuto || overflow == EOverflow::kScroll ||
            overflow == EOverflow::kHidden) &&
           !UsesOverlayScrollbars() &&
           GetNode() != GetDocument().ViewportDefiningElement();
  } else {
    EOverflow overflow = StyleRef().OverflowX();
    return !StyleRef().IsHorizontalWritingMode() &&
           (overflow == EOverflow::kAuto || overflow == EOverflow::kScroll ||
            overflow == EOverflow::kHidden) &&
           !UsesOverlayScrollbars() &&
           GetNode() != GetDocument().ViewportDefiningElement();
  }
}

PhysicalBoxStrut LayoutBox::ComputeScrollbarsInternal(
    ShouldClampToContentBox clamp_to_content_box,
    OverlayScrollbarClipBehavior overlay_scrollbar_clip_behavior,
    ShouldIncludeScrollbarGutter include_scrollbar_gutter) const {
  NOT_DESTROYED();
  PhysicalBoxStrut scrollbars;
  PaintLayerScrollableArea* scrollable_area = GetScrollableArea();

  if (include_scrollbar_gutter == kIncludeScrollbarGutter &&
      HasScrollbarGutters(kVerticalScrollbar)) {
    LayoutUnit gutter_size =
        LayoutUnit(HypotheticalScrollbarThickness(*this, kVerticalScrollbar));
    if (ShouldPlaceVerticalScrollbarOnLeft()) {
      scrollbars.left = gutter_size;
      if (StyleRef().IsScrollbarGutterBothEdges())
        scrollbars.right = gutter_size;
    } else {
      scrollbars.right = gutter_size;
      if (StyleRef().IsScrollbarGutterBothEdges())
        scrollbars.left = gutter_size;
    }
  } else if (scrollable_area) {
    if (ShouldPlaceVerticalScrollbarOnLeft()) {
      scrollbars.left = LayoutUnit(scrollable_area->VerticalScrollbarWidth(
          overlay_scrollbar_clip_behavior));
    } else {
      scrollbars.right = LayoutUnit(scrollable_area->VerticalScrollbarWidth(
          overlay_scrollbar_clip_behavior));
    }
  }

  if (include_scrollbar_gutter == kIncludeScrollbarGutter &&
      HasScrollbarGutters(kHorizontalScrollbar)) {
    LayoutUnit gutter_size =
        LayoutUnit(HypotheticalScrollbarThickness(*this, kHorizontalScrollbar));
    scrollbars.bottom = gutter_size;
    if (StyleRef().IsScrollbarGutterBothEdges())
      scrollbars.top = gutter_size;
  } else if (scrollable_area) {
    scrollbars.bottom = LayoutUnit(scrollable_area->HorizontalScrollbarHeight(
        overlay_scrollbar_clip_behavior));
  }

  // Use the width of the vertical scrollbar, unless it's larger than the
  // logical width of the content box, in which case we'll use that instead.
  // Scrollbar handling is quite bad in such situations, and this code here
  // is just to make sure that left-hand scrollbars don't mess up
  // scrollWidth. For the full story, visit http://crbug.com/724255.
  if (scrollbars.left > 0 && clamp_to_content_box == kClampToContentBox) {
    const LayoutUnit max_width =
        StitchedSize().width -
        (BorderOutsets() + PaddingOutsets()).HorizontalSum();
    scrollbars.left =
        std::min(scrollbars.left, max_width.ClampNegativeToZero());
  }

  return scrollbars;
}

bool LayoutBox::Autoscroll(const PhysicalOffset& position_in_root_frame) {
  NOT_DESTROYED();
  LocalFrame* frame = GetFrame();
  if (!frame)
    return false;

  LocalFrameView* frame_view = frame->View();
  if (!frame_view)
    return false;

  PhysicalOffset absolute_position =
      frame_view->ConvertFromRootFrame(position_in_root_frame);
  mojom::blink::ScrollIntoViewParamsPtr params =
      scroll_into_view_util::CreateScrollIntoViewParams(
          ScrollAlignment::ToEdgeIfNeeded(), ScrollAlignment::ToEdgeIfNeeded(),
          mojom::blink::ScrollType::kUser);
  return scroll_into_view_util::ScrollRectToVisible(
      *this,
      PhysicalRect(absolute_position,
                   PhysicalSize(LayoutUnit(1), LayoutUnit(1))),
      std::move(params), nullptr, false, true);
}

// If specified point is outside the border-belt-excluded box (the border box
// inset by the autoscroll activation threshold), returned offset denotes
// direction of scrolling.
PhysicalOffset LayoutBox::CalculateAutoscrollDirection(
    const gfx::PointF& point_in_root_frame) const {
  NOT_DESTROYED();
  if (!GetFrame())
    return PhysicalOffset();

  LocalFrameView* frame_view = GetFrame()->View();
  if (!frame_view)
    return PhysicalOffset();

#if BUILDFLAG(IS_IOS)
  float autoscroll_belt_size_in_viewport =
      frame_view->GetChromeClient()->WindowToViewportScalar(
          GetFrame(), kAutoscrollBeltSizeInDIPs);
  float autoscroll_belt_size_for_bottom_in_viewport =
      frame_view->GetChromeClient()->WindowToViewportScalar(
          GetFrame(), kAutoscrollBeltSizeInDIPsBottom);
#else
  float autoscroll_belt_size_in_viewport = kAutoscrollBeltSize;
  float autoscroll_belt_size_for_bottom_in_viewport = kAutoscrollBeltSizeBottom;
#endif  // BUILDFLAG(IS_IOS)

  PhysicalRect absolute_scrolling_box(AbsoluteBoundingBoxRect());

  // Exclude scrollbars so the border belt (activation area) starts from the
  // scrollbar-content edge rather than the window edge.
  ExcludeScrollbars(absolute_scrolling_box,
                    kExcludeOverlayScrollbarSizeForHitTesting);

  PhysicalRect belt_box =
      View()->GetFrameView()->ConvertToRootFrame(absolute_scrolling_box);
  LayoutUnit autoscroll_belt_size_layout_unit =
      LayoutUnit(autoscroll_belt_size_in_viewport);
  belt_box.ContractEdges(
      autoscroll_belt_size_layout_unit, autoscroll_belt_size_layout_unit,
      LayoutUnit(autoscroll_belt_size_for_bottom_in_viewport),
      autoscroll_belt_size_layout_unit);

  gfx::PointF point = point_in_root_frame;

  if (point.x() < belt_box.X())
    point.Offset(-autoscroll_belt_size_in_viewport, 0);
  else if (point.x() > belt_box.Right())
    point.Offset(autoscroll_belt_size_in_viewport, 0);

  if (point.y() < belt_box.Y())
    point.Offset(0, -autoscroll_belt_size_in_viewport);
  else if (point.y() > belt_box.Bottom())
    point.Offset(0, autoscroll_belt_size_for_bottom_in_viewport);

  return PhysicalOffset::FromVector2dFRound(point - point_in_root_frame);
}

LayoutBox* LayoutBox::FindAutoscrollable(LayoutObject* layout_object,
                                         bool is_middle_click_autoscroll) {
  while (layout_object && !(layout_object->IsBox() &&
                            To<LayoutBox>(layout_object)->IsUserScrollable())) {
    // Do not start selection-based autoscroll when the node is inside a
    // fixed-position element.
    if (!is_middle_click_autoscroll && layout_object->IsBox() &&
        To<LayoutBox>(layout_object)->IsFixedToView()) {
      return nullptr;
    }

    if (!layout_object->Parent() &&
        layout_object->GetNode() == layout_object->GetDocument() &&
        layout_object->GetDocument().LocalOwner()) {
      layout_object =
          layout_object->GetDocument().LocalOwner()->GetLayoutObject();
    } else {
      layout_object = layout_object->Parent();
    }
  }

  return DynamicTo<LayoutBox>(layout_object);
}

bool LayoutBox::HasHorizontallyScrollableAncestor(LayoutObject* layout_object) {
  while (layout_object) {
    if (layout_object->IsBox() &&
        To<LayoutBox>(layout_object)->HasScrollableOverflowX())
      return true;

    // Scroll is not propagating.
    if (layout_object->StyleRef().OverscrollBehaviorX() !=
            EOverscrollBehavior::kAuto &&
        layout_object->StyleRef().OverscrollBehaviorX() !=
            EOverscrollBehavior::kChain) {
      break;
    }

    if (!layout_object->Parent() &&
        layout_object->GetNode() == layout_object->GetDocument() &&
        layout_object->GetDocument().LocalOwner()) {
      layout_object =
          layout_object->GetDocument().LocalOwner()->GetLayoutObject();
    } else {
      layout_object = layout_object->Parent();
    }
  }

  return false;
}

gfx::Vector2d LayoutBox::OriginAdjustmentForScrollbars() const {
  NOT_DESTROYED();
  if (CanSkipComputeScrollbars())
    return gfx::Vector2d();

  PhysicalBoxStrut scrollbars = ComputeScrollbarsInternal(kClampToContentBox);
  return gfx::Vector2d(scrollbars.left.ToInt(), scrollbars.top.ToInt());
}

gfx::Point LayoutBox::ScrollOrigin() const {
  NOT_DESTROYED();
  return GetScrollableArea() ? GetScrollableArea()->ScrollOrigin()
                             : gfx::Point();
}

PhysicalOffset LayoutBox::ScrolledContentOffset() const {
  NOT_DESTROYED();
  DCHECK(IsScrollContainer());
  DCHECK(GetScrollableArea());
  return PhysicalOffset::FromVector2dFFloor(
      GetScrollableArea()->GetScrollOffset());
}

gfx::Vector2d LayoutBox::PixelSnappedScrolledContentOffset() const {
  NOT_DESTROYED();
  DCHECK(IsScrollContainer());
  DCHECK(GetScrollableArea());
  return GetScrollableArea()->ScrollOffsetInt();
}

PhysicalRect LayoutBox::ClippingRect() const {
  NOT_DESTROYED();
  PhysicalRect result(InfiniteIntRect());
  if (ShouldClipOverflowAlongEitherAxis()) {
    result = OverflowClipRect();
  }
  if (HasCSSClip()) {
    result.Intersect(CSSClipRect());
  }

  return result;
}

gfx::PointF LayoutBox::PerspectiveOrigin(const PhysicalSize* size) const {
  NOT_DESTROYED();
  if (!HasTransformRelatedProperty())
    return gfx::PointF();

  // Use the |size| parameter instead of |Size()| if present.
  gfx::SizeF float_size = size ? gfx::SizeF(*size) : gfx::SizeF(StitchedSize());

  return PointForLengthPoint(StyleRef().PerspectiveOrigin(), float_size);
}

bool LayoutBox::MapVisualRectToContainer(
    const LayoutObject* container_object,
    const PhysicalOffset& container_offset,
    const LayoutObject* ancestor,
    VisualRectFlags visual_rect_flags,
    TransformState& transform_state) const {
  NOT_DESTROYED();
  bool container_preserve_3d = container_object->StyleRef().Preserves3D() &&
                               container_object == NearestAncestorForElement();

  TransformState::TransformAccumulation accumulation =
      container_preserve_3d ? TransformState::kAccumulateTransform
                            : TransformState::kFlattenTransform;

  // If there is no transform on this box, adjust for container offset and
  // container scrolling, then apply container clip.
  if (!ShouldUseTransformFromContainer(container_object)) {
    transform_state.Move(container_offset, accumulation);
    if (container_object->IsBox() && container_object != ancestor &&
        !To<LayoutBox>(container_object)
             ->MapContentsRectToBoxSpace(transform_state, accumulation, *this,
                                         visual_rect_flags)) {
      return false;
    }
    return true;
  }

  // Otherwise, do the following:
  // 1. Generate transformation matrix combining, in this order
  //    a) transform,
  //    b) container offset,
  //    c) container scroll offset,
  //    d) perspective applied by container.
  // 2. Apply transform + flattening.
  // 3. Apply container clip.

  // This step is now deprecated by default because it inflated rotated and
  // skewed elements with each step up the ancestor chain.
  if (!RuntimeEnabledFeatures::VisualRectMappingFixForExpansionEnabled()) {
    // Use EnclosingBoundingBox because we cannot properly compute pixel
    // snapping for painted elements within the transform since we don't know
    // the desired subpixel accumulation at this point, and the transform may
    // include a scale. This only makes sense for non-preserve3D.
    if (!StyleRef().Preserves3D()) {
      transform_state.Flatten();
      transform_state.SetQuad(gfx::QuadF(gfx::RectF(gfx::ToEnclosingRect(
          transform_state.LastPlanarQuad().BoundingBox()))));
    }
  }

  // 1. Generate transformation matrix.
  // a) Transform.
  gfx::Transform transform;
  if (Layer() && Layer()->Transform())
    transform.PreConcat(Layer()->CurrentTransform());

  // b) Container offset.
  transform.PostTranslate(container_offset.left.ToFloat(),
                          container_offset.top.ToFloat());

  // c) Container scroll offset.
  if (container_object->IsBox() && container_object != ancestor &&
      To<LayoutBox>(container_object)->ContainedContentsScroll(*this)) {
    PhysicalOffset offset(
        -To<LayoutBox>(container_object)->ScrolledContentOffset());
    transform.PostTranslate(offset.left, offset.top);
  }

  bool has_perspective = container_object && container_object->HasPerspective();
  if (has_perspective && container_object != NearestAncestorForElement()) {
    has_perspective = false;

    if (StyleRef().Preserves3D() || transform.Creates3d()) {
      UseCounter::Count(GetDocument(),
                        WebFeature::kDifferentPerspectiveCBOrParent);
    }
  }

  // d) Perspective applied by container.
  if (has_perspective) {
    // Perspective on the container affects us, so we have to factor it in here.
    DCHECK(container_object->HasLayer());
    gfx::PointF perspective_origin;
    if (const auto* container_box = DynamicTo<LayoutBox>(container_object))
      perspective_origin = container_box->PerspectiveOrigin();

    gfx::Transform perspective_matrix;
    perspective_matrix.ApplyPerspectiveDepth(
        container_object->StyleRef().UsedPerspective());
    perspective_matrix.ApplyTransformOrigin(perspective_origin.x(),
                                            perspective_origin.y(), 0);

    transform = perspective_matrix * transform;
  }

  // 2. Apply transform and flatten.
  transform_state.ApplyTransform(transform, accumulation);
  if (!container_preserve_3d)
    transform_state.Flatten();

  // 3. Apply container clip.
  if (container_object->IsBox() && container_object != ancestor &&
      container_object->HasClipRelatedProperty()) {
    return To<LayoutBox>(container_object)
        ->ApplyBoxClips(transform_state, accumulation, visual_rect_flags);
  }

  return true;
}

bool LayoutBox::MapContentsRectToBoxSpace(
    TransformState& transform_state,
    TransformState::TransformAccumulation accumulation,
    const LayoutObject& contents,
    VisualRectFlags visual_rect_flags) const {
  NOT_DESTROYED();
  if (!HasClipRelatedProperty())
    return true;

  if (ContainedContentsScroll(contents))
    transform_state.Move(-ScrolledContentOffset());

  return ApplyBoxClips(transform_state, accumulation, visual_rect_flags);
}

bool LayoutBox::ContainedContentsScroll(const LayoutObject& contents) const {
  NOT_DESTROYED();
  if (IsA<LayoutView>(this) &&
      contents.StyleRef().GetPosition() == EPosition::kFixed) {
    return false;
  }
  return IsScrollContainer();
}

bool LayoutBox::ApplyBoxClips(
    TransformState& transform_state,
    TransformState::TransformAccumulation accumulation,
    VisualRectFlags visual_rect_flags) const {
  NOT_DESTROYED();
  if (visual_rect_flags.Has(VisualRectFlag::kSkipAncestorAndViewportClips)) {
    return true;
  }
  transform_state.Flatten();
  PhysicalRect rect = PhysicalRect::EnclosingRect(
      transform_state.LastPlanarQuad().BoundingBox());

  bool does_intersect;
  // This won't work fully correctly for fixed-position elements, who should
  // receive CSS clip but for whom the current object is not in the containing
  // block chain.
  PhysicalRect clip_rect = ClippingRect();
  if (visual_rect_flags.Has(VisualRectFlag::kEdgeInclusive)) {
    does_intersect = rect.InclusiveIntersect(clip_rect);
  } else {
    rect.Intersect(clip_rect);
    does_intersect = !rect.IsEmpty();
  }
  transform_state.SetQuad(gfx::QuadF(gfx::RectF(rect)));

  return does_intersect;
}

// TODO (lajava) Shouldn't we implement these functions based on physical
// direction ?.
LayoutUnit LayoutBox::OverrideContainingBlockContentLogicalWidth() const {
  NOT_DESTROYED();
  DCHECK(HasOverrideContainingBlockContentLogicalWidth());
  return rare_data_->override_containing_block_content_logical_width_;
}

// TODO (lajava) Shouldn't we implement these functions based on physical
// direction ?.
bool LayoutBox::HasOverrideContainingBlockContentLogicalWidth() const {
  NOT_DESTROYED();
  return rare_data_ &&
         rare_data_->has_override_containing_block_content_logical_width_;
}

// TODO (lajava) Shouldn't we implement these functions based on physical
// direction ?.
void LayoutBox::SetOverrideContainingBlockContentLogicalWidth(
    LayoutUnit logical_width) {
  NOT_DESTROYED();
  DCHECK_GE(logical_width, LayoutUnit(-1));
  EnsureRareData().override_containing_block_content_logical_width_ =
      logical_width;
  EnsureRareData().has_override_containing_block_content_logical_width_ = true;
}

bool LayoutBox::HitTestAllPhases(HitTestResult& result,
                                 const HitTestLocation& hit_test_location,
                                 const PhysicalOffset& accumulated_offset) {
  NOT_DESTROYED();
  if (!MayIntersect(result, hit_test_location, accumulated_offset))
    return false;
  return LayoutObject::HitTestAllPhases(result, hit_test_location,
                                        accumulated_offset);
}

bool LayoutBox::HitTestOverflowControl(
    HitTestResult& result,
    const HitTestLocation& hit_test_location,
    const PhysicalOffset& adjusted_location) const {
  NOT_DESTROYED();

  auto* scrollable_area = GetScrollableArea();
  if (!scrollable_area)
    return false;

  if (!VisibleToHitTestRequest(result.GetHitTestRequest()))
    return false;

  PhysicalOffset local_point = hit_test_location.Point() - adjusted_location;
  if (!scrollable_area->HitTestOverflowControls(result,
                                                ToRoundedPoint(local_point)))
    return false;

  UpdateHitTestResult(result, local_point);
  return result.AddNodeToListBasedTestResult(
             NodeForHitTest(), hit_test_location) == kStopHitTesting;
}

void LayoutBox::Paint(const PaintInfo& paint_info) const {
  NOT_DESTROYED();
  NOTREACHED();
}

PhysicalRect LayoutBox::BackgroundPaintedExtent() const {
  NOT_DESTROYED();
  return PhysicalBackgroundRect(kBackgroundPaintedExtent);
}

bool LayoutBox::BackgroundIsKnownToBeOpaqueInRect(
    const PhysicalRect& local_rect) const {
  NOT_DESTROYED();
  // If the element has appearance, it might be painted by theme.
  // We cannot be sure if theme paints the background opaque.
  // In this case it is safe to not assume opaqueness.
  // FIXME: May be ask theme if it paints opaque.
  if (StyleRef().HasEffectiveAppearance())
    return false;
  // FIXME: Check the opaqueness of background images.

  // FIXME: Use rounded rect if border radius is present.
  if (StyleRef().HasBorderRadius())
    return false;
  if (StyleRef().HasBorderShape()) {
    return false;
  }
  if (HasClipPath())
    return false;
  if (StyleRef().HasBlendMode())
    return false;
  return PhysicalBackgroundRect(kBackgroundKnownOpaqueRect)
      .Contains(local_rect);
}

// Note that callers are responsible for checking
// ChildPaintBlockedByDisplayLock(), since that is a property of the parent
// rather than of the child.
static bool IsCandidateForOpaquenessTest(const LayoutBox& child_box) {
  // Skip all layers to simplify ForegroundIsKnownToBeOpaqueInRect(). This
  // covers cases of clipped, transformed, translucent, composited, etc.
  if (child_box.HasLayer())
    return false;
  const ComputedStyle& child_style = child_box.StyleRef();
  if (child_style.Visibility() != EVisibility::kVisible ||
      child_style.ShapeOutside()) {
    return false;
  }
  if (child_box.StitchedSize().IsZero()) {
    return false;
  }
  // A replaced element with border-radius always clips the content.
  if (child_box.IsLayoutReplaced() && child_style.HasBorderRadius())
    return false;
  return true;
}

bool LayoutBox::ForegroundIsKnownToBeOpaqueInRect(
    const PhysicalRect& local_rect,
    unsigned max_depth_to_test) const {
  NOT_DESTROYED();
  if (!max_depth_to_test)
    return false;
  if (ChildPaintBlockedByDisplayLock())
    return false;
  for (LayoutObject* child = SlowFirstChild(); child;
       child = child->NextSibling()) {
    // We do not bother checking descendants of |LayoutInline|, including
    // block-in-inline, because the cost of checking them overweights the
    // benefits.
    if (!child->IsBox())
      continue;
    auto* child_box = To<LayoutBox>(child);
    if (!IsCandidateForOpaquenessTest(*child_box))
      continue;
    DCHECK(!child_box->IsPositioned());
    PhysicalRect child_local_rect = local_rect;
    child_local_rect.Move(-child_box->PhysicalLocation());
    if (child_local_rect.Y() < 0 || child_local_rect.X() < 0) {
      // If there is unobscured area above/left of a static positioned box then
      // the rect is probably not covered. This can cause false-negative in
      // non-horizontal-tb writing mode but is allowed.
      return false;
    }
    if (child_local_rect.Bottom() > child_box->StitchedSize().height ||
        child_local_rect.Right() > child_box->StitchedSize().width) {
      continue;
    }
    if (RuntimeEnabledFeatures::CompositeBGColorAnimationEnabled() &&
        child->StyleRef().HasCurrentBackgroundColorAnimation()) {
      return false;
    }
    if (child_box->BackgroundIsKnownToBeOpaqueInRect(child_local_rect))
      return true;
    if (child_box->ForegroundIsKnownToBeOpaqueInRect(child_local_rect,
                                                     max_depth_to_test - 1))
      return true;
  }
  return false;
}

DISABLE_CFI_PERF
bool LayoutBox::ComputeBackgroundIsKnownToBeObscured() const {
  NOT_DESTROYED();
  if (ScrollsOverflow())
    return false;
  // If the element has overflow clipping, children are clipped to the padding
  // box. If the background extends beyond the padding box (e.g., visible
  // through transparent or non-solid borders), children cannot fully obscure
  // the background in the border area.
  if (HasNonVisibleOverflow() &&
      !BackgroundClipBorderBoxIsEquivalentToPaddingBox()) {
    return false;
  }
  // Test to see if the children trivially obscure the background.
  if (!StyleRef().HasBackground())
    return false;
  // Root background painting is special.
  if (IsA<LayoutView>(this))
    return false;
  if (StyleRef().BoxShadow())
    return false;
  if (IsFragmented()) {
    // The code here doesn't really understand fragmentation, but rather works
    // in the stitched-fragments coordinate system (pretending that there's no
    // block fragmentation, imagining that all fragments are laid out in a tall
    // continuous strip after oneanother).
    return false;
  }
  return ForegroundIsKnownToBeOpaqueInRect(BackgroundPaintedExtent(),
                                           kBackgroundObscurationTestMaxDepth);
}

void LayoutBox::ImageChanged(WrappedImagePtr image,
                             CanDeferInvalidation defer) {
  NOT_DESTROYED();
  LayoutBoxModelObject::ImageChanged(image, defer);

  bool is_box_reflect_image =
      (StyleRef().BoxReflect() && StyleRef().BoxReflect()->Mask().GetImage() &&
       StyleRef().BoxReflect()->Mask().GetImage()->Data() == image);

  if (is_box_reflect_image && HasLayer()) {
    Layer()->SetFilterOnEffectNodeDirty();
    SetNeedsPaintPropertyUpdate();
  }

  // TODO(chrishtr): support delayed paint invalidation for animated border
  // images.
  if ((StyleRef().BorderImage().GetImage() &&
       StyleRef().BorderImage().GetImage()->Data() == image) ||
      (StyleRef().MaskBoxImage().GetImage() &&
       StyleRef().MaskBoxImage().GetImage()->Data() == image) ||
      is_box_reflect_image) {
    SetShouldDoFullPaintInvalidationWithoutLayoutChange(
        PaintInvalidationReason::kImage);
  }

  if (!BackgroundTransfersToView()) {
    for (const FillLayer* layer = &StyleRef().BackgroundLayers(); layer;
         layer = layer->Next()) {
      if (layer->GetImage() && image == layer->GetImage()->Data()) {
        bool maybe_animated =
            layer->GetImage()->CachedImage() &&
            layer->GetImage()->CachedImage()->GetImage() &&
            layer->GetImage()->CachedImage()->GetImage()->MaybeAnimated();
        if (defer == CanDeferInvalidation::kYes && maybe_animated)
          SetMayNeedPaintInvalidationAnimatedBackgroundImage();
        else
          SetBackgroundNeedsFullPaintInvalidation();
        break;
      }
    }
  }

  ShapeValue* shape_outside_value = StyleRef().ShapeOutside();
  if (!GetFrameView()->IsInPerformLayout() && IsFloating() &&
      shape_outside_value && shape_outside_value->GetImage() &&
      shape_outside_value->GetImage()->Data() == image) {
    ShapeOutsideInfo& info = ShapeOutsideInfo::EnsureInfo(*this);
    if (!info.IsComputingShape()) {
      info.MarkShapeAsDirty();
      if (auto* containing_block = ContainingBlock()) {
        containing_block->SetChildNeedsLayout();
      }
    }
  }
}

ResourcePriority LayoutBox::ComputeResourcePriority() const {
  NOT_DESTROYED();
  PhysicalRect view_bounds = ViewRect();
  PhysicalRect object_bounds = PhysicalContentBoxRect();
  // TODO(japhet): Is this IgnoreTransforms correct? Would it be better to use
  // the visual rect (which has ancestor clips and transforms applied)? Should
  // we map to the top-level viewport instead of the current (sub) frame?
  object_bounds.Move(LocalToAbsolutePoint(
      PhysicalOffset(), {MapCoordinatesMode::kIgnoreTransforms}));

  // The object bounds might be empty right now, so intersects will fail since
  // it doesn't deal with empty rects. Use PhysicalRect::Contains in that case.
  bool is_visible;
  if (!object_bounds.IsEmpty())
    is_visible = view_bounds.Intersects(object_bounds);
  else
    is_visible = view_bounds.Contains(object_bounds);

  PhysicalRect screen_rect;
  if (!object_bounds.IsEmpty()) {
    screen_rect = view_bounds;
    screen_rect.Intersect(object_bounds);
  }

  int screen_area = 0;
  if (!screen_rect.IsEmpty() && is_visible)
    screen_area = (screen_rect.Width() * screen_rect.Height()).ToInt();
  return ResourcePriority(
      is_visible ? ResourcePriority::kVisible : ResourcePriority::kNotVisible,
      screen_area);
}

void LayoutBox::LocationChanged() {
  NOT_DESTROYED();
  // The location may change because of layout of other objects. Should check
  // this object for paint invalidation.
  if (!NeedsLayout())
    SetShouldCheckForPaintInvalidation();

  if (RuntimeEnabledFeatures::OffsetPathTransformUpdateFixEnabled()) {
    if (HasLayer() && StyleRef().HasOffset()) {
      Layer()->UpdateTransform();
    }
  }
}

void LayoutBox::SizeChanged() {
  NOT_DESTROYED();
  SetScrollableAreaSizeChanged(true);
  // The size may change because of layout of other objects. Should check this
  // object for paint invalidation.
  if (!NeedsLayout())
    SetShouldCheckForPaintInvalidation();
  // In flipped blocks writing mode, our children can change physical location,
  // but their flipped location remains the same.
  if (HasFlippedBlocksWritingMode()) {
    if (ChildrenInline())
      SetSubtreeShouldDoFullPaintInvalidation();
    else
      SetSubtreeShouldCheckForPaintInvalidation();
  }
}

bool LayoutBox::IntersectsVisibleViewport() const {
  NOT_DESTROYED();
  LayoutView* layout_view = View();
  while (auto* owner = layout_view->GetFrame()->OwnerLayoutObject()) {
    layout_view = owner->View();
  }
  // If this is the outermost LayoutView then it will always intersect. (`rect`
  // will be the viewport in that case.)
  if (this == layout_view) {
    return true;
  }
  PhysicalRect rect = VisualOverflowRect();
  MapToVisualRectInAncestorSpace(layout_view, rect);
  return rect.Intersects(PhysicalRect(
      layout_view->GetFrameView()->GetScrollableArea()->VisibleContentRect(
          kExcludeScrollbars)));
}

void LayoutBox::EnsureIsReadyForPaintInvalidation() {
  NOT_DESTROYED();
  LayoutBoxModelObject::EnsureIsReadyForPaintInvalidation();

  bool new_obscured = ComputeBackgroundIsKnownToBeObscured();
  if (BackgroundIsKnownToBeObscured() != new_obscured) {
    SetBackgroundIsKnownToBeObscured(new_obscured);
    SetBackgroundNeedsFullPaintInvalidation();
  }

  if (MayNeedPaintInvalidationAnimatedBackgroundImage() &&
      !BackgroundIsKnownToBeObscured()) {
    SetBackgroundNeedsFullPaintInvalidation();
    SetShouldDelayFullPaintInvalidation();
  }

  if (ShouldDelayFullPaintInvalidation() && IntersectsVisibleViewport()) {
    // Do regular full paint invalidation if the object with delayed paint
    // invalidation is on screen.
    ClearShouldDelayFullPaintInvalidation();
    DCHECK(ShouldDoFullPaintInvalidation());
  }
}

void LayoutBox::InvalidatePaint(const PaintInvalidatorContext& context) const {
  NOT_DESTROYED();
  BoxPaintInvalidator(*this, context).InvalidatePaint();
}

void LayoutBox::ClearPaintFlags() {
  NOT_DESTROYED();
  LayoutObject::ClearPaintFlags();

  if (auto* scrollable_area = GetScrollableArea()) {
    if (auto* scrollbar =
            DynamicTo<CustomScrollbar>(scrollable_area->HorizontalScrollbar()))
      scrollbar->ClearPaintFlags();
    if (auto* scrollbar =
            DynamicTo<CustomScrollbar>(scrollable_area->VerticalScrollbar()))
      scrollbar->ClearPaintFlags();
  }
}

PhysicalRect LayoutBox::OverflowClipRect(
    OverlayScrollbarClipBehavior overlay_scrollbar_clip_behavior) const {
  NOT_DESTROYED();
  PhysicalRect clip_rect;

  if (IsEffectiveRootScroller()) {
    // If this box is the effective root scroller, use the viewport clipping
    // rect since it will account for the URL bar correctly which the border
    // box does not. We can do this because the effective root scroller is
    // restricted such that it exactly fills the viewport. See
    // RootScrollerController::IsValidRootScroller()
    clip_rect = PhysicalRect(PhysicalOffset(), View()->ViewRect().size);
  } else {
    clip_rect = PhysicalBorderBoxRect();
    clip_rect.Contract(BorderOutsets());

    // Videos need to be pre-snapped so that they line up with the
    // display_rect and can enable hardware overlays.
    // Embedded objects are always sized to fit the content rect, but they
    // could overflow by 1px due to pre-snapping. Adjust clip rect to
    // match pre-snapped box as a special case.
    if (IsVideo() || IsLayoutEmbeddedContent())
      clip_rect = LayoutReplaced::PreSnappedRectForPersistentSizing(clip_rect);

    if (HasNonVisibleOverflow()) {
      const auto overflow_clip = GetOverflowClipAxes();
      if (overflow_clip != kOverflowClipBothAxis) {
        ApplyVisibleOverflowToClipRect(overflow_clip, clip_rect);
      } else if (ShouldApplyOverflowClipMargin()) {
        switch (StyleRef().OverflowClipMargin()->GetReferenceBox()) {
          case StyleOverflowClipMargin::ReferenceBox::kBorderBox:
            clip_rect.Expand(BorderOutsets());
            break;
          case StyleOverflowClipMargin::ReferenceBox::kPaddingBox:
            break;
          case StyleOverflowClipMargin::ReferenceBox::kContentBox:
            clip_rect.Contract(PaddingOutsets());
            break;
        }
        clip_rect.Inflate(StyleRef().OverflowClipMargin()->GetMargin());
      }
    }
  }

  if (IsScrollContainer()) {
    // The additional gutters created by scrollbar-gutter don't occlude the
    // content underneath, so they should not be clipped out here.
    // See https://crbug.com/710214
    ExcludeScrollbars(clip_rect, overlay_scrollbar_clip_behavior,
                      kExcludeScrollbarGutter);
  }

  return clip_rect;
}

PhysicalRect LayoutBox::OverflowClipRectForScrollNode() const {
  return OverflowClipRect();
}

void LayoutBox::ExcludeScrollbars(
    PhysicalRect& rect,
    OverlayScrollbarClipBehavior overlay_scrollbar_clip_behavior,
    ShouldIncludeScrollbarGutter include_scrollbar_gutter) const {
  NOT_DESTROYED();
  if (CanSkipComputeScrollbars())
    return;

  PhysicalBoxStrut scrollbars = ComputeScrollbarsInternal(
      kDoNotClampToContentBox, overlay_scrollbar_clip_behavior,
      include_scrollbar_gutter);
  rect.offset.top += scrollbars.top;
  rect.offset.left += scrollbars.left;
  rect.size.width -= scrollbars.HorizontalSum();
  rect.size.height -= scrollbars.VerticalSum();
  rect.size.ClampNegativeToZero();
}

PhysicalRect LayoutBox::CSSClipRect() const {
  NOT_DESTROYED();
  PhysicalSize stitched_size = StitchedSize();
  PhysicalRect clip_rect(PhysicalOffset(), stitched_size);
  LayoutUnit width = stitched_size.width;
  LayoutUnit height = stitched_size.height;

  if (!StyleRef().ClipLeft().IsAuto()) {
    LayoutUnit c = ValueForLength(StyleRef().ClipLeft(), width);
    clip_rect.offset.left += c;
    clip_rect.size.width -= c;
  }

  if (!StyleRef().ClipRight().IsAuto()) {
    clip_rect.size.width -=
        width - ValueForLength(StyleRef().ClipRight(), width);
  }

  if (!StyleRef().ClipTop().IsAuto()) {
    LayoutUnit c = ValueForLength(StyleRef().ClipTop(), height);
    clip_rect.offset.top += c;
    clip_rect.size.height -= c;
  }

  if (!StyleRef().ClipBottom().IsAuto()) {
    clip_rect.size.height -=
        height - ValueForLength(StyleRef().ClipBottom(), height);
  }

  return clip_rect;
}

LayoutUnit LayoutBox::ContainingBlockLogicalHeightForRelPositioned() const {
  NOT_DESTROYED();
  DCHECK(IsRelPositioned());

  const auto* container = To<LayoutBoxModelObject>(Container());

  if (const auto* box = DynamicTo<LayoutBox>(container)) {
    const PhysicalSize size = box->PhysicalContentBoxRect().size;
    return box->StyleRef().IsHorizontalWritingMode() ? size.height : size.width;
  }

  // TODO(ikilpatrick): This is resolving percentages against incorrectly if
  // the container is an inline.
  const auto* layout_inline = To<LayoutInline>(container);

  // If the containing block is empty, return a height of 0.
  if (!layout_inline->HasInlineFragments()) {
    return LayoutUnit();
  }

  const LayoutUnit block_size =
      ToLogicalSize(layout_inline->PhysicalLinesBoundingBox().size,
                    layout_inline->StyleRef().GetWritingMode())
          .block_size;
  return (block_size - layout_inline->BorderPaddingBlockSize())
      .ClampNegativeToZero();
}

LayoutUnit LayoutBox::ContainingBlockLogicalWidthForContent() const {
  NOT_DESTROYED();
  if (HasOverrideContainingBlockContentLogicalWidth())
    return OverrideContainingBlockContentLogicalWidth();

  LayoutBlock* cb = ContainingBlock();
  if (IsOutOfFlowPositioned()) {
    const PhysicalSize size = cb->PhysicalPaddingBoxRect().size;
    return cb->StyleRef().IsHorizontalWritingMode() ? size.width : size.height;
  }
  return cb->ContentLogicalWidth();
}

PhysicalOffset LayoutBox::OffsetFromContainerInternal(
    const LayoutObject* o,
    MapCoordinatesFlags mode) const {
  NOT_DESTROYED();
  DCHECK_EQ(o, Container());

  PhysicalOffset offset = PhysicalLocation();

  if (NeedsAnchorPositionScrollAdjustment()) {
    offset += AnchorPositionScrollTranslationOffset();
  }

  return offset + LayoutBoxModelObject::OffsetFromContainerInternal(o, mode);
}

bool LayoutBox::HasInlineFragments() const {
  NOT_DESTROYED();
  return first_fragment_item_index_;
}

void LayoutBox::ClearFirstInlineFragmentItemIndex() {
  NOT_DESTROYED();
  CHECK(IsInLayoutNGInlineFormattingContext()) << *this;
  first_fragment_item_index_ = 0u;
}

void LayoutBox::SetFirstInlineFragmentItemIndex(wtf_size_t index) {
  NOT_DESTROYED();
  CHECK(IsInLayoutNGInlineFormattingContext()) << *this;
  DCHECK_NE(index, 0u);
  first_fragment_item_index_ = index;
}

void LayoutBox::InLayoutNGInlineFormattingContextWillChange(bool new_value) {
  NOT_DESTROYED();
  if (IsInLayoutNGInlineFormattingContext())
    ClearFirstInlineFragmentItemIndex();
}

bool LayoutBox::PhysicalFragmentList::MayHaveFragmentItems() const {
  return !IsEmpty() && front().IsInlineFormattingContext();
}

bool LayoutBox::PhysicalFragmentList::SlowHasFragmentItems() const {
  for (const PhysicalBoxFragment& fragment : *this) {
    if (fragment.HasItems())
      return true;
  }
  return false;
}

wtf_size_t LayoutBox::PhysicalFragmentList::IndexOf(
    const PhysicalBoxFragment& fragment) const {
  wtf_size_t index = 0;
  for (const auto& result : layout_results_) {
    if (&result->GetPhysicalFragment() == &fragment) {
      return index;
    }
    ++index;
  }
  return kNotFound;
}

bool LayoutBox::PhysicalFragmentList::Contains(
    const PhysicalBoxFragment& fragment) const {
  return IndexOf(fragment) != kNotFound;
}

void LayoutBox::AddMeasureLayoutResult(const LayoutResult* result) {
  NOT_DESTROYED();
  // Ensure the given result is valid for the measure cache.
  if (result->Status() != LayoutResult::kSuccess) {
    return;
  }
  if (result->GetConstraintSpaceForCaching().CacheSlot() !=
      LayoutResultCacheSlot::kMeasure) {
    return;
  }
  DCHECK(
      To<PhysicalBoxFragment>(result->GetPhysicalFragment()).IsOnlyForNode());

  if (!measure_cache_) {
    measure_cache_ = MakeGarbageCollected<MeasureCache>();
  }
  // Clear out old measure results if we need non-simplifed layout.
  if (NeedsLayout() && !NeedsSimplifiedLayoutOnly()) {
    measure_cache_->Clear();
  }
  measure_cache_->Add(result);
}

void LayoutBox::SetCachedLayoutResult(const LayoutResult* result,
                                      wtf_size_t index) {
  NOT_DESTROYED();
  if (result->GetConstraintSpaceForCaching().CacheSlot() ==
      LayoutResultCacheSlot::kMeasure) {
    DCHECK(!result->GetPhysicalFragment().GetBreakToken());
    DCHECK(
        To<PhysicalBoxFragment>(result->GetPhysicalFragment()).IsOnlyForNode());
    DCHECK_EQ(index, 0u);
    // We don't early return here, when setting the "measure" result we also
    // set the "layout" result.
    if (measure_cache_) {
      measure_cache_->InvalidateItems();
    }
    AddMeasureLayoutResult(result);
    if (IsTableCell()) {
      To<LayoutTableCell>(this)->InvalidateLayoutResultCacheAfterMeasure();
    }
  } else {
    // We have a "layout" result, and we may need to clear the old "measure"
    // result if we needed non-simplified layout.
    if (NeedsLayout() && !NeedsSimplifiedLayoutOnly()) {
      if (measure_cache_) {
        measure_cache_->Clear();
      }
    }
  }

  // If we're about to cache a layout result that is different than the measure
  // result, mark the measure result's fragment as no longer having valid
  // children. It can still be used to query information about this box's
  // fragment from the measure pass, but children might be out of sync with the
  // latest version of the tree.
  if (measure_cache_) {
    measure_cache_->SetFragmentChildrenInvalid(result);
  }

  SetLayoutResult(result, index);
}

void LayoutBox::SetLayoutResult(const LayoutResult* result, wtf_size_t index) {
  NOT_DESTROYED();
  DCHECK_EQ(result->Status(), LayoutResult::kSuccess);
  const auto& box_fragment =
      To<PhysicalBoxFragment>(result->GetPhysicalFragment());

  if (index != kNotFound && layout_results_.size() > index) {
    if (layout_results_.size() > index + 1) {
      // If we have reached the end, remove surplus results from previous
      // layout.
      //
      // Note: When an OOF is fragmented, we wait to lay it out at the
      // fragmentation context root. If the OOF lives above a column spanner,
      // though, we may lay it out early to make sure the OOF contributes to the
      // correct column block-size. Thus, if an item broke as a result of a
      // spanner, remove subsequent sibling items so that OOFs don't try to
      // access old fragments.
      //
      // Additionally, if an outer multicol has a spanner break, we may try
      // to access old fragments of the inner multicol if it hasn't completed
      // layout yet. Remove subsequent multicol fragments to avoid OOFs from
      // trying to access old fragments.
      //
      // TODO(layout-dev): Other solutions to handling interactions between OOFs
      // and spanner breaks may need to be considered.
      if (!box_fragment.GetBreakToken() ||
          box_fragment.GetBreakToken()->IsCausedByColumnSpanner() ||
          box_fragment.IsFragmentationContextRoot()) {
        // Before forgetting any old fragments and their items, we need to clear
        // associations.
        if (box_fragment.IsInlineFormattingContext())
          FragmentItems::ClearAssociatedFragments(this);
        ShrinkLayoutResults(index + 1);
      }
    }
    ReplaceLayoutResult(std::move(result), index);
    return;
  }

  DCHECK(index == layout_results_.size() || index == kNotFound);
  AppendLayoutResult(result);

  if (!box_fragment.GetBreakToken()) {
    FinalizeLayoutResults();
  }
}

void LayoutBox::AppendLayoutResult(const LayoutResult* result) {
  NOT_DESTROYED();
  const auto& fragment = To<PhysicalBoxFragment>(result->GetPhysicalFragment());
  // |layout_results_| is particularly critical when side effects are disabled.
  DCHECK(!DisableLayoutSideEffectsScope::IsDisabled());
  layout_results_.push_back(std::move(result));
  InvalidateCachedGeometry();
  CheckDidAddFragment(*this, fragment);
}

void LayoutBox::ReplaceLayoutResult(const LayoutResult* result,
                                    wtf_size_t index) {
  NOT_DESTROYED();
  DCHECK_LE(index, layout_results_.size());
  const LayoutResult* old_result = layout_results_[index];
  if (old_result == result)
    return;
  const auto& fragment = To<PhysicalBoxFragment>(result->GetPhysicalFragment());
  const auto& old_fragment = old_result->GetPhysicalFragment();
  bool got_new_fragment = &old_fragment != &fragment;
  if (got_new_fragment) {
    if (HasFragmentItems()) {
      if (!index)
        InvalidateItems(*old_result);
      FragmentItems::ClearAssociatedFragments(this);
    }
    // We are about to replace a fragment, and the size may have changed. The
    // inline-size and total stitched block-size may still remain unchanged,
    // though, and pre-paint can only detect changes in the total stitched
    // size. So this is our last chance to detect any size changes at the
    // fragment itself. Only do this if we're fragmented, though. Otherwise
    // leave it to pre-paint to figure out if invalidation is really required,
    // since it's fine to just check the stitched sizes when not fragmented.
    // Unconditionally requiring full paint invalidation at size changes may be
    // unnecessary and expensive.
    if (layout_results_.size() > 1 && fragment.Size() != old_fragment.Size()) {
      SetShouldDoFullPaintInvalidation();
    }
  }
  // |layout_results_| is particularly critical when side effects are disabled.
  DCHECK(!DisableLayoutSideEffectsScope::IsDisabled());
  layout_results_[index] = std::move(result);
  InvalidateCachedGeometry();
  CheckDidAddFragment(*this, fragment, index);

  if (got_new_fragment && !fragment.GetBreakToken()) {
    // If this is the last result, the results vector better agree on that.
    DCHECK_EQ(index, layout_results_.size() - 1);

    FinalizeLayoutResults();
  }
}

void LayoutBox::FinalizeLayoutResults() {
  NOT_DESTROYED();
  DCHECK(!layout_results_.empty());
  DCHECK(!layout_results_.back()->GetPhysicalFragment().GetBreakToken());
#if EXPENSIVE_DCHECKS_ARE_ON()
  CheckMayHaveFragmentItems();
#endif
  // If we've added all the results we were going to, and the node establishes
  // an inline formatting context, we have some finalization to do.
  if (HasFragmentItems()) {
    FragmentItems::FinalizeAfterLayout(layout_results_,
                                       *To<LayoutBlockFlow>(this));
  }
}

void LayoutBox::RebuildFragmentTreeSpine() {
  NOT_DESTROYED();
  DCHECK(PhysicalFragmentCount());
  // If this box has an associated layout-result, rebuild the spine of the
  // fragment-tree to ensure consistency.
  LayoutBox* container = this;
  while (container && container->PhysicalFragmentCount() &&
         !container->NeedsLayout()) {
    for (auto& result : container->layout_results_)
      result = LayoutResult::CloneWithPostLayoutFragments(*result);
    if (MeasureCache* measure_cache = container->measure_cache_) {
      // In case any of the now-replaced cached results above were in fact
      // measure-results (see how SetCachedLayoutResult() may write into both
      // the measure cache and the layout results vector), the measure results
      // are now outdated. Remove them.
      measure_cache->Clear();
    }
    container = container->ContainingNGBox();
  }

  if (container && container->NeedsLayout()) {
    // We stopped walking upwards because this container needs layout. This
    // typically means that updating the associated layout results is waste of
    // time, since we're probably going to lay it out anyway. However, in some
    // cases the container is going to hit the cache and therefore not perform
    // actual layout. If this happens, we need to update the layout results at
    // that point.
    container->SetHasBrokenSpine();
  }
}

void LayoutBox::ShrinkLayoutResults(wtf_size_t results_to_keep) {
  NOT_DESTROYED();
  DCHECK_GE(layout_results_.size(), results_to_keep);
  // Invalidate if inline |DisplayItemClient|s will be destroyed.
  for (wtf_size_t i = results_to_keep; i < layout_results_.size(); i++)
    InvalidateItems(*layout_results_[i]);
  // |layout_results_| is particularly critical when side effects are disabled.
  DCHECK(!DisableLayoutSideEffectsScope::IsDisabled());
  layout_results_.Shrink(results_to_keep);
  InvalidateCachedGeometry();
}

#if EXPENSIVE_DCHECKS_ARE_ON()
void LayoutBox::CheckMayHaveFragmentItems() const {
  NOT_DESTROYED();
  if (!MayHaveFragmentItems()) {
    DCHECK(!PhysicalFragments().SlowHasFragmentItems());
  }
}
#endif

void LayoutBox::InvalidateCachedGeometry() {
  NOT_DESTROYED();
  SetHasValidCachedGeometry(false);
}

// static
void LayoutBox::InvalidateItems(const LayoutResult& result) {
  // Invalidate if inline |DisplayItemClient|s will be destroyed.
  const auto& box_fragment =
      To<PhysicalBoxFragment>(result.GetPhysicalFragment());
  if (!box_fragment.HasItems())
    return;
  ObjectPaintInvalidator(*box_fragment.GetLayoutObject())
      .SlowSetPaintingLayerNeedsRepaint();
}

const LayoutResult* LayoutBox::GetCachedLayoutResult(
    const BlockBreakToken* break_token) const {
  NOT_DESTROYED();
  wtf_size_t index = FragmentIndex(break_token);
  if (index >= layout_results_.size())
    return nullptr;
  const LayoutResult* result = layout_results_[index];
  DCHECK(!result->GetPhysicalFragment().IsLayoutObjectDestroyedOrMoved() ||
         BeingDestroyed());
  return result;
}

const LayoutResult* LayoutBox::GetCachedMeasureResult(
    const ConstraintSpace& space,
    std::optional<FragmentGeometry>* fragment_geometry) const {
  NOT_DESTROYED();
  if (!measure_cache_) {
    return nullptr;
  }

  // If we've already had an actual layout pass, and the node fragmented, we
  // cannot reliably re-use the measure result. What we want to avoid here is
  // simplified layout inside a measure-result, as that would descend into a
  // fragment subtree generated by actual (fragmented) layout, which is
  // invalid. But it seems safer to stop such attempts here, so that we don't
  // hand out results that may cause problems if we end up with simplified
  // layout inside.
  if (!layout_results_.empty()) {
    const PhysicalBoxFragment* first_fragment = GetPhysicalFragment(0);
    if (first_fragment->GetBreakToken()) {
      return nullptr;
    }
  }

  return measure_cache_
             ? measure_cache_->Find(BlockNode(const_cast<LayoutBox*>(this)),
                                    space, fragment_geometry)
             : nullptr;
}

const LayoutResult* LayoutBox::GetSingleCachedLayoutResult() const {
  DCHECK_LE(layout_results_.size(), 1u);
  return GetCachedLayoutResult(nullptr);
}

const LayoutResult* LayoutBox::GetSingleCachedMeasureResultForTesting() const {
  NOT_DESTROYED();
  return measure_cache_ ? measure_cache_->GetLastForTesting() : nullptr;
}

const LayoutResult* LayoutBox::GetLayoutResult(wtf_size_t i) const {
  NOT_DESTROYED();
  return layout_results_[i].Get();
}

const PhysicalBoxFragment&
LayoutBox::PhysicalFragmentList::Iterator::operator*() const {
  return To<PhysicalBoxFragment>((*iterator_)->GetPhysicalFragment());
}

const PhysicalBoxFragment& LayoutBox::PhysicalFragmentList::front() const {
  return To<PhysicalBoxFragment>(
      layout_results_.front()->GetPhysicalFragment());
}

const PhysicalBoxFragment& LayoutBox::PhysicalFragmentList::back() const {
  return To<PhysicalBoxFragment>(layout_results_.back()->GetPhysicalFragment());
}

const FragmentData* LayoutBox::FragmentDataFromPhysicalFragment(
    const PhysicalBoxFragment& physical_fragment) const {
  NOT_DESTROYED();
  return &FragmentList().at(BoxFragmentIndex(physical_fragment));
}

bool LayoutBox::IsValidColumnSpannerInTree(const ComputedStyle& style) const {
  NOT_DESTROYED();
  if (!Parent() || !IsInsideMulticol() || !IsSelfValidColumnSpanner(style)) {
    return false;
  }

  // This looks like a spanner, but if we're inside something unbreakable or
  // something that establishes a new formatting context, it's not to be treated
  // as one.
  return DoesAncestryAllowColumnSpanner(style);
}

bool LayoutBox::IsSelfValidColumnSpanner(const ComputedStyle& style) const {
  NOT_DESTROYED();
  // Note that this function may be called in many circumstances, such as before
  // it is inserted into the tree, and even as part of calculating the
  // containing block. Be careful.
  if (style.GetColumnSpan() != EColumnSpan::kAll) {
    return false;
  }

  // The spec says that column-span only applies to in-flow block-level
  // elements.
  if (ShouldBeHandledAsInline(style) || ShouldBeHandledAsFloating(style) ||
      ToPositionedState(style.GetPosition()) == kIsOutOfFlowPositioned) {
    return false;
  }

  return true;
}

bool LayoutBox::DoesAncestryAllowColumnSpanner(
    const ComputedStyle& style) const {
  NOT_DESTROYED();
  DCHECK(IsInsideMulticol());
  for (const LayoutBox* ancestor = Parent()->EnclosingBox(); ancestor;
       ancestor = ancestor->ContainingBlock()) {
    if (ancestor->IsMulticolContainer()) {
      return true;
    }
    if (ancestor->ShouldPreventColumnSpannerDescendants()) {
      return false;
    }
  }
  return false;
}

bool LayoutBox::ShouldPreventColumnSpannerDescendants() const {
  NOT_DESTROYED();

  if (IsSelfValidColumnSpanner()) {
    // No spanners inside spanners in the same multicol context.
    return true;
  }

  const auto* block_flow = DynamicTo<LayoutBlockFlow>(this);
  if (!block_flow) {
    // Needs to be in a block-flow container, and not e.g. a table.
    return true;
  }

  // Make sure that there's nothing about this ancestor that prevents `this`
  // from becoming a column spanner. We require the ancestor to participate in
  // the block formatting context established by the multicol container
  // (i.e. that there are no formatting contexts in-between). Transforms are
  // also forbidden, since they insist on being in the containing block chain
  // for everything inside, which will easily conflict with a spanners's need to
  // have the multicol container as its direct containing block.
  if (block_flow->IsMonolithic() || block_flow->CreatesNewFormattingContext() ||
      block_flow->CanContainFixedPositionObjects()) {
    return true;
  }
  DCHECK(!IsColumnSpanAll());
  return false;
}

void LayoutBox::MarkColumnSpannerCandidatesForLayoutIfNeeded() {
  NOT_DESTROYED();

  // This function examines relevant descendants, and its ancestry, but not
  // itself. It assumes that it has just changed whether it allows spanners
  // inside or not.
  DCHECK(IsInsideMulticol());

  if (IsMulticolContainer() || IsSelfValidColumnSpanner()) {
    return;
  }

  // First check if we really are inside multicol, and that nothing on the way
  // prevents descendants from becoming spanners.
  if (!DoesAncestryAllowColumnSpanner()) {
    return;
  }

  // Look for spanner descendants, and mark them for layout.
  for (LayoutObject* descendant = NextInPreOrder(this); descendant;) {
    if (auto* box = DynamicTo<LayoutBox>(descendant)) {
      if (box->IsSelfValidColumnSpanner()) {
        box->MarkParentForSpannerOrOutOfFlowPositionedChange();
        descendant = descendant->NextInPreOrderAfterChildren(this);
        continue;
      }
      if (box->ShouldPreventColumnSpannerDescendants()) {
        descendant = descendant->NextInPreOrderAfterChildren(this);
        continue;
      }
    }
    descendant = descendant->NextInPreOrder(this);
  }
}

void LayoutBox::InflateVisualRectForFilterUnderContainer(
    TransformState& transform_state,
    const LayoutObject& container,
    const LayoutBoxModelObject* ancestor_to_stop_at) const {
  NOT_DESTROYED();
  transform_state.Flatten();
  // Apply visual overflow caused by reflections and filters defined on objects
  // between this object and container (not included) or ancestorToStopAt
  // (included).
  PhysicalOffset offset_from_container = OffsetFromContainer(&container);
  transform_state.Move(offset_from_container);
  for (LayoutObject* parent = Parent(); parent && parent != container;
       parent = parent->Parent()) {
    if (parent->IsBox()) {
      // Convert rect into coordinate space of parent to apply parent's
      // reflection and filter.
      PhysicalOffset parent_offset = parent->OffsetFromAncestor(&container);
      transform_state.Move(-parent_offset);
      To<LayoutBox>(parent)->InflateVisualRectForFilter(transform_state);
      transform_state.Move(parent_offset);
    }
    if (parent == ancestor_to_stop_at)
      break;
  }
  transform_state.Move(-offset_from_container);
}

bool LayoutBox::MapToVisualRectInAncestorSpaceInternal(
    const LayoutBoxModelObject* ancestor,
    TransformState& transform_state,
    VisualRectFlags visual_rect_flags) const {
  NOT_DESTROYED();

  if (ancestor == this)
    return true;

  if (!visual_rect_flags.Has(VisualRectFlag::kIgnoreFilters)) {
    InflateVisualRectForFilter(transform_state);
  }

  if (LayoutObject* canvas_layout_object = CanvasForDrawingLayoutObject()) {
    if (!MapVisualRectToContainer(canvas_layout_object, PhysicalOffset(),
                                  ancestor, visual_rect_flags,
                                  transform_state)) {
      return false;
    }
    return canvas_layout_object->MapToVisualRectInAncestorSpaceInternal(
        ancestor, transform_state, visual_rect_flags);
  }

  AncestorSkipInfo skip_info(ancestor, true);
  LayoutObject* container = Container(&skip_info);
  if (!container)
    return true;

  PhysicalOffset container_offset = PhysicalLocation();

  if (IsStickyPositioned()) {
    container_offset += StickyPositionOffset();
  } else if (NeedsAnchorPositionScrollAdjustment()) [[unlikely]] {
    container_offset += AnchorPositionScrollTranslationOffset();
  }

  if (skip_info.FilterSkipped() &&
      !visual_rect_flags.Has(VisualRectFlag::kIgnoreFilters)) {
    InflateVisualRectForFilterUnderContainer(transform_state, *container,
                                             ancestor);
  }

  if (!MapVisualRectToContainer(container, container_offset, ancestor,
                                visual_rect_flags, transform_state))
    return false;

  if (skip_info.AncestorSkipped()) {
    bool preserve3D = container->StyleRef().Preserves3D();
    TransformState::TransformAccumulation accumulation =
        preserve3D ? TransformState::kAccumulateTransform
                   : TransformState::kFlattenTransform;

    // If the ancestor is below the container, then we need to map the rect into
    // ancestor's coordinates.
    PhysicalOffset ancestor_container_offset =
        ancestor->OffsetFromAncestor(container);
    transform_state.Move(-ancestor_container_offset, accumulation);
    return true;
  }

  if (IsFixedPositioned() && container == ancestor && container->IsLayoutView())
    transform_state.Move(To<LayoutView>(container)->OffsetForFixedPosition());

  return container->MapToVisualRectInAncestorSpaceInternal(
      ancestor, transform_state, visual_rect_flags);
}

void LayoutBox::InflateVisualRectForFilter(
    TransformState& transform_state) const {
  NOT_DESTROYED();
  if (!Layer() || !Layer()->PaintsWithFilters())
    return;

  transform_state.Flatten();
  PhysicalRect rect = PhysicalRect::EnclosingRect(
      transform_state.LastPlanarQuad().BoundingBox());
  transform_state.SetQuad(
      gfx::QuadF(gfx::RectF(Layer()->MapRectForFilter(rect))));
}

PhysicalRect LayoutBox::LocalCaretRect(int caret_offset,
                                       CaretShape caret_shape) const {
  NOT_DESTROYED();
  // VisiblePositions at offsets inside containers either a) refer to the
  // positions before/after those containers (tables and select elements) or
  // b) refer to the position inside an empty block.
  // They never refer to children.
  // FIXME: Paint the carets inside empty blocks differently than the carets
  // before/after elements.

  const SimpleFontData* font_data = StyleRef().GetFont()->PrimaryFont();
  const LayoutUnit font_height =
      font_data ? LayoutUnit(font_data->GetFontMetrics().Height())
                : LayoutUnit();

  // FIXME: ignoring :first-line, missing good reason to take care of
  const LogicalSize caret_size = {GetFrameView()->BarCaretWidth(), font_height};

  // FIXME: Border/padding should be added for all elements but this workaround
  // is needed because we use offsets inside an "atomic" element to represent
  // positions before and after the element in deprecated editing offsets.
  bool apply_border_padding =
      GetNode() &&
      !(EditingIgnoresContent(*GetNode()) || IsDisplayInsideTable(GetNode()));

  WritingDirectionMode writing_direction = StyleRef().GetWritingDirection();
  LogicalOffset offset;
  LayoutUnit content_inline_size = LogicalWidth();
  if (apply_border_padding) {
    BoxStrut border_padding = (BorderOutsets() + PaddingOutsets())
                                  .ConvertToLogical(writing_direction);
    offset.inline_offset = border_padding.inline_start;
    offset.block_offset = border_padding.block_start;
    content_inline_size -= border_padding.InlineSum();
  }
  if (caret_offset) {
    offset.inline_offset += content_inline_size - caret_size.inline_size;
  }

  return WritingModeConverter(writing_direction, StitchedSize())
      .ToPhysical({offset, caret_size});
}

PositionWithAffinity LayoutBox::PositionForPointInFragments(
    const PhysicalOffset& target) const {
  NOT_DESTROYED();
  DCHECK_GE(GetDocument().Lifecycle().GetState(),
            DocumentLifecycle::kPrePaintClean);
  DCHECK_GT(PhysicalFragmentCount(), 0u);

  if (PhysicalFragmentCount() == 1) {
    const PhysicalBoxFragment* fragment = GetPhysicalFragment(0);
    return fragment->PositionForPoint(target);
  }

  // When |this| is block fragmented, find the closest fragment.
  const PhysicalBoxFragment* closest_fragment = nullptr;
  PhysicalOffset closest_fragment_offset;
  LayoutUnit shortest_square_distance = LayoutUnit::Max();
  for (const PhysicalBoxFragment& fragment : PhysicalFragments()) {
    // If |fragment| contains |target|, call its |PositionForPoint|.
    const PhysicalOffset fragment_offset = fragment.OffsetFromOwnerLayoutBox();
    const PhysicalSize distance =
        PhysicalRect(fragment_offset, fragment.Size()).DistanceAsSize(target);
    if (distance.IsZero())
      return fragment.PositionForPoint(target - fragment_offset);

    // Otherwise find the closest fragment.
    const LayoutUnit square_distance =
        distance.width * distance.width + distance.height * distance.height;
    if (square_distance < shortest_square_distance || !closest_fragment) {
      shortest_square_distance = square_distance;
      closest_fragment = &fragment;
      closest_fragment_offset = fragment_offset;
    }
  }
  DCHECK(closest_fragment);
  return closest_fragment->PositionForPoint(target - closest_fragment_offset);
}

bool LayoutBox::IsSemiReplaced() const {
  // Exclude <fieldset> from this check, for layout purposes they aren't really
  // form control elements.
  if (const auto* element = DynamicTo<Element>(GetNode())) {
    return IsA<HTMLImageElement>(element) ||
           (element->IsFormControlElement() &&
            !IsA<HTMLFieldSetElement>(element));
  }
  return false;
}

// Children of LayoutCustom object's are only considered "items" when it has a
// loaded algorithm.
bool LayoutBox::IsCustomItem() const {
  NOT_DESTROYED();
  auto* parent_layout_box = DynamicTo<LayoutCustom>(Parent());
  return parent_layout_box && parent_layout_box->IsLoaded();
}

bool LayoutBox::HasTopOverflow() const {
  NOT_DESTROYED();
  // Early-return for the major case.
  if (IsHorizontalWritingMode()) {
    return false;
  }
  switch (StyleRef().GetWritingMode()) {
    case WritingMode::kHorizontalTb:
      return false;
    case WritingMode::kSidewaysLr:
      return StyleRef().IsLeftToRightDirection();
    case WritingMode::kVerticalLr:
    case WritingMode::kVerticalRl:
    case WritingMode::kSidewaysRl:
      return !StyleRef().IsLeftToRightDirection();
  }
}

bool LayoutBox::HasLeftOverflow() const {
  NOT_DESTROYED();
  // Early-return for the major case.
  if (IsHorizontalWritingMode()) {
    return !StyleRef().IsLeftToRightDirection();
  }
  switch (StyleRef().GetWritingMode()) {
    case WritingMode::kHorizontalTb:
      return !StyleRef().IsLeftToRightDirection();
    case WritingMode::kVerticalLr:
    case WritingMode::kSidewaysLr:
      return false;
    case WritingMode::kVerticalRl:
    case WritingMode::kSidewaysRl:
      return true;
  }
}

void LayoutBox::SetScrollableOverflowFromLayoutResults() {
  NOT_DESTROYED();
  ClearSelfNeedsScrollableOverflowRecalc();
  ClearChildNeedsScrollableOverflowRecalc();
  if (overflow_) {
    overflow_->scrollable_overflow.reset();
  }

  if (IsLayoutReplaced()) {
    return;
  }

  const WritingMode writing_mode = StyleRef().GetWritingMode();
  std::optional<PhysicalRect> scrollable_overflow;
  LayoutUnit consumed_block_size;
  LayoutUnit fragment_width_sum;

  // Iterate over all the fragments and unite their individual
  // scrollable-overflow to determine the final scrollable-overflow.
  for (const auto& layout_result : layout_results_) {
    const auto& fragment =
        To<PhysicalBoxFragment>(layout_result->GetPhysicalFragment());

    // In order to correctly unite the overflow, we need to shift an individual
    // fragment's scrollable-overflow by previously consumed block-size so far.
    PhysicalOffset offset_adjust;
    switch (writing_mode) {
      case WritingMode::kHorizontalTb:
        offset_adjust = {LayoutUnit(), consumed_block_size};
        break;
      case WritingMode::kVerticalRl:
      case WritingMode::kSidewaysRl:
        // For flipped-blocks writing-modes, we build the total overflow rect
        // from right-to-left (adding with negative offsets). At the end we
        // need to make the origin relative to the LHS, so we add the total
        // fragment width.
        fragment_width_sum += fragment.Size().width;
        offset_adjust = {-fragment.Size().width - consumed_block_size,
                         LayoutUnit()};
        break;
      case WritingMode::kVerticalLr:
      case WritingMode::kSidewaysLr:
        offset_adjust = {consumed_block_size, LayoutUnit()};
        break;
      default:
        NOTREACHED();
    }

    PhysicalRect fragment_scrollable_overflow = fragment.ScrollableOverflow();
    fragment_scrollable_overflow.offset += offset_adjust;

    // If we are the first fragment just set the scrollable-overflow.
    if (!scrollable_overflow) {
      scrollable_overflow = fragment_scrollable_overflow;
    } else {
      scrollable_overflow->UniteEvenIfEmpty(fragment_scrollable_overflow);
    }

    if (const auto* break_token = fragment.GetBreakToken()) {
      // The legacy engine doesn't understand our concept of repeated
      // fragments. Stop now. The overflow rectangle will represent the
      // fragment(s) generated under the first repeated root.
      if (break_token->IsRepeated())
        break;
      consumed_block_size = break_token->ConsumedBlockSize();
    }
  }

  if (!scrollable_overflow) {
    return;
  }

  if (IsFlippedBlocksWritingMode(writing_mode)) {
    scrollable_overflow->offset.left += fragment_width_sum;
  }

  if (scrollable_overflow->IsEmpty() ||
      PhysicalPaddingBoxRect().Contains(*scrollable_overflow)) {
    return;
  }

  DCHECK(!ScrollableOverflowIsSet());
  if (!overflow_)
    overflow_ = MakeGarbageCollected<BoxOverflowModel>();
  overflow_->scrollable_overflow.emplace(*scrollable_overflow);
}

RecalcScrollableOverflowResult LayoutBox::RecalcScrollableOverflowNG() {
  NOT_DESTROYED();

  RecalcScrollableOverflowResult child_result;
  // Don't attempt to rebuild the fragment tree or recalculate
  // scrollable-overflow, layout will do this for us.
  if (NeedsLayout())
    return RecalcScrollableOverflowResult();

  if (ChildNeedsScrollableOverflowRecalc()) {
    child_result = RecalcChildScrollableOverflowNG();
  }

  bool should_recalculate_scrollable_overflow =
      SelfNeedsScrollableOverflowRecalc() ||
      child_result.scrollable_overflow_changed;
  bool rebuild_fragment_tree = child_result.rebuild_fragment_tree;
  bool scrollable_overflow_changed = false;

  if (rebuild_fragment_tree || should_recalculate_scrollable_overflow) {
    for (auto& layout_result : layout_results_) {
      const auto& fragment =
          To<PhysicalBoxFragment>(layout_result->GetPhysicalFragment());
      std::optional<PhysicalRect> scrollable_overflow;

      // Recalculate our scrollable-overflow if a child had its
      // scrollable-overflow changed, or if we are marked as dirty.
      if (should_recalculate_scrollable_overflow) {
        const PhysicalRect old_scrollable_overflow =
            fragment.ScrollableOverflow();
        const bool has_block_fragmentation =
            layout_result->GetConstraintSpaceForCaching()
                .HasBlockFragmentation();
#if DCHECK_IS_ON()
        PhysicalBoxFragment::AllowPostLayoutScope allow_post_layout_scope;
#endif
        const PhysicalRect new_scrollable_overflow =
            ScrollableOverflowCalculator::
                RecalculateScrollableOverflowForFragment(
                    fragment, has_block_fragmentation);

        // Set the appropriate flags if the scrollable-overflow changed.
        if (old_scrollable_overflow != new_scrollable_overflow) {
          scrollable_overflow = new_scrollable_overflow;
          scrollable_overflow_changed = true;
          rebuild_fragment_tree = true;
        }
      }

      if (scrollable_overflow) {
        fragment.GetMutableForStyleRecalc().SetScrollableOverflow(
            *scrollable_overflow);
      }
    }
    SetScrollableOverflowFromLayoutResults();
  }

  if (scrollable_overflow_changed && IsScrollContainer()) {
    Layer()->GetScrollableArea()->UpdateAfterOverflowRecalc();
  }

  // Only indicate to our parent that our scrollable overflow changed if we
  // have:
  //  - No layout containment applied.
  //  - No clipping (in both axes).
  scrollable_overflow_changed = scrollable_overflow_changed &&
                                !ShouldApplyLayoutContainment() &&
                                !ShouldClipOverflowAlongBothAxis();

  return {scrollable_overflow_changed, rebuild_fragment_tree};
}

RecalcScrollableOverflowResult LayoutBox::RecalcChildScrollableOverflowNG() {
  NOT_DESTROYED();
  DCHECK(ChildNeedsScrollableOverflowRecalc());
  ClearChildNeedsScrollableOverflowRecalc();

#if DCHECK_IS_ON()
  // We use PostLayout methods to navigate the fragment tree and reach the
  // corresponding LayoutObjects, so we need to use AllowPostLayoutScope here.
  PhysicalBoxFragment::AllowPostLayoutScope allow_post_layout_scope;
#endif
  RecalcScrollableOverflowResult result;
  for (auto& layout_result : layout_results_) {
    const auto& fragment =
        To<PhysicalBoxFragment>(layout_result->GetPhysicalFragment());
    if (fragment.HasItems()) {
      for (InlineCursor cursor(fragment); cursor; cursor.MoveToNext()) {
        const PhysicalBoxFragment* child =
            cursor.Current()->PostLayoutBoxFragment();
        if (!child || !child->GetLayoutObject()->IsBox())
          continue;
        result.Unite(
            child->MutableOwnerLayoutBox()->RecalcScrollableOverflow());
      }
    }

    RecalcFragmentScrollableOverflow(result, fragment);
  }

  return result;
}

void LayoutBox::AddSelfVisualOverflow(const PhysicalRect& rect) {
  NOT_DESTROYED();
  if (rect.IsEmpty())
    return;

  PhysicalRect border_box = PhysicalBorderBoxRect();
  if (border_box.Contains(rect))
    return;

  if (!VisualOverflowIsSet()) {
    if (!overflow_)
      overflow_ = MakeGarbageCollected<BoxOverflowModel>();

    overflow_->visual_overflow.emplace(border_box);
  }

  overflow_->visual_overflow->AddSelfVisualOverflow(rect);
}

void LayoutBox::AddContentsVisualOverflow(const PhysicalRect& rect) {
  NOT_DESTROYED();
  if (rect.IsEmpty())
    return;

  // If hasOverflowClip() we always save contents visual overflow because we
  // need it
  // e.g. to determine whether to apply rounded corner clip on contents.
  // Otherwise we save contents visual overflow only if it overflows the border
  // box.
  PhysicalRect border_box = PhysicalBorderBoxRect();
  if (!HasNonVisibleOverflow() && border_box.Contains(rect))
    return;

  if (!VisualOverflowIsSet()) {
    if (!overflow_)
      overflow_ = MakeGarbageCollected<BoxOverflowModel>();

    overflow_->visual_overflow.emplace(border_box);
  }
  overflow_->visual_overflow->AddContentsVisualOverflow(rect);
}

void LayoutBox::UpdateHasSubpixelVisualEffectOutsets(
    const PhysicalBoxStrut& outsets) {
  NOT_DESTROYED();
  if (!VisualOverflowIsSet()) {
    return;
  }
  overflow_->visual_overflow->SetHasSubpixelVisualEffectOutsets(
      !outsets.top.IsInteger() || !outsets.right.IsInteger() ||
      !outsets.bottom.IsInteger() || !outsets.left.IsInteger());
}

void LayoutBox::SetVisualOverflow(const PhysicalRect& self,
                                  const PhysicalRect& contents) {
  NOT_DESTROYED();
  ClearVisualOverflow();
  AddSelfVisualOverflow(self);
  AddContentsVisualOverflow(contents);
  if (!VisualOverflowIsSet())
    return;

  const PhysicalRect overflow_rect =
      overflow_->visual_overflow->SelfVisualOverflowRect();
  const PhysicalSize box_size = StitchedSize();
  const PhysicalBoxStrut outsets(
      -overflow_rect.Y(), overflow_rect.Right() - box_size.width,
      overflow_rect.Bottom() - box_size.height, -overflow_rect.X());
  UpdateHasSubpixelVisualEffectOutsets(outsets);

  // |OutlineMayBeAffectedByDescendants| is set whenever outline style
  // changes. Update to the actual value here.
  const ComputedStyle& style = StyleRef();
  if (style.HasOutline()) {
    const LayoutUnit outline_extent(OutlinePainter::OutlineOutsetExtent(
        style, OutlineInfo::GetFromStyle(style)));
    SetOutlineMayBeAffectedByDescendants(
        outsets.top != outline_extent || outsets.right != outline_extent ||
        outsets.bottom != outline_extent || outsets.left != outline_extent);
  }
}

void LayoutBox::ClearVisualOverflow() {
  NOT_DESTROYED();
  if (overflow_)
    overflow_->visual_overflow.reset();
  // overflow_ will be reset by MutableForPainting::ClearPreviousOverflowData()
  // if we don't need it to store previous overflow data.
}

bool LayoutBox::CanUseFragmentsForVisualOverflow() const {
  NOT_DESTROYED();
  // TODO(crbug.com/1144203): Legacy, or no-fragments-objects such as
  // table-column. What to do with them is TBD.
  if (!PhysicalFragmentCount())
    return false;
  const PhysicalBoxFragment& fragment = *GetPhysicalFragment(0);
  if (!fragment.CanUseFragmentsForInkOverflow())
    return false;
  return true;
}

// Copy visual overflow from |PhysicalFragments()|.
void LayoutBox::CopyVisualOverflowFromFragments() {
  NOT_DESTROYED();
  DCHECK(CanUseFragmentsForVisualOverflow());
  const PhysicalRect previous_visual_overflow =
      VisualOverflowRectAllowingUnset();
  CopyVisualOverflowFromFragmentsWithoutInvalidations();
  const PhysicalRect visual_overflow = VisualOverflowRect();
  if (visual_overflow == previous_visual_overflow)
    return;
  SetShouldCheckForPaintInvalidation();
}

void LayoutBox::CopyVisualOverflowFromFragmentsWithoutInvalidations() {
  NOT_DESTROYED();
  DCHECK(CanUseFragmentsForVisualOverflow());
  if (!PhysicalFragmentCount()) [[unlikely]] {
    DCHECK(IsLayoutTableCol());
    ClearVisualOverflow();
    return;
  }

  if (PhysicalFragmentCount() == 1) {
    const PhysicalBoxFragment& fragment = *GetPhysicalFragment(0);
    DCHECK(fragment.CanUseFragmentsForInkOverflow());
    if (!fragment.HasInkOverflow()) {
      ClearVisualOverflow();
      return;
    }
    SetVisualOverflow(fragment.SelfInkOverflowRect(),
                      fragment.ContentsInkOverflowRect());
    return;
  }

  // When block-fragmented, stitch visual overflows from all fragments.
  const LayoutBlock* cb = ContainingBlock();
  DCHECK(cb);
  const WritingMode writing_mode = cb->StyleRef().GetWritingMode();
  bool has_overflow = false;
  PhysicalRect self_rect;
  PhysicalRect contents_rect;
  const PhysicalBoxFragment* last_fragment = nullptr;
  for (const PhysicalBoxFragment& fragment : PhysicalFragments()) {
    DCHECK(fragment.CanUseFragmentsForInkOverflow());
    if (!fragment.HasInkOverflow()) {
      last_fragment = &fragment;
      continue;
    }
    has_overflow = true;

    PhysicalRect fragment_self_rect = fragment.SelfInkOverflowRect();
    PhysicalRect fragment_contents_rect = fragment.ContentsInkOverflowRect();

    // Stitch this fragment to the bottom of the last one in horizontal
    // writing mode, or to the right in vertical. Flipped blocks is handled
    // later, after the loop.
    if (last_fragment) {
      const BlockBreakToken* break_token = last_fragment->GetBreakToken();
      DCHECK(break_token);
      const LayoutUnit block_offset = break_token->ConsumedBlockSize();
      if (blink::IsHorizontalWritingMode(writing_mode)) {
        fragment_self_rect.offset.top += block_offset;
        fragment_contents_rect.offset.top += block_offset;
      } else {
        fragment_self_rect.offset.left += block_offset;
        fragment_contents_rect.offset.left += block_offset;
      }
    }
    last_fragment = &fragment;

    self_rect.Unite(fragment_self_rect);
    contents_rect.Unite(fragment_contents_rect);

    // The legacy engine doesn't understand our concept of repeated
    // fragments. Stop now. The overflow rectangle will represent the
    // fragment(s) generated under the first repeated root.
    if (fragment.GetBreakToken() && fragment.GetBreakToken()->IsRepeated()) {
      break;
    }
  }

  if (!has_overflow) {
    ClearVisualOverflow();
    return;
  }
  SetVisualOverflow(self_rect, contents_rect);
}

DISABLE_CFI_PERF
bool LayoutBox::HasUnsplittableScrollingOverflow() const {
  NOT_DESTROYED();
  // Fragmenting scrollbars is only problematic in interactive media, e.g.
  // multicol on a screen. If we're printing, which is non-interactive media, we
  // should allow objects with non-visible overflow to be paginated as normally.
  if (GetDocument().Printing())
    return false;

  // Treat any scrollable container as monolithic.
  return IsScrollContainer();
}

bool LayoutBox::IsMonolithic() const {
  NOT_DESTROYED();
  // TODO(almaher): Don't consider a writing mode root monolitic if
  // IsFlexibleBox(). The breakability should be handled at the item
  // level. (Likely same for Table and Grid).
  if (IsInline() || IsSemiReplaced() || HasUnsplittableScrollingOverflow() ||
      IsOverscrollContainer() || (Parent() && IsWritingModeRoot()) ||
      (IsFixedPositioned() && GetDocument().Printing() &&
       IsA<LayoutView>(Container())) ||
      ShouldApplySizeContainment() || IsFrameSet() ||
      StyleRef().HasLineClamp() || IsScrollMarkerGroup()) {
    return true;
  }

  return false;
}

PhysicalBoxStrut LayoutBox::BorderOutsetsForClipping() const {
  NOT_DESTROYED();
  auto padding_box = -BorderOutsets();
  if (!ShouldApplyOverflowClipMargin())
    return padding_box;

  PhysicalBoxStrut overflow_clip_margin;
  switch (StyleRef().OverflowClipMargin()->GetReferenceBox()) {
    case StyleOverflowClipMargin::ReferenceBox::kBorderBox:
      break;
    case StyleOverflowClipMargin::ReferenceBox::kPaddingBox:
      overflow_clip_margin = padding_box;
      break;
    case StyleOverflowClipMargin::ReferenceBox::kContentBox:
      overflow_clip_margin = padding_box - PaddingOutsets();
      break;
  }

  return overflow_clip_margin.Inflate(
      StyleRef().OverflowClipMargin()->GetMargin());
}

PhysicalRect LayoutBox::VisualOverflowRect() const {
  NOT_DESTROYED();
  if (!VisualOverflowIsSet())
    return PhysicalBorderBoxRect();

  const PhysicalRect& self_visual_overflow_rect =
      overflow_->visual_overflow->SelfVisualOverflowRect();
  if (HasMask()) {
    return self_visual_overflow_rect;
  }

  const OverflowClipAxes overflow_clip_axes = GetOverflowClipAxes();
  if (ShouldApplyOverflowClipMargin()) {
    // We should apply overflow clip margin only if we clip overflow on both
    // axis.
    DCHECK_EQ(overflow_clip_axes, kOverflowClipBothAxis);
    const PhysicalRect& contents_visual_overflow_rect =
        overflow_->visual_overflow->ContentsVisualOverflowRect();
    if (!contents_visual_overflow_rect.IsEmpty()) {
      PhysicalRect result = PhysicalBorderBoxRect();
      PhysicalBoxStrut outsets = BorderOutsetsForClipping();
      result.ExpandEdges(outsets.top, outsets.right, outsets.bottom,
                         outsets.left);
      result.Intersect(contents_visual_overflow_rect);
      result.Unite(self_visual_overflow_rect);
      return result;
    }
  }

  if (overflow_clip_axes == kOverflowClipBothAxis)
    return self_visual_overflow_rect;

  PhysicalRect result =
      overflow_->visual_overflow->ContentsVisualOverflowRect();
  result.Unite(self_visual_overflow_rect);
  ApplyOverflowClip(overflow_clip_axes, self_visual_overflow_rect, result);
  return result;
}

#if DCHECK_IS_ON()
PhysicalRect LayoutBox::VisualOverflowRectAllowingUnset() const {
  NOT_DESTROYED();
  InkOverflow::ReadUnsetAsNoneScope read_unset_as_none;
  return VisualOverflowRect();
}

void LayoutBox::CheckIsVisualOverflowComputed() const {
  NOT_DESTROYED();
  // TODO(crbug.com/1205708): There are still too many failures. Disable the
  // the check for now. Need to investigate the reason.
  return;
  /*
  if (InkOverflow::ReadUnsetAsNoneScope::IsActive())
    return;
  if (!CanUseFragmentsForVisualOverflow())
    return;
  // TODO(crbug.com/1203402): MathML needs some more work.
  if (IsMathML())
    return;
  for (const PhysicalBoxFragment& fragment : PhysicalFragments())
    DCHECK(fragment.IsInkOverflowComputed());
  */
}
#endif

PhysicalOffset LayoutBox::OffsetPoint(const Element* parent) const {
  NOT_DESTROYED();
  return AdjustedPositionRelativeTo(PhysicalLocation(), parent);
}

LayoutUnit LayoutBox::StitchedBlockSize() const {
  NOT_DESTROYED();
  if (!PhysicalFragmentCount()) {
    return LayoutUnit();
  }
  auto writing_direction = StyleRef().GetWritingDirection();
  const auto& first_fragment = *GetPhysicalFragment(0);
  if (first_fragment.IsOnlyForNode() ||
      (first_fragment.GetBreakToken() &&
       first_fragment.GetBreakToken()->IsRepeated())) {
    // This node either generates a single fragment, or we're dealing with
    // repeated content, which isn't stitched.
    return LogicalFragment(writing_direction, first_fragment).BlockSize();
  }

  wtf_size_t idx = PhysicalFragmentCount();
  DCHECK_GT(idx, 1u);
  idx--;
  // Calculating the stitched size is straight-forward if the node isn't
  // overflowed: Just add the consumed block-size of the last break token
  // and the block-size of the last fragment. If it is overflowed, on the
  // other hand, we need to search backwards until we find the end of the
  // block-end border edge.
  PhysicalSize last_content_fragment_size = GetPhysicalFragment(idx)->Size();
  LayoutUnit previously_consumed_block_size;
  while (idx) {
    // Look at the preceding break token.
    idx--;
    const BlockBreakToken* break_token =
        GetPhysicalFragment(idx)->GetBreakToken();
    if (!break_token->IsAtBlockEnd()) {
      previously_consumed_block_size = break_token->ConsumedBlockSize();
      break;
    }
    last_content_fragment_size = GetPhysicalFragment(idx)->Size();
  }

  LogicalSize logical_size(
      ToLogicalSize(last_content_fragment_size, StyleRef().GetWritingMode()));
  return previously_consumed_block_size + logical_size.block_size;
}

PhysicalSize LayoutBox::StitchedSize() const {
  NOT_DESTROYED();
  if (!HasValidCachedGeometry()) {
    // const_cast in order to update the cached value.
    const_cast<LayoutBox*>(this)->SetHasValidCachedGeometry(true);
    const_cast<LayoutBox*>(this)->frame_size_ = ComputeSize();
  }
  return frame_size_;
}

PhysicalSize LayoutBox::ComputeSize() const {
  NOT_DESTROYED();
  const auto& results = GetLayoutResults();
  if (results.size() == 0) {
    return PhysicalSize();
  }
  const auto& first_fragment = results[0]->GetPhysicalFragment();
  if (results.size() == 1u) {
    return first_fragment.Size();
  }
  WritingModeConverter converter(first_fragment.Style().GetWritingDirection());
  const BlockBreakToken* previous_break_token = nullptr;
  LogicalSize size;
  for (const auto& result : results) {
    const auto& physical_fragment =
        To<PhysicalBoxFragment>(result->GetPhysicalFragment());
    LogicalSize fragment_logical_size =
        converter.ToLogical(physical_fragment.Size());
    if (physical_fragment.IsFirstForNode()) {
      // Inline-size will only be set at the first fragment. Subsequent
      // fragments may have different inline-size (either because fragmentainer
      // inline-size is variable, or e.g. because available inline-size is
      // affected by floats). The legacy engine doesn't handle variable
      // inline-size (since it doesn't really understand fragmentation).  This
      // means that things like offsetWidth won't work correctly (since that's
      // still being handled by the legacy engine), but at least layout,
      // painting and hit-testing will be correct.
      size = fragment_logical_size;
    } else {
      DCHECK(previous_break_token);
      size.block_size = fragment_logical_size.block_size +
                        previous_break_token->ConsumedBlockSize();
    }
    previous_break_token = physical_fragment.GetBreakToken();
    // Continue in order to update logical height, unless this fragment is
    // past the block-end of the generating node (happens with overflow) or
    // is a repeated one.
    if (!previous_break_token || previous_break_token->IsRepeated() ||
        previous_break_token->IsAtBlockEnd()) {
      break;
    }
  }
  return converter.ToPhysical(size);
}

LayoutBox* LayoutBox::LocationContainer() const {
  NOT_DESTROYED();
  // A non-root SVG object derived from LayoutBox doesn't have a meaningful
  // location container.
  if (IsSVGChild()) {
    return nullptr;
  }

  // The box's location is relative to its containing box.
  LayoutObject* container = Container();
  while (container && !container->IsBox())
    container = container->Container();
  return To<LayoutBox>(container);
}

DISABLE_CFI_PERF
PhysicalRect LayoutBox::PhysicalContractedBoxRect(ContractionEdge edge) const {
  NOT_DESTROYED();
  PhysicalRect rect(PhysicalOffset(), StitchedSize());
  PhysicalBoxStrut inset;
  if (PhysicalFragmentCount() == 1u) {
    // Optimize for the common case - one fragment.
    const PhysicalBoxFragment* fragment = GetPhysicalFragment(0);
    if (fragment->HasBorders()) {
      inset += fragment->Borders();
    }
    if (fragment->HasScrollbar()) {
      inset += fragment->Scrollbar();
    }
    if (edge == kContractToContentEdge && fragment->HasPadding()) {
      inset += fragment->Padding();
    }
  } else if (PhysicalFragmentCount()) {
    const PhysicalBoxFragment* top_fragment = FragmentForTopEdge(*this);
    const PhysicalBoxFragment* right_fragment = FragmentForRightEdge(*this);
    const PhysicalBoxFragment* bottom_fragment = FragmentForBottomEdge(*this);
    const PhysicalBoxFragment* left_fragment = FragmentForLeftEdge(*this);
    inset.top += top_fragment->Borders().top + top_fragment->Scrollbar().top;
    inset.right +=
        right_fragment->Borders().right + right_fragment->Scrollbar().right;
    inset.bottom +=
        bottom_fragment->Borders().bottom + bottom_fragment->Scrollbar().bottom;
    inset.left +=
        left_fragment->Borders().left + left_fragment->Scrollbar().left;

    if (edge == kContractToContentEdge) {
      inset.top += top_fragment->Padding().top;
      inset.right += right_fragment->Padding().right;
      inset.bottom += bottom_fragment->Padding().bottom;
      inset.left += left_fragment->Padding().left;
    }
  }

  rect.Contract(inset);

  // We need to clamp negative values. This function can be called during layout
  // before the size of the box has been updated. The scrollbar may also be
  // wider than the padding box.
  rect.size.width = rect.size.width.ClampNegativeToZero();
  rect.size.height = rect.size.height.ClampNegativeToZero();

  return rect;
}

ShapeOutsideInfo* LayoutBox::GetShapeOutsideInfo() const {
  NOT_DESTROYED();
  return ShapeOutsideInfo::Info(*this);
}

CustomLayoutChild* LayoutBox::GetCustomLayoutChild() const {
  NOT_DESTROYED();
  DCHECK(rare_data_);
  DCHECK(rare_data_->layout_child_);
  return rare_data_->layout_child_.Get();
}

void LayoutBox::AddCustomLayoutChildIfNeeded() {
  NOT_DESTROYED();
  if (!IsCustomItem())
    return;

  const AtomicString& name = Parent()->StyleRef().DisplayLayoutCustomName();
  LayoutWorklet* worklet = LayoutWorklet::From(*GetDocument().domWindow());
  const CSSLayoutDefinition* definition =
      worklet->Proxy()->FindDefinition(name);

  // If there isn't a definition yet, the web developer defined layout isn't
  // loaded yet (or is invalid). The layout tree will get re-attached when
  // loaded, so don't bother creating a script representation of this node yet.
  if (!definition)
    return;

  EnsureRareData().layout_child_ =
      MakeGarbageCollected<CustomLayoutChild>(*definition, BlockNode(this));
}

void LayoutBox::ClearCustomLayoutChild() {
  NOT_DESTROYED();
  if (!rare_data_)
    return;

  if (rare_data_->layout_child_)
    rare_data_->layout_child_->ClearLayoutNode();

  rare_data_->layout_child_ = nullptr;
}

PhysicalRect LayoutBox::DebugRect() const {
  NOT_DESTROYED();
  return PhysicalRect(PhysicalLocation(), StitchedSize());
}

OverflowClipAxes LayoutBox::ComputeOverflowClipAxes() const {
  NOT_DESTROYED();
  if (ShouldApplyPaintContainment()) {
    return kOverflowClipBothAxis;
  }

  if (!RespectsCSSOverflow() || !HasNonVisibleOverflow())
    return kNoOverflowClip;

  if (IsScrollContainer())
    return kOverflowClipBothAxis;
  return (StyleRef().OverflowX() == EOverflow::kVisible ? kNoOverflowClip
                                                        : kOverflowClipX) |
         (StyleRef().OverflowY() == EOverflow::kVisible ? kNoOverflowClip
                                                        : kOverflowClipY);
}

void LayoutBox::MutableForPainting::SavePreviousOverflowData() {
  if (!GetLayoutBox().overflow_)
    GetLayoutBox().overflow_ = MakeGarbageCollected<BoxOverflowModel>();
  auto& previous_overflow = GetLayoutBox().overflow_->previous_overflow_data;
  if (!previous_overflow)
    previous_overflow.emplace();
  previous_overflow->previous_scrollable_overflow_rect =
      GetLayoutBox().ScrollableOverflowRect();
  previous_overflow->previous_visual_overflow_rect =
      GetLayoutBox().VisualOverflowRect();
  previous_overflow->previous_self_visual_overflow_rect =
      GetLayoutBox().SelfVisualOverflowRect();
}

void LayoutBox::MutableForPainting::SavePreviousGapGeometries() {
  auto* previous_gap_geometries =
      MakeGarbageCollected<GCedHeapVector<Member<const GapGeometry>>>();
  for (const PhysicalBoxFragment& fragment :
       GetLayoutBox().PhysicalFragments()) {
    previous_gap_geometries->push_back(fragment.GetGapGeometry());
  }
  GetLayoutBox().EnsureRareData().previous_gap_geometries_ =
      previous_gap_geometries;
}

void LayoutBox::MutableForPainting::ClearPreviousGapGeometries() {
  if (auto* rare_data = GetLayoutBox().rare_data_.Get()) {
    rare_data->previous_gap_geometries_ = nullptr;
  }
}

void LayoutBox::MutableForPainting::SetPreviousGeometryForLayoutShiftTracking(
    const PhysicalOffset& paint_offset,
    const PhysicalSize& size,
    const PhysicalRect& visual_overflow_rect) {
  FirstFragment().SetPaintOffset(paint_offset);
  GetLayoutBox().previous_size_ = size;
  if (PhysicalRect(PhysicalOffset(), size).Contains(visual_overflow_rect))
    return;

  if (!GetLayoutBox().overflow_)
    GetLayoutBox().overflow_ = MakeGarbageCollected<BoxOverflowModel>();
  auto& previous_overflow = GetLayoutBox().overflow_->previous_overflow_data;
  if (!previous_overflow)
    previous_overflow.emplace();
  previous_overflow->previous_visual_overflow_rect = visual_overflow_rect;
  // Other previous rects don't matter because they are used for paint
  // invalidation and we always do full paint invalidation on reattachment.
}

void LayoutBox::MutableForPainting::UpdateBackgroundPaintLocation(
    bool needs_root_element_group) {
  GetLayoutBox().SetBackgroundPaintLocation(
      GetLayoutBox().ComputeBackgroundPaintLocation(needs_root_element_group));
}

RasterEffectOutset LayoutBox::VisualRectOutsetForRasterEffects() const {
  NOT_DESTROYED();
  // If the box has subpixel visual effect outsets, as the visual effect may be
  // painted along the pixel-snapped border box, the pixels on the anti-aliased
  // edge of the effect may overflow the calculated visual rect. Expand visual
  // rect by one pixel in the case.
  return VisualOverflowIsSet() &&
                 overflow_->visual_overflow->HasSubpixelVisualEffectOutsets()
             ? RasterEffectOutset::kWholePixel
             : RasterEffectOutset::kNone;
}

TextDirection LayoutBox::ResolvedDirection() const {
  NOT_DESTROYED();
  if (IsInLayoutNGInlineFormattingContext() && IsInline()) {
    InlineCursor cursor;
    cursor.MoveTo(*this);
    if (cursor) {
      return cursor.Current().ResolvedDirection();
    }
  }
  return StyleRef().Direction();
}

void LayoutBox::OverrideTickmarks(Vector<gfx::Rect> tickmarks) {
  NOT_DESTROYED();
  GetScrollableArea()->SetTickmarksOverride(std::move(tickmarks));
  InvalidatePaintForTickmarks();
}

void LayoutBox::InvalidatePaintForTickmarks() {
  NOT_DESTROYED();
  ScrollableArea* scrollable_area = GetScrollableArea();
  if (!scrollable_area)
    return;
  Scrollbar* scrollbar = scrollable_area->VerticalScrollbar();
  if (!scrollbar)
    return;
  scrollbar->SetNeedsPaintInvalidation(static_cast<ScrollbarPart>(~kThumbPart));
}

static bool HasInsetBoxShadow(const ComputedStyle& style) {
  if (!style.BoxShadow())
    return false;
  for (const ShadowData& shadow : style.BoxShadow()->Shadows()) {
    if (shadow.Style() == ShadowStyle::kInset)
      return true;
  }
  return false;
}

// If all borders and scrollbars are opaque, then background-clip: border-box
// is equivalent to background-clip: padding-box.
bool LayoutBox::BackgroundClipBorderBoxIsEquivalentToPaddingBox() const {
  NOT_DESTROYED();
  const auto* scrollable_area = GetScrollableArea();
  if (scrollable_area) {
    if (auto* scrollbar = scrollable_area->HorizontalScrollbar()) {
      if (!scrollbar->IsOverlayScrollbar() && !scrollbar->IsOpaque()) {
        return false;
      }
    }
    if (auto* scrollbar = scrollable_area->VerticalScrollbar()) {
      if (!scrollbar->IsOverlayScrollbar() && !scrollbar->IsOpaque()) {
        return false;
      }
    }
  }

  if (StyleRef().BorderTopWidth() &&
      (!ResolveColor(GetCSSPropertyBorderTopColor()).IsOpaque() ||
       StyleRef().BorderTopStyle() != EBorderStyle::kSolid)) {
    return false;
  }
  if (StyleRef().BorderRightWidth() &&
      (!ResolveColor(GetCSSPropertyBorderRightColor()).IsOpaque() ||
       StyleRef().BorderRightStyle() != EBorderStyle::kSolid)) {
    return false;
  }
  if (StyleRef().BorderBottomWidth() &&
      (!ResolveColor(GetCSSPropertyBorderBottomColor()).IsOpaque() ||
       StyleRef().BorderBottomStyle() != EBorderStyle::kSolid)) {
    return false;
  }
  if (StyleRef().BorderLeftWidth() &&
      (!ResolveColor(GetCSSPropertyBorderLeftColor()).IsOpaque() ||
       StyleRef().BorderLeftStyle() != EBorderStyle::kSolid)) {
    return false;
  }

  if (!StyleRef().IsScrollbarGutterAuto()) {
    return false;
  }

  return true;
}

BackgroundPaintLocation LayoutBox::ComputeBackgroundPaintLocation(
    bool needs_root_element_group) const {
  NOT_DESTROYED();
  bool may_have_scrolling_layers_without_scrolling = IsA<LayoutView>(this);
  const auto* scrollable_area = GetScrollableArea();
  bool scrolls_overflow = scrollable_area && scrollable_area->ScrollsOverflow();
  if (!scrolls_overflow && !may_have_scrolling_layers_without_scrolling)
    return kBackgroundPaintInBorderBoxSpace;

  if (IsA<LayoutView>(this)) {
    if (needs_root_element_group) {
      // We must paint background in the contents space to apply the root
      // element effects.
      return kBackgroundPaintInContentsSpace;
    }
    if (GetDocument().GetSettings()->GetLCDTextPreference() ==
        LCDTextPreference::kStronglyPreferred) {
      // If we care about LCD text, paint root backgrounds into scrolling
      // contents layer even if style suggests otherwise.
      return kBackgroundPaintInContentsSpace;
    }
  }

  // Inset box shadow is painted in the scrolling area above the background, and
  // it doesn't scroll, so the background can only be painted in the main layer.
  if (HasInsetBoxShadow(StyleRef()))
    return kBackgroundPaintInBorderBoxSpace;

  // For simplicity, assume any border image can have inset, like the above.
  if (StyleRef().BorderImage().GetImage()) {
    return kBackgroundPaintInBorderBoxSpace;
  }

  // Assume optimistically that the background can be painted in the scrolling
  // contents until we find otherwise.
  BackgroundPaintLocation paint_location = kBackgroundPaintInContentsSpace;

  // If elastic overscroll may shift the content, we have to consider
  // that this will shift backgrounds painted into the content space when
  // determining whether we can change the applied background attachment.
  bool elastic_overscroll_may_shift_content =
      RuntimeEnabledFeatures::
          ElasticOverscrollBackgroundPaintLocationFixEnabled() &&
#if BUILDFLAG(IS_ANDROID)
      // On android, elastic overscroll stretches the content but does not
      // shift it beyond its scrolling extents.
      false;
#else
      (IsA<LayoutView>(this)
           ? Platform::Current()->IsElasticOverscrollEnabledOnRoot()
           : Platform::Current()->IsElasticOverscrollEnabledForSubscroll()) &&
      (StyleRef().OverscrollBehaviorX() != EOverscrollBehavior::kNone ||
       StyleRef().OverscrollBehaviorY() != EOverscrollBehavior::kNone);
#endif

  Color background_color = ResolveColor(GetCSSPropertyBackgroundColor());

  const FillLayer* layer = &(StyleRef().BackgroundLayers());
  for (; layer; layer = layer->Next()) {
    if (layer->Attachment() == EFillAttachment::kLocal)
      continue;

    // The background color is either the only background or it's the
    // bottommost value from the background property (see final-bg-layer in
    // https://drafts.csswg.org/css-backgrounds/#the-background).
    if (!layer->GetImage() && !layer->Next() &&
        !background_color.IsFullyTransparent() &&
        StyleRef().IsScrollbarGutterAuto()) {
      // Solid color layers with an effective background clip of the padding box
      // can be treated as local as long as painting it in the content space
      // is visually identical. If we have elastic overscroll, we can only
      // do this if the color is opaque and can be painted in both spaces.
      if (!elastic_overscroll_may_shift_content ||
          background_color.IsOpaque()) {
        // If elastic overscroll may shift the content space, we must upgrade
        // the background to paint in both spaces if it can be painted in
        // content space at all (determined by the subsequent checks).
        if (elastic_overscroll_may_shift_content) {
          paint_location = kBackgroundPaintInBothSpaces;
        }
        EFillBox clip = layer->Clip();
        if (clip == EFillBox::kPadding) {
          continue;
        }
        // A border box can be treated as a padding box if the border is opaque
        // or there is no border and we don't have custom scrollbars.
        if (clip == EFillBox::kBorder) {
          if (BackgroundClipBorderBoxIsEquivalentToPaddingBox()) {
            continue;
          }
          // If we have an opaque background color, we can safely paint it into
          // both the scrolling contents layer and the graphics layer to
          // preserve LCD text. The background color is either the only
          // background or behind background-attachment:local images (ensured by
          // previous iterations of the loop). For the latter case, the first
          // paint of the images doesn't matter because it will be covered by
          // the second paint of the opaque color.
          if (background_color.IsOpaque()) {
            paint_location = kBackgroundPaintInBothSpaces;
            continue;
          }
        } else if (clip == EFillBox::kContent &&
                   StyleRef().PaddingTop().IsZero() &&
                   StyleRef().PaddingLeft().IsZero() &&
                   StyleRef().PaddingRight().IsZero() &&
                   StyleRef().PaddingBottom().IsZero()) {
          // A content fill box can be treated as a padding fill box if there is
          // no padding.
          continue;
        }
      }
    }
    return kBackgroundPaintInBorderBoxSpace;
  }

  // It can't paint in the scrolling contents because it has different 3d
  // context than the scrolling contents.
  if (!StyleRef().Preserves3D() && Parent() &&
      Parent()->StyleRef().Preserves3D()) {
    return kBackgroundPaintInBorderBoxSpace;
  }

  if (StyleRef().HasBorderShape()) {
    // Border-shape clips are applied in the border box space. Painting the
    // background in the scrolling contents layer would bypass that clip.
    return kBackgroundPaintInBorderBoxSpace;
  }

  return paint_location;
}

bool LayoutBox::ComputeCanCompositeBackgroundAttachmentFixed() const {
  NOT_DESTROYED();
  DCHECK(IsBackgroundAttachmentFixedObject());
  if (GetDocument().GetSettings()->GetLCDTextPreference() ==
      LCDTextPreference::kStronglyPreferred) {
    return false;
  }
  // The fixed attachment background must be the only background layer.
  if (StyleRef().BackgroundLayers().Next() ||
      IsSpecialClipFillBox(StyleRef().BackgroundLayers().Clip())) {
    return false;
  }
  // To support box shadow, we'll need to paint the outset and inset box
  // shadows in separate display items in case there are outset box shadow,
  // background, inset box shadow and border in paint order.
  if (StyleRef().BoxShadow()) {
    return false;
  }
  // The theme may paint the background differently for an appearance.
  if (StyleRef().HasEffectiveAppearance()) {
    return false;
  }
  // For now the BackgroundClip paint property node doesn't support rounded
  // corners. If we want to support this, we need to ensure
  // - there is no obvious bleeding issues, and
  // - both the fast path and the slow path of composited rounded clip work.
  if (StyleRef().HasBorderRadius()) {
    return false;
  }
  return true;
}

bool LayoutBox::IsFixedToView(
    const LayoutObject* container_for_fixed_position) const {
  NOT_DESTROYED();
  if (!IsFixedPositioned())
    return false;

  const auto* container = container_for_fixed_position;
  if (!container)
    container = Container();
  else
    DCHECK_EQ(container, Container());
  return container->IsLayoutView();
}

PhysicalRect LayoutBox::ComputeStickyConstrainingRect() const {
  NOT_DESTROYED();
  DCHECK(IsScrollContainer());
  PhysicalRect constraining_rect = OverflowClipRect();
  constraining_rect.Move(-BorderOutsets().Offset());
  constraining_rect.Contract(PaddingOutsets());

  // Subtract off the scroll origin to move into scrolling content space.
  constraining_rect.Move(-PhysicalOffset(ScrollOrigin()));
  return constraining_rect;
}

AnchorPositionScrollData* LayoutBox::GetAnchorPositionScrollData() const {
  NOT_DESTROYED();
  if (Element* element = DynamicTo<Element>(GetNode())) {
    return element->GetAnchorPositionScrollData();
  }
  return nullptr;
}

bool LayoutBox::NeedsAnchorPositionScrollAdjustment() const {
  NOT_DESTROYED();
  if (auto* data = GetAnchorPositionScrollData()) {
    return data->NeedsScrollAdjustment();
  }
  return false;
}

bool LayoutBox::AnchorPositionScrollAdjustmentAfectedByViewportScrolling()
    const {
  NOT_DESTROYED();
  if (auto* data = GetAnchorPositionScrollData()) {
    return data->NeedsScrollAdjustment() &&
           data->IsAffectedByViewportScrolling();
  }
  return false;
}

PhysicalOffset LayoutBox::AnchorPositionScrollTranslationOffset() const {
  NOT_DESTROYED();
  if (auto* data = GetAnchorPositionScrollData()) {
    return data->TranslationAsPhysicalOffset();
  }
  return PhysicalOffset();
}

namespace {

template <typename Function>
void ForEachAnchorMapOnContainer(const LayoutBox& box, Function func) {
  const LayoutObject* container = box.Container();
  if (!container) {
    // This is not supposed to be possible, but it is (crbug.com/424420492).
    DCHECK(false);
    return;
  }
  if (container->IsLayoutBlock()) {
    for (const PhysicalBoxFragment& fragment :
         To<LayoutBlock>(container)->PhysicalFragments()) {
      if (const AnchorMap* anchor_map = fragment.GetAnchorMap()) {
        func(*anchor_map);
      }
    }
    return;
  }

  // The container is an inline that's also an abspos containing block.
  CHECK(container->IsLayoutInline());
  const LayoutInline* inline_container = To<LayoutInline>(container);
  if (!inline_container->HasInlineFragments()) {
    return;
  }
  InlineCursor cursor;
  cursor.MoveTo(*container);
  for (; cursor; cursor.MoveToNextForSameLayoutObject()) {
    if (const PhysicalBoxFragment* fragment = cursor.Current().BoxFragment()) {
      if (const AnchorMap* anchor_map = fragment->GetAnchorMap()) {
        func(*anchor_map);
      }
    }
  }
}

#if EXPENSIVE_DCHECKS_ARE_ON()
template <typename Function>
void AssertSameDataOnLayoutResults(
    const LayoutBox::LayoutResultList& layout_results,
    Function func) {
  // When an out-of-flow box is fragmented, the position fallback results on all
  // fragments should be the same.
  for (wtf_size_t i = 1; i < layout_results.size(); ++i) {
    DCHECK(func(layout_results[i]) == func(layout_results[i - 1]));
  }
}

#endif

}  // namespace

const LayoutObject* LayoutBox::FindTargetAnchor(
    const ScopedCSSName& anchor_name) const {
  NOT_DESTROYED();
  if (!IsOutOfFlowPositioned()) {
    return nullptr;
  }

  AnchorScopedName* anchor_scoped_name = ToAnchorScopedName(anchor_name, *this);

  // Go through the already built AnchorMap to avoid tree traversal.
  const LayoutObject* anchor = nullptr;
  auto search_for_anchor = [&](const AnchorMap& anchor_map) {
    if (const LayoutObject* current =
            anchor_map.AnchorLayoutObject(*this, anchor_scoped_name)) {
      if (!anchor ||
          (anchor != current && anchor->IsBeforeInPreOrder(*current))) {
        anchor = current;
      }
    }
  };
  ForEachAnchorMapOnContainer(*this, search_for_anchor);
  return anchor;
}

const LayoutObject* LayoutBox::AcceptableImplicitAnchor() const {
  NOT_DESTROYED();
  if (!IsOutOfFlowPositioned()) {
    return nullptr;
  }
  Element* element = DynamicTo<Element>(GetNode());
  Element* anchor_element =
      element ? element->ImplicitAnchorElement() : nullptr;
  LayoutObject* anchor_layout_object =
      anchor_element ? anchor_element->GetLayoutObject() : nullptr;
  if (!anchor_layout_object) {
    return nullptr;
  }
  // Go through the already built AnchorMap to avoid tree traversal.
  bool is_acceptable_anchor = false;
  auto validate_anchor = [&](const AnchorMap& anchor_map) {
    if (anchor_map.AnchorLayoutObject(*this, anchor_element)) {
      is_acceptable_anchor = true;
    }
  };
  ForEachAnchorMapOnContainer(*this, validate_anchor);
  return is_acceptable_anchor ? anchor_layout_object : nullptr;
}

const GCedHeapVector<NonOverflowingScrollRange>*
LayoutBox::NonOverflowingScrollRanges() const {
  NOT_DESTROYED();
  const auto& layout_results = GetLayoutResults();
  if (layout_results.empty()) {
    return nullptr;
  }
  // We only need to check the first fragment, because when the box is
  // fragmented, position fallback results are duplicated on all fragments.
#if EXPENSIVE_DCHECKS_ARE_ON()
  for (wtf_size_t i = 1; i < layout_results.size(); ++i) {
    DCHECK(base::ValuesEquivalent(
        layout_results[i]->NonOverflowingScrollRanges(),
        layout_results[i - 1]->NonOverflowingScrollRanges()));
  }
#endif
  return layout_results.front()->NonOverflowingScrollRanges();
}

const BoxStrut& LayoutBox::OutOfFlowInsetsForGetComputedStyle() const {
  NOT_DESTROYED();
  const auto& layout_results = GetLayoutResults();
  // We should call this function only after the node is laid out.
  CHECK(layout_results.size());
  // We only need to check the first fragment, because when the box is
  // fragmented, insets are duplicated on all fragments.
#if EXPENSIVE_DCHECKS_ARE_ON()
  AssertSameDataOnLayoutResults(layout_results, [](const auto& result) {
    return result->OutOfFlowInsetsForGetComputedStyle();
  });
#endif
  return GetLayoutResults().front()->OutOfFlowInsetsForGetComputedStyle();
}

Element* LayoutBox::AccessibilityAnchor() const {
  NOT_DESTROYED();
  const auto& layout_results = GetLayoutResults();
  if (layout_results.empty()) {
    return nullptr;
  }
  return layout_results.front()->AccessibilityAnchor();
}

const GCedHeapHashSet<Member<Element>>*
LayoutBox::DisplayLocksAffectedByAnchors() const {
  NOT_DESTROYED();
  const auto& layout_results = GetLayoutResults();
  if (layout_results.empty()) {
    return nullptr;
  }
  return layout_results.front()->DisplayLocksAffectedByAnchors();
}

void LayoutBox::NotifyContainingDisplayLocksForAnchorPositioning(
    const GCedHeapHashSet<Member<Element>>*
        past_display_locks_affected_by_anchors,
    const GCedHeapHashSet<Member<Element>>* display_locks_affected_by_anchors)
    const {
  NOT_DESTROYED();
  auto notify_display_locks =
      [](const GCedHeapHashSet<Member<Element>>* display_locks) {
        if (!display_locks) {
          return;
        }
        for (auto& display_lock_element : *display_locks) {
          display_lock_element->GetDisplayLockContext()
              ->SetAnchorPositioningRenderStateMayHaveChanged();
        }
      };

  notify_display_locks(past_display_locks_affected_by_anchors);
  notify_display_locks(display_locks_affected_by_anchors);
}

bool LayoutBox::NeedsAnchorPositionScrollAdjustmentInX() const {
  NOT_DESTROYED();
  const auto& layout_results = GetLayoutResults();
  if (layout_results.empty()) {
    return false;
  }
  // We only need to check the first fragment, because when the box is
  // fragmented, position fallback results are duplicated on all fragments.
#if EXPENSIVE_DCHECKS_ARE_ON()
  AssertSameDataOnLayoutResults(layout_results, [](const auto& result) {
    return result->NeedsAnchorPositionScrollAdjustmentInX();
  });
#endif
  return layout_results.front()->NeedsAnchorPositionScrollAdjustmentInX();
}

bool LayoutBox::NeedsAnchorPositionScrollAdjustmentInY() const {
  NOT_DESTROYED();
  const auto& layout_results = GetLayoutResults();
  if (layout_results.empty()) {
    return false;
  }
  // We only need to check the first fragment, because when the box is
  // fragmented, position fallback results are duplicated on all fragments.
#if EXPENSIVE_DCHECKS_ARE_ON()
  AssertSameDataOnLayoutResults(layout_results, [](const auto& result) {
    return result->NeedsAnchorPositionScrollAdjustmentInY();
  });
#endif
  return layout_results.front()->NeedsAnchorPositionScrollAdjustmentInY();
}

WritingModeConverter LayoutBox::CreateWritingModeConverter() const {
  NOT_DESTROYED();
  return WritingModeConverter(
      {StyleRef().GetWritingMode(), TextDirection::kLtr}, StitchedSize());
}

PhysicalOffset LayoutBox::PhysicalLocation() const {
  NOT_DESTROYED();
  return frame_location_;
}

PhysicalRect LayoutBox::BoundingBoxRelativeToFirstFragment() const {
  NOT_DESTROYED();
  PhysicalRect bounding_rect;
  const PhysicalBoxFragment* first_fragment = nullptr;
  for (const PhysicalBoxFragment& fragment : PhysicalFragments()) {
    PhysicalOffset offset;
    if (!first_fragment) {
      first_fragment = &fragment;
    } else {
      offset = fragment.OffsetFromRootFragmentationContext() -
               first_fragment->OffsetFromRootFragmentationContext();
    }
    PhysicalRect fragment_rect(offset, fragment.Size());
    bounding_rect.UniteEvenIfEmpty(fragment_rect);

    if (const BlockBreakToken* break_token = fragment.GetBreakToken()) {
      if (break_token->IsAtBlockEnd()) {
        // Ignore subsequent fragments that are just there to hold overflowing
        // children.
        break;
      }
    }
  }

  return bounding_rect;
}

bool LayoutBox::IsReadingFlowContainer() const {
  NOT_DESTROYED();
  // TODO(almaher): Add reading flow support for grid-lanes.
  const ComputedStyle& style = StyleRef();
  switch (style.ReadingFlow()) {
    case EReadingFlow::kNormal:
      return false;
    case EReadingFlow::kFlexVisual:
    case EReadingFlow::kFlexFlow:
      return IsFlexibleBox();
    case EReadingFlow::kGridRows:
    case EReadingFlow::kGridColumns:
    case EReadingFlow::kGridOrder:
      return IsLayoutGrid();
    case EReadingFlow::kSourceOrder:
      return IsLayoutBlock() || IsFlexibleBox() || IsLayoutGridOrGridLanes();
  }
  return false;
}

const GCedHeapVector<Member<Node>>& LayoutBox::ReadingFlowNodes() const {
  NOT_DESTROYED();
  if (const auto* nodes = GetPhysicalFragment(0)->ReadingFlowNodes()) {
    return *nodes;
  }
  DEFINE_STATIC_LOCAL(Persistent<GCedHeapVector<Member<Node>>>, empty_vector,
                      (MakeGarbageCollected<GCedHeapVector<Member<Node>>>()));
  return *empty_vector.Get();
}

}  // namespace blink
